AOP.Logging.Core
1.4.0
dotnet add package AOP.Logging.Core --version 1.4.0
NuGet\Install-Package AOP.Logging.Core -Version 1.4.0
<PackageReference Include="AOP.Logging.Core" Version="1.4.0" />
<PackageVersion Include="AOP.Logging.Core" Version="1.4.0" />
<PackageReference Include="AOP.Logging.Core" />
paket add AOP.Logging.Core --version 1.4.0
#r "nuget: AOP.Logging.Core, 1.4.0"
#:package AOP.Logging.Core@1.4.0
#addin nuget:?package=AOP.Logging.Core&version=1.4.0
#tool nuget:?package=AOP.Logging.Core&version=1.4.0
AOP.Logging.NET
A powerful, attribute-based Aspect-Oriented Programming (AOP) logging framework for C# that provides seamless method interception and automatic logging using Source Generators.
Features
- Attribute-Based Logging: Simple, declarative logging with
[LogClass],[LogMethod], and related attributes - Compile-Time Source Generation: Zero runtime overhead with C# Source Generators
- Microsoft.Extensions.Logging Integration: Works with your existing logging infrastructure
- Async/Await Support: Full support for async methods and Task-based operations
- Sensitive Data Protection: Automatically mask sensitive data with
[SensitiveData]attribute - Structured Logging: Rich, contextual logging with proper parameter serialization
- Configurable: Fine-grained control over what gets logged and how
- Dependency Injection: First-class support for Microsoft.Extensions.DependencyInjection
- Performance Optimized: Minimal overhead with intelligent logging decisions
Installation
Install the NuGet packages:
# Core library with attributes
dotnet add package AOP.Logging.Core
# Source Generator (required for compile-time code generation)
dotnet add package AOP.Logging.SourceGenerator
# Dependency Injection extensions
dotnet add package AOP.Logging.DependencyInjection
Quick Start
1. Configure Services
using AOP.Logging.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
// Add AOP logging
services.AddAopLogging(options =>
{
options.DefaultLogLevel = LogLevel.Information;
options.LogExecutionTime = true;
options.LogParameters = true;
options.LogReturnValues = true;
});
// Register your services with logging
services.AddTransientWithLogging<IMyService, MyService>();
})
.Build();
await host.RunAsync();
2. Add Logging to Your Classes
AOP.Logging supports two patterns for adding logging to your methods:
Pattern 1: Core Suffix (Traditional - Backward Compatible)
using AOP.Logging.Core.Attributes;
[LogClass]
public partial class MyService : IMyService
{
// Implement your business logic in private *Core methods
private int AddCore(int a, int b)
{
return a + b;
}
[LogMethod(LogLevel.Debug)]
private async Task<string> GetDataAsyncCore(int id)
{
await Task.Delay(100);
return $"Data for {id}";
}
// The Source Generator automatically creates public wrapper methods:
// - public int Add(int a, int b) - with automatic logging
// - public async Task<string> GetDataAsync(int id) - with automatic logging
}
Pattern 2: No Core Suffix (New - Flexible)
using AOP.Logging.Core.Attributes;
[LogClass]
public partial class OrderService : IOrderService
{
// Methods without "Core" suffix work too!
private async Task<string> CreateOrder(string customerId, decimal amount)
{
await Task.Delay(50);
return Guid.NewGuid().ToString();
}
// The Source Generator creates wrapper methods with "Logged" suffix:
// - public async Task<string> CreateOrderLogged(string customerId, decimal amount)
}
Important:
- Classes using AOP logging must be declared as
partial. - Core Suffix Pattern: Methods ending with
Coregenerate wrappers without the suffix (e.g.,AddCore→Add) - No Core Pattern: Methods without
Coregenerate wrappers withLoggedsuffix (e.g.,CreateOrder→CreateOrderLogged) - Both patterns can be mixed in the same class for maximum flexibility!
3. Run and See the Logs
info: MyService[0]
Entering MyService.Add
info: MyService[0]
Exiting MyService.Add (took 0ms)
Usage Examples
Note: The examples below show the business logic implementation. You can use either pattern:
- Core Suffix Pattern: Implement methods as
private *Coremethods (e.g.,AddCore,ProcessDataCore), and the generator creates wrappers without the suffix.- No Core Pattern: Implement methods without the Core suffix, and the generator creates wrappers with
Loggedsuffix (e.g.,CreateOrder→CreateOrderLogged).
Basic Method Logging
[LogClass]
public partial class CalculatorService
{
private int AddCore(int a, int b) => a + b;
private int MultiplyCore(int a, int b) => a * b;
// Source Generator creates: public int Add(int a, int b) and public int Multiply(int a, int b)
}
Custom Log Levels
[LogClass(LogLevel.Debug)]
public partial class DebugService
{
[LogMethod(LogLevel.Warning)]
public void PerformCriticalOperation()
{
// This method will log at Warning level
}
}
Sensitive Data Protection
[LogClass]
public partial class UserService
{
public async Task<User> CreateUserAsync(
string email,
[SensitiveData] string password)
{
// password will appear as "***SENSITIVE***" in logs
var user = new User { Email = email };
return user;
}
}
Exception Logging
[LogClass]
public partial class DataService
{
[LogException(LogLevel.Error)]
public void ProcessData(string data)
{
if (string.IsNullOrEmpty(data))
{
throw new ArgumentException("Data cannot be null");
}
// Exception will be automatically logged
}
}
Async Method Support
[LogClass]
public partial class ApiService
{
public async Task<ApiResponse> FetchDataAsync(string endpoint)
{
await Task.Delay(100);
return new ApiResponse { Data = "Success" };
}
public async Task<T> GetAsync<T>(string url)
{
// Generic async methods are fully supported
await Task.Delay(50);
return default(T)!;
}
}
Parameter Control
[LogClass]
public partial class ReportService
{
public Report Generate(
[LogParameter(Name = "ReportId")] int id,
[LogParameter(Skip = true)] string internalToken,
[LogParameter(MaxLength = 50)] string description)
{
// internalToken won't be logged
// description will be truncated to 50 characters
return new Report { Id = id };
}
}
Return Value Logging
[LogClass]
public partial class CalculationService
{
[LogResult(Name = "CalculationResult")]
public double Calculate(double x, double y)
{
return Math.Sqrt(x * x + y * y);
}
[LogResult(Skip = true)]
public byte[] GetBinaryData()
{
// Return value won't be logged (useful for large data)
return new byte[1024];
}
}
Selective Method Logging
[LogClass]
public partial class MixedService
{
public void LoggedMethod()
{
// This will be logged
}
[LogMethod(Skip = true)]
public void NotLoggedMethod()
{
// This will NOT be logged
}
}
Mixed Pattern Usage (Core + No Core)
[LogClass]
public partial class MixedPatternService
{
// Old pattern: Method with "Core" suffix
// Wrapper: public async Task<int> ProcessData(string data)
private async Task<int> ProcessDataCore(string data)
{
await Task.Delay(50);
return data.Length;
}
// New pattern: Method without "Core" suffix
// Wrapper: public bool ValidateInputLogged(string input)
private bool ValidateInput(string input)
{
return !string.IsNullOrEmpty(input);
}
}
Selective Logging Without [LogClass]
public partial class SelectiveService
{
// Only this method will be logged (has [LogMethod])
// Wrapper: public async Task<bool> ImportantOperationLogged(string data)
[LogMethod(LogLevel.Information)]
private async Task<bool> ImportantOperation(string data)
{
await Task.Delay(100);
return !string.IsNullOrEmpty(data);
}
// This won't be logged (no [LogMethod] and no [LogClass])
private void UnloggedHelper(string data)
{
// Not logged
}
}
Configuration
Global Options
services.AddAopLogging(options =>
{
// Default log level
options.DefaultLogLevel = LogLevel.Information;
// Execution time tracking
options.LogExecutionTime = true;
// Parameter and return value logging
options.LogParameters = true;
options.LogReturnValues = true;
// Exception logging
options.LogExceptions = true;
// String and collection limits
options.MaxStringLength = 1000;
options.MaxCollectionSize = 10;
// Structured logging
options.UseStructuredLogging = true;
// Namespace filtering
options.IncludedNamespaces.Add("MyApp.Services");
options.ExcludedNamespaces.Add("MyApp.Internal");
// Class filtering with wildcards
options.IncludedClasses.Add("*Service");
options.ExcludedClasses.Add("*Internal");
// Custom message formats
options.EntryMessageFormat = "→ {ClassName}.{MethodName}";
options.ExitMessageFormat = "← {ClassName}.{MethodName} ({ExecutionTime}ms)";
options.ExceptionMessageFormat = "✗ {ClassName}.{MethodName}: {ExceptionMessage}";
});
Message Format Placeholders
Entry Messages:
{ClassName}- The name of the class{MethodName}- The name of the method{Parameters}- Formatted parameter list
Exit Messages:
{ClassName}- The name of the class{MethodName}- The name of the method{ReturnValue}- The return value{ExecutionTime}- Execution time in milliseconds
Exception Messages:
{ClassName}- The name of the class{MethodName}- The name of the method{ExceptionType}- The exception type name{ExceptionMessage}- The exception message{ExecutionTime}- Execution time before exception
Attributes Reference
[LogClass]
Marks a class for automatic logging of all public methods.
[LogClass(LogLevel.Information)]
public partial class MyService { }
Properties:
LogLevel- Log level for all methods (default: Information)LogExecutionTime- Track execution time (default: true)LogParameters- Log method parameters (default: true)LogReturnValue- Log return values (default: true)LogExceptions- Log exceptions (default: true)
[LogMethod]
Controls logging for a specific method, overriding class-level settings.
[LogMethod(LogLevel.Debug)]
public void MyMethod() { }
Properties:
LogLevel- Log level for this methodLogExecutionTime- Track execution timeLogParameters- Log method parametersLogReturnValue- Log return valueLogExceptions- Log exceptionsSkip- Skip logging for this methodEntryMessage- Custom entry message templateExitMessage- Custom exit message template
[LogParameter]
Controls logging for a specific parameter.
public void MyMethod([LogParameter(Name = "UserId")] int id) { }
Properties:
Skip- Skip logging this parameterName- Custom name in logsMaxLength- Maximum length for string values
[LogResult]
Controls logging for method return values.
[LogResult(Name = "Result")]
public int Calculate() => 42;
Properties:
Skip- Skip logging the return valueName- Custom name in logsMaxLength- Maximum length for string values
[LogException]
Controls exception logging behavior.
[LogException(LogLevel.Error)]
public void RiskyOperation() { }
Properties:
LogLevel- Log level for exceptions (default: Error)IncludeDetails- Include stack trace and inner exceptions (default: true)Rethrow- Rethrow the exception after logging (default: true)Message- Custom exception message template
[SensitiveData]
Marks data as sensitive, preventing it from being logged.
public void Login([SensitiveData] string password) { }
Properties:
MaskValue- The mask to use (default: "SENSITIVE")ShowLength- Show the length of sensitive data (default: false)
Dependency Injection Extensions
Service Registration with Logging
// Transient
services.AddTransientWithLogging<IMyService, MyService>();
// Scoped
services.AddScopedWithLogging<IMyService, MyService>();
// Singleton
services.AddSingletonWithLogging<IMyService, MyService>();
These extension methods automatically inject the IMethodLogger into your services.
Best Practices
- Use
partialclasses: Classes with logging attributes must be declared aspartial - Protect sensitive data: Always use
[SensitiveData]for passwords, tokens, and PII - Choose appropriate log levels: Use Debug for verbose logging, Information for normal flow, Warning for unusual situations
- Limit collection sizes: Set
MaxCollectionSizeto prevent logging large collections - Skip unnecessary logging: Use
Skip = truefor methods that don't need logging - Use structured logging: Enable
UseStructuredLoggingfor better log analysis
Performance Considerations
- Compile-time generation: All logging code is generated at compile time, not runtime
- Zero reflection: No reflection is used during logging execution
- Conditional logging: Logs are only formatted when the log level is enabled
- Minimal allocations: Optimized for low memory allocation
- Async-friendly: Async methods are properly handled without blocking
Requirements
- .NET 8.0 or .NET 10.0
- C# 11.0 or higher
- Microsoft.Extensions.Logging 8.0+
Sample Project
Check out the sample project for complete working examples demonstrating all features.
To run the sample:
cd samples/AOP.Logging.Sample
dotnet run
Versioning
This project follows Semantic Versioning and uses Conventional Commits for automated version management.
Release Process
- Automatic Versioning: Versions are calculated automatically based on commit messages
- Conventional Commits: All commits must follow the conventional commits specification
- semantic-release: Automated version calculation and release management
See VERSIONING.md for detailed information about our versioning strategy.
Commit Message Format
# Features (bumps MINOR version)
feat: add custom interceptor support
# Bug fixes (bumps PATCH version)
fix: resolve null reference in logger
# Breaking changes (bumps MAJOR version)
breaking: redesign attribute API
Contributing
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
Important: All commits must follow Conventional Commits format for proper version management.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Roadmap
- Custom interceptor support
- Performance counters integration
- OpenTelemetry integration
- Configuration from appsettings.json
- Advanced filtering expressions
- Log correlation support
Made with ❤️ by the AOP.Logging community
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Extensions.Logging.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Options (>= 6.0.0)
-
net10.0
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Options (>= 8.0.0)
-
net8.0
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Options (>= 8.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on AOP.Logging.Core:
| Package | Downloads |
|---|---|
|
AOP.Logging.DependencyInjection
Dependency injection extensions for AOP.Logging framework, providing seamless integration with Microsoft.Extensions.DependencyInjection. |
GitHub repositories
This package is not used by any popular GitHub repositories.