Mariodbx.AspectLogging
3.0.1
See the version list below for details.
dotnet add package Mariodbx.AspectLogging --version 3.0.1
NuGet\Install-Package Mariodbx.AspectLogging -Version 3.0.1
<PackageReference Include="Mariodbx.AspectLogging" Version="3.0.1" />
<PackageVersion Include="Mariodbx.AspectLogging" Version="3.0.1" />
<PackageReference Include="Mariodbx.AspectLogging" />
paket add Mariodbx.AspectLogging --version 3.0.1
#r "nuget: Mariodbx.AspectLogging, 3.0.1"
#:package Mariodbx.AspectLogging@3.0.1
#addin nuget:?package=Mariodbx.AspectLogging&version=3.0.1
#tool nuget:?package=Mariodbx.AspectLogging&version=3.0.1
AspectLogging
A powerful Aspect-Oriented Programming (AOP) library for .NET that provides transparent, non-invasive logging of method calls using dynamic proxies. AspectLogging enables comprehensive observability with distributed tracing without modifying your business logic.
Table of Contents
- Overview
- Architecture
- Key Features
- Installation
- Quick Start
- Configuration
- Distributed Tracing
- Sample Project
- Advanced Usage
- How It Works
- Performance Considerations
- API Reference
- Contributing
- License
Overview
AspectLogging implements Aspect-Oriented Programming principles to separate cross-cutting concerns (logging) from business logic. It uses .NET's DispatchProxy to create dynamic proxies that intercept method calls, providing comprehensive logging and distributed tracing without requiring changes to your existing codebase.
Core Capabilities
- Transparent Interception: Automatically wraps service interfaces with logging behavior
- Async/Await Support: Full support for asynchronous method interception
- Distributed Tracing: Full call graph correlation with parent/child spans
- OpenTelemetry Compatible: Uses System.Diagnostics.Activity for tracing
- Configurable Behavior: Fine-grained control over what and how to log
- HTTP Middleware: Middleware for request/response logging with automatic span creation
- Performance-Aware: Configurable performance thresholds and filtering
- Structured Logging: Works with any ILogger provider (Serilog, NLog, Console, etc.)
Architecture
System Architecture with Distributed Tracing
graph TB
subgraph "Client Layer"
Browser[Browser/Client]
Controller[ASP.NET Controller]
end
subgraph "Middleware Layer"
LoggingMiddleware[LoggingMiddleware]
TracingRoot[Create Root Span<br/>TraceId + SpanId]
end
subgraph "Tracing Infrastructure"
TracingContext[TracingContext<br/>AsyncLocal SpanInfo]
SpanInfo[SpanInfo<br/>Parent/Child Correlation]
ActivitySource[System.Diagnostics.Activity<br/>OpenTelemetry Compatible]
end
subgraph "Proxy Layer"
LoggingProxy[LoggingProxy T]
ChildSpan[Create Child Span<br/>ParentSpanId = Current.SpanId]
end
subgraph "Service Layer"
Service1[IProductService]
Service2[IInventoryService]
Service3[IOrderService]
end
subgraph "Logging Output"
Logger[ILogger]
LogScope[Log Scope<br/>TraceId, SpanId, ParentSpanId, Depth]
end
Browser --> LoggingMiddleware
LoggingMiddleware --> TracingRoot
TracingRoot --> TracingContext
TracingContext --> SpanInfo
SpanInfo --> ActivitySource
Controller --> LoggingProxy
LoggingProxy --> ChildSpan
ChildSpan --> TracingContext
LoggingProxy --> Service1
LoggingProxy --> Service2
LoggingProxy --> Service3
LoggingProxy --> Logger
TracingContext --> LogScope
LogScope --> Logger
style LoggingMiddleware fill:#e1f5ff
style LoggingProxy fill:#e1f5ff
style TracingContext fill:#fff4e1
style SpanInfo fill:#fff4e1
style Logger fill:#e8f5e9
Component Diagram
graph LR
subgraph "AspectLogging Library"
subgraph "Core Components"
LP[LoggingProxy of T]
SCE[ServiceCollectionExtensions]
end
subgraph "Tracing"
TC[TracingContext]
SI[SpanInfo]
TS[TracingScope]
end
subgraph "Configuration"
LPO[LoggingProxyOptions]
TO[TracingOptions]
Preset[LoggingPreset]
end
subgraph "Formatting"
VF[ValueFormatter]
TF[TypeFormatter]
end
subgraph "Attributes"
NLA[NoLogAttribute]
end
subgraph "Middleware"
LM[LoggingMiddleware]
end
end
SCE --> LP
SCE --> LPO
SCE --> Preset
LP --> LPO
LP --> TC
LP --> VF
LP --> NLA
LM --> TC
TC --> SI
TC --> TS
VF --> TF
LPO --> TO
style LP fill:#e1f5ff
style TC fill:#fff4e1
style SI fill:#fff4e1
style LM fill:#e1f5ff
Key Features
1. Dynamic Proxy Interception with Tracing
AspectLogging uses DispatchProxy to create runtime proxies that wrap your service interfaces with automatic span creation:
sequenceDiagram
participant Client
participant Middleware as LoggingMiddleware
participant Proxy as LoggingProxy
participant TracingCtx as TracingContext
participant Logger
participant Service as Service Implementation
Client->>Middleware: HTTP Request
activate Middleware
Middleware->>TracingCtx: StartHttpSpan()
Note over TracingCtx: Create Root Span<br/>TraceId, SpanId, Depth=0
Middleware->>Proxy: CallMethod(args)
activate Proxy
Proxy->>TracingCtx: StartMethodSpan()
Note over TracingCtx: Create Child Span<br/>ParentSpanId, Depth=1
Proxy->>Logger: Log Entry (TraceId, SpanId, ParentSpanId)
Proxy->>Service: Invoke Method(args)
activate Service
alt Success
Service-->>Proxy: Return Result
Proxy->>Logger: Log Exit (Duration, Depth)
Proxy->>TracingCtx: EndSpan()
Proxy-->>Middleware: Return Result
else Exception
Service--xProxy: Throw Exception
Proxy->>Logger: Log Exception (Error)
Proxy->>TracingCtx: EndSpan(error)
Proxy--xMiddleware: Rethrow Exception
end
deactivate Service
deactivate Proxy
Middleware->>TracingCtx: EndHttpSpan()
Middleware-->>Client: HTTP Response
deactivate Middleware
2. Deep Call Graph Tracing
Track complex service call hierarchies with parent/child span correlation:
graph TD
subgraph "HTTP Request - Depth 0"
HTTP[HTTP POST /api/orders<br/>TraceId: abc123<br/>SpanId: span-001<br/>ParentSpanId: null]
end
subgraph "Service Layer - Depth 1"
OrderSvc[IOrderService.PlaceOrderAsync<br/>SpanId: span-002<br/>ParentSpanId: span-001]
end
subgraph "Validation Layer - Depth 2"
ValidSvc[IOrderValidationService.ValidateOrderAsync<br/>SpanId: span-003<br/>ParentSpanId: span-002]
PricingSvc[IPricingService.CalculateBulkPriceAsync<br/>SpanId: span-004<br/>ParentSpanId: span-002]
end
subgraph "Data Layer - Depth 3"
ProdRepo[IProductRepository.GetByIdAsync<br/>SpanId: span-005<br/>ParentSpanId: span-003]
TaxSvc[ITaxCalculationService.CalculateTaxAsync<br/>SpanId: span-006<br/>ParentSpanId: span-004]
end
subgraph "Deep Operations - Depth 4-8"
StockVal[IStockValidationService.ValidateQuantityAsync<br/>SpanId: span-007<br/>ParentSpanId: span-005]
ShipSvc[IShippingCalculationService.EstimateAsync<br/>SpanId: span-008<br/>ParentSpanId: span-006]
end
HTTP --> OrderSvc
OrderSvc --> ValidSvc
OrderSvc --> PricingSvc
ValidSvc --> ProdRepo
PricingSvc --> TaxSvc
ProdRepo --> StockVal
TaxSvc --> ShipSvc
style HTTP fill:#e1f5ff
style OrderSvc fill:#fff4e1
style ValidSvc fill:#e8f5e9
style PricingSvc fill:#e8f5e9
style ProdRepo fill:#fce4ec
style TaxSvc fill:#fce4ec
style StockVal fill:#f3e5f5
style ShipSvc fill:#f3e5f5
3. Asynchronous Method Support
Full support for Task and Task<T> return types with proper async/await handling and span propagation:
sequenceDiagram
participant Client
participant Proxy as LoggingProxy
participant TracingCtx as TracingContext
participant Logger
participant Service as Async Service
Client->>Proxy: CallAsyncMethod()
activate Proxy
Proxy->>TracingCtx: StartMethodSpan()
Note over TracingCtx: Store in AsyncLocal<br/>Propagates to awaited calls
Proxy->>Logger: Log Entry
Proxy->>Service: Invoke Async Method
Note over Proxy: Start Stopwatch
Proxy-->>Client: Return Task
deactivate Proxy
Note over Service: Async Operation<br/>Span context preserved
Service-->>Proxy: Task Completes
activate Proxy
Note over Proxy: Stop Stopwatch
Proxy->>Logger: Log Exit (Duration, Result)
Proxy->>TracingCtx: EndSpan()
deactivate Proxy
4. Dependency Injection Integration
Seamless integration with Microsoft.Extensions.DependencyInjection:
graph TD
subgraph "Service Registration Flow"
A[IServiceCollection] --> B{AddAspectLogging}
B --> C[Scan Registered Services]
C --> D{Is Interface?}
D -->|Yes| E{Matches Namespace?}
D -->|No| F[Skip]
E -->|Yes| G{Is Excluded?}
E -->|No| F
G -->|No| H[Wrap with LoggingProxy]
G -->|Yes| F
H --> I[Replace Registration]
end
subgraph "Runtime Resolution"
J[Resolve IService] --> K[Get LoggingProxy]
K --> L[Proxy.Invoke]
L --> M[Create Span + Log + Call Original]
end
I -.-> J
style B fill:#e1f5ff
style H fill:#fff4e1
style K fill:#e1f5ff
Installation
Package Manager Console
Install-Package AspectLogging
.NET CLI
dotnet add package AspectLogging
Package Reference
<PackageReference Include="Mariodbx.AspectLogging" Version="3.0.0" />
Quick Start
1. Configure Services
using AspectLogging;
var builder = WebApplication.CreateBuilder(args);
// Configure logging (any ILogger-compatible provider works)
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
// Register your services
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddScoped<IInventoryService, InventoryService>();
// Add AspectLogging with distributed tracing
builder.Services.AddAspectLogging(options =>
{
options.Preset = LoggingPreset.Development;
options.Tracing.Enabled = true;
options.Tracing.ServiceName = "MyApp";
});
var app = builder.Build();
// Enable HTTP request logging with automatic span creation
app.UseLoggingMiddleware();
2. Use Preset Configuration
// Simple preset-based configuration
builder.Services.AddAspectLogging(LoggingPreset.Balanced);
// Preset with namespace filtering
builder.Services.AddAspectLogging(
LoggingPreset.Development,
"MyApp.Services",
"MyApp.Repositories");
3. JSON Configuration
{
"LoggingProxy": {
"IncludedNamespaces": ["MyApp.Services", "MyApp.Repositories"],
"LogMethodEntry": true,
"LogMethodExit": true,
"LogDetailedArguments": true,
"LogReturnValues": true,
"LogExceptions": true,
"EntryLogLevel": "Debug",
"ExitLogLevel": "Debug",
"ExceptionLogLevel": "Error",
"Tracing": {
"Enabled": true,
"ServiceName": "MyApp",
"PushToLogContext": true,
"LogSpanEnd": true
}
}
}
builder.Services.AddLoggingProxyForInterfaces(
builder.Configuration,
"LoggingProxy");
Example Output
With distributed tracing enabled, your logs will include full span correlation:
[INF] [TraceId:SpanId] HTTP POST /api/orders - Request started
SpanDepth: 0 | ParentSpanId: root
[INF] [TraceId:span-001] → Enter IOrderService.PlaceOrderAsync
parent: root-span | depth: 1
[INF] [TraceId:span-002] → Enter IOrderValidationService.ValidateOrderAsync
parent: span-001 | depth: 2
[INF] [TraceId:span-003] → Enter IProductRepository.GetByIdAsync
parent: span-002 | depth: 3
[INF] [TraceId:span-003] ← Exit IProductRepository.GetByIdAsync (11ms)
parent: span-002 | depth: 3
[INF] [TraceId:span-004] → Enter IStockValidationService.ValidateQuantityAsync
parent: span-003 | depth: 4
[INF] [TraceId:span-004] ← Exit IStockValidationService.ValidateQuantityAsync (19ms)
parent: span-003 | depth: 4
[INF] [TraceId:span-002] ← Exit IOrderValidationService.ValidateOrderAsync (254ms)
parent: span-001 | depth: 2
[INF] [TraceId:span-001] ← Exit IOrderService.PlaceOrderAsync (709ms)
parent: root-span | depth: 1
[INF] [TraceId:SpanId] HTTP POST /api/orders completed: 200 in 783ms
Configuration
Configuration Options Reference
graph TD
subgraph "LoggingProxyOptions"
A[IncludedNamespaces]
B[ExcludedServices]
C[ExcludedMethods]
D[LogMethodEntry]
E[LogMethodExit]
F[LogDetailedArguments]
G[LogReturnValues]
H[LogExceptions]
I[EntryLogLevel]
J[ExitLogLevel]
K[ExceptionLogLevel]
L[MinimumDurationMs]
M[MaxArgumentLength]
N[Tracing]
end
subgraph "TracingOptions"
O[Enabled]
P[ServiceName]
Q[PushToLogContext]
R[LogSpanStart]
S[LogSpanEnd]
T[SpanLogLevel]
end
N --> O
N --> P
N --> Q
N --> R
N --> S
N --> T
style A fill:#e1f5ff
style N fill:#fff4e1
style O fill:#e8f5e9
Complete Configuration Example
builder.Services.AddLoggingProxyForInterfaces(options =>
{
// Namespace filtering
options.IncludedNamespaces = new[] { "MyApp.Services", "MyApp.Repositories" };
options.ExcludedServices = new HashSet<Type> { typeof(IHealthCheckService) };
options.ExcludedMethods = new HashSet<string> { "ToString", "GetHashCode" };
// Logging behavior
options.LogMethodEntry = true;
options.LogMethodExit = true;
options.LogDetailedArguments = true;
options.LogReturnValues = true;
options.LogExceptions = true;
// Log levels
options.EntryLogLevel = LogLevel.Debug;
options.ExitLogLevel = LogLevel.Debug;
options.ExceptionLogLevel = LogLevel.Error;
// Performance
options.MinimumDurationMs = 0; // Log all calls (use > 0 for slow-only)
options.MaxArgumentLength = 500;
// Distributed tracing
options.Tracing.Enabled = true;
options.Tracing.ServiceName = "MyApp";
options.Tracing.PushToLogContext = true;
options.Tracing.LogSpanEnd = true;
options.Tracing.SpanLogLevel = LogLevel.Debug;
});
Preset Configurations
AspectLogging provides four preset configurations optimized for different environments:
graph LR
subgraph "LoggingPreset Options"
Dev[Development]
Bal[Balanced]
Prod[Production]
Min[Minimal]
end
subgraph "Development Features"
D1[✓ Entry Logging]
D2[✓ Exit Logging]
D3[✓ Detailed Arguments]
D4[✓ Return Values]
D5[✓ Exceptions]
D6[✓ Tracing Enabled]
D7[Duration: 0ms]
end
subgraph "Production Features"
P1[✗ Entry Logging]
P2[✓ Exit Logging]
P3[✗ Detailed Arguments]
P4[✗ Return Values]
P5[✓ Exceptions]
P6[✓ Tracing Enabled]
P7[Duration: 100ms]
end
Dev --> D1
Dev --> D2
Dev --> D3
Dev --> D4
Dev --> D5
Dev --> D6
Dev --> D7
Prod --> P1
Prod --> P2
Prod --> P3
Prod --> P4
Prod --> P5
Prod --> P6
Prod --> P7
style Dev fill:#e8f5e9
style Bal fill:#fff4e1
style Prod fill:#e1f5ff
style Min fill:#fce4ec
| Preset | Entry | Exit | Args | Returns | Exceptions | Tracing | Min Duration |
|---|---|---|---|---|---|---|---|
| Development | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 0ms |
| Balanced | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | 0ms |
| Production | ✗ | ✓ | ✗ | ✗ | ✓ | ✓ | 100ms |
| Minimal | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | 1000ms |
Usage
// Development - maximum observability
builder.Services.AddAspectLogging(LoggingPreset.Development);
// Production - exceptions and slow operations only
builder.Services.AddAspectLogging(LoggingPreset.Production);
HTTP Middleware
The logging middleware creates root spans for HTTP requests and logs request/response details:
app.UseLoggingMiddleware();
Middleware Pipeline
sequenceDiagram
participant Client
participant Middleware as LoggingMiddleware
participant TracingCtx as TracingContext
participant Logger
participant Next as Next Middleware
participant App as Application
Client->>Middleware: HTTP Request
activate Middleware
Middleware->>TracingCtx: StartHttpSpan()
Note over TracingCtx: Create Root Span<br/>TraceId, SpanId, Depth=0
Middleware->>Logger: Log Request (Method, Path, TraceId)
Note over Middleware: Start Stopwatch
alt Has Request Body
Middleware->>Middleware: Buffer & Read Body
Middleware->>Logger: Log Request Body
end
Middleware->>Next: InvokeAsync(context)
activate Next
Next->>App: Process Request
Note over App: Service calls create<br/>child spans automatically
App-->>Next: Response
Next-->>Middleware: Continue
deactivate Next
Note over Middleware: Stop Stopwatch
Middleware->>Logger: Log Response (Status, Duration, TraceId)
Middleware->>TracingCtx: EndHttpSpan()
Middleware-->>Client: HTTP Response
deactivate Middleware
Distributed Tracing
Overview
AspectLogging provides built-in distributed tracing that correlates HTTP requests with all downstream service calls. The middleware and logging proxy work together to create a complete call graph.
Tracing Architecture
graph TB
subgraph "Tracing Flow"
HTTP[HTTP Request] --> MW[LoggingMiddleware]
MW --> |StartHttpSpan| Root[Root Span<br/>Depth: 0]
Root --> |AsyncLocal| TC[TracingContext]
TC --> Svc1[Service Call 1]
Svc1 --> |StartMethodSpan| Span1[Child Span<br/>Depth: 1]
Span1 --> Svc2[Service Call 2]
Svc2 --> |StartMethodSpan| Span2[Child Span<br/>Depth: 2]
Span2 --> Svc3[Service Call 3]
Svc3 --> |StartMethodSpan| Span3[Child Span<br/>Depth: 3]
Span3 --> Svc4[Service Call 4]
Svc4 --> |StartMethodSpan| Span4[Child Span<br/>Depth: 4+]
end
subgraph "Span Correlation"
Root2[TraceId: abc<br/>SpanId: 001<br/>Parent: null]
Span1b[TraceId: abc<br/>SpanId: 002<br/>Parent: 001]
Span2b[TraceId: abc<br/>SpanId: 003<br/>Parent: 002]
Span3b[TraceId: abc<br/>SpanId: 004<br/>Parent: 003]
Span4b[TraceId: abc<br/>SpanId: 005<br/>Parent: 004]
end
Root -.-> Root2
Span1 -.-> Span1b
Span2 -.-> Span2b
Span3 -.-> Span3b
Span4 -.-> Span4b
style Root fill:#e1f5ff
style Span1 fill:#fff4e1
style Span2 fill:#e8f5e9
style Span3 fill:#fce4ec
style Span4 fill:#f3e5f5
SpanInfo Structure
Each span contains:
| Property | Description |
|---|---|
SpanId |
Unique identifier for this span |
ParentSpanId |
ID of the parent span (null for root) |
TraceId |
Shared ID linking all spans in a request |
OperationName |
Method or operation name |
Depth |
Nesting level (0 = HTTP, 1+ = service calls) |
Kind |
Server, Client, Internal, Producer, Consumer |
Tags |
Custom key-value metadata |
StartTime |
When the span started |
DurationMs |
Execution time in milliseconds |
OpenTelemetry Compatibility
AspectLogging uses System.Diagnostics.Activity for compatibility with OpenTelemetry:
// Access the activity source for custom instrumentation
var activitySource = TracingContext.ActivitySource;
// Add OpenTelemetry exporters
builder.Services.AddOpenTelemetry()
.WithTracing(builder => builder
.AddSource("AspectLogging")
.AddJaegerExporter()
.AddZipkinExporter());
Tracing Configuration
options.Tracing.Enabled = true; // Enable/disable tracing
options.Tracing.ServiceName = "MyApp"; // Tag spans with service name
options.Tracing.PushToLogContext = true; // Add to ILogger.BeginScope
options.Tracing.LogSpanStart = false; // Log when spans start
options.Tracing.LogSpanEnd = true; // Log when spans end
options.Tracing.SpanLogLevel = LogLevel.Debug;
Sample Project
The repository includes a comprehensive sample project demonstrating all features: samples/AspectLogging.Sample.WebApi
Sample Architecture
graph TB
subgraph "MVC Frontend"
Home[HomeController]
Views[Razor Views<br/>Bootstrap 5]
end
subgraph "API Controllers"
Products[ProductsController]
Orders[OrdersController]
Pricing[PricingController]
end
subgraph "Business Services"
ProdSvc[IProductService]
OrderSvc[IOrderService]
PricingSvc[IPricingService]
InvSvc[IInventoryService]
end
subgraph "Validation & Processing"
OrderVal[IOrderValidationService]
StockVal[IStockValidationService]
PaySvc[IPaymentService]
NotifySvc[INotificationService]
end
subgraph "Pricing Components"
TaxSvc[ITaxCalculationService]
ShipSvc[IShippingCalculationService]
DiscountSvc[IDiscountService]
end
subgraph "Data Layer"
Repo[IProductRepository]
end
Home --> ProdSvc
Home --> OrderSvc
Home --> PricingSvc
Products --> ProdSvc
Orders --> OrderSvc
Pricing --> PricingSvc
ProdSvc --> InvSvc
ProdSvc --> Repo
OrderSvc --> OrderVal
OrderSvc --> PaySvc
OrderSvc --> NotifySvc
OrderSvc --> PricingSvc
OrderVal --> Repo
OrderVal --> StockVal
InvSvc --> StockVal
StockVal --> Repo
PricingSvc --> TaxSvc
PricingSvc --> ShipSvc
PricingSvc --> DiscountSvc
style Home fill:#e1f5ff
style OrderSvc fill:#fff4e1
style PricingSvc fill:#fff4e1
style StockVal fill:#e8f5e9
style Repo fill:#fce4ec
Running the Sample
cd samples/AspectLogging.Sample.WebApi
dotnet run
Navigate to http://localhost:5000 to access:
| Page | Description |
|---|---|
| Dashboard | Overview with statistics |
| Products | Product listing with CRUD operations |
| Tracing Demo | One-click demonstration of deep call chains |
| Pricing Calculator | Interactive pricing with complex calculations |
| Cart & Orders | Full order flow with validation and payment |
Sample Call Depth
The sample demonstrates spans up to 8+ levels deep:
HTTP POST /Home/RunComplexOperation [Depth 0]
└── IOrderService.PlaceOrderAsync [Depth 1]
├── IOrderValidationService.ValidateOrderRequestAsync [Depth 2]
│ ├── IProductRepository.GetByIdAsync [Depth 3]
│ │ └── IStockValidationService.ValidateQuantityAsync [Depth 4]
│ │ └── IProductRepository.GetByIdAsync [Depth 5]
│ │ └── IStockValidationService.ValidateQuantityAsync [Depth 6]
│ │ └── IProductRepository.GetByIdAsync [Depth 7]
│ │ └── IStockValidationService.ValidateQuantityAsync [Depth 8]
│ └── ...
├── IPricingService.CalculateBulkPriceAsync [Depth 2]
│ ├── IProductRepository.GetByIdAsync [Depth 3]
│ │ └── ... [Depth 4-7]
│ ├── IDiscountService.CalculateCouponDiscountAsync [Depth 6]
│ │ └── ITaxCalculationService.CalculateTaxAmountAsync [Depth 7]
│ │ └── IShippingCalculationService.GetBaseShippingRateAsync [Depth 8]
│ └── ...
├── IPaymentService.ProcessPaymentAsync [Depth 2]
└── INotificationService.SendOrderConfirmationAsync [Depth 2]
Advanced Usage
Excluding Methods from Logging
Use the [NoLog] attribute to exclude sensitive methods:
public interface IUserService
{
Task<User> GetUserAsync(int id);
[NoLog] // This method won't be logged
Task<string> GetPasswordHashAsync(int userId);
}
// Or exclude an entire interface
[NoLog]
public interface ISensitiveDataService
{
// None of these methods will be logged
Task<string> GetSsnAsync(int userId);
Task<string> GetCreditCardAsync(int userId);
}
Custom Tracing Tags
Add custom metadata to spans:
// In your service method
TracingContext.SetTag("customer.id", customerId);
TracingContext.SetTag("order.total", orderTotal);
TracingContext.SetTag("payment.method", "CreditCard");
Manual Span Creation
For non-proxied code that needs tracing:
using (var scope = TracingContext.StartMethodSpan("CustomOperation"))
{
// Your code here
// Span will be automatically closed and timed
}
Accessing Current Span Info
var currentSpan = TracingContext.Current;
if (currentSpan != null)
{
var traceId = currentSpan.TraceId;
var spanId = currentSpan.SpanId;
var depth = currentSpan.Depth;
}
How It Works
Method Interception Flow
sequenceDiagram
participant Consumer
participant Proxy as LoggingProxy
participant Tracing as TracingContext
participant Logger as ILogger
participant Service as Implementation
Consumer->>Proxy: Method Call
activate Proxy
Note over Proxy: Check [NoLog] attribute
alt Has [NoLog]
Proxy->>Service: Direct call
Service-->>Proxy: Result
Proxy-->>Consumer: Result
else Normal Flow
Proxy->>Tracing: StartMethodSpan(methodName)
Note over Tracing: Create child span<br/>Push to AsyncLocal
Proxy->>Logger: Log Entry (with TraceId, SpanId, Depth)
Note over Proxy: Start Stopwatch
alt Sync Method
Proxy->>Service: Invoke
Service-->>Proxy: Result
else Async Method
Proxy->>Service: Invoke
Note over Service: Async execution
Service-->>Proxy: Task completes
end
Note over Proxy: Stop Stopwatch
alt Success
Proxy->>Logger: Log Exit (Duration, Result)
else Exception
Proxy->>Logger: Log Exception
Proxy->>Proxy: Rethrow
end
Proxy->>Tracing: EndSpan()
Proxy-->>Consumer: Result/Exception
end
deactivate Proxy
Service Registration Process
flowchart TD
A[AddAspectLogging Called] --> B[Get All Service Descriptors]
B --> C{For Each Descriptor}
C --> D{Is Interface Type?}
D -->|No| C
D -->|Yes| E{Matches Namespace?}
E -->|No| C
E -->|Yes| F{Has NoLog Attribute?}
F -->|Yes| C
F -->|No| G{Is Excluded Type?}
G -->|Yes| C
G -->|No| H[Create Proxy Factory]
H --> I[Replace ServiceDescriptor]
I --> J[Factory creates LoggingProxy]
J --> K[Proxy wraps original implementation]
K --> C
C --> L[Registration Complete]
style A fill:#e1f5ff
style H fill:#fff4e1
style J fill:#e8f5e9
style K fill:#e8f5e9
Performance Considerations
Overhead Analysis
graph LR
subgraph "Performance Impact Factors"
A[Proxy Creation] -->|One-time| B[Negligible]
C[Method Interception] -->|Per Call| D[Minimal]
E[Logging Operations] -->|Per Call| F[Configurable]
G[Span Creation] -->|Per Call| H[~1μs]
I[Formatting] -->|Per Value| J[Moderate]
end
subgraph "Optimization Strategies"
K[Use MinimumDurationMs]
L[Disable DetailedArgs/Returns]
M[Use Production Preset]
N[Exclude High-Frequency Methods]
O[Use Async Logging Sink]
end
F --> K
F --> L
F --> M
J --> N
D --> O
style K fill:#c8e6c9
style L fill:#c8e6c9
style M fill:#c8e6c9
style N fill:#c8e6c9
style O fill:#c8e6c9
Recommendations
| Environment | Recommended Preset | Notes |
|---|---|---|
| Development | Development |
Maximum observability |
| Staging | Balanced |
Good coverage with lower overhead |
| Production | Production |
Exceptions and slow operations only |
| High-Performance | Minimal |
Exceptions only, no tracing |
Benchmarks
Typical overhead per method call:
| Operation | Overhead |
|---|---|
| Proxy interception | < 1 μs |
| Span creation | ~1 μs |
| Basic logging | 5-10 μs |
| Detailed argument logging | 10-50 μs (depends on complexity) |
| Async method additional | < 1 μs |
API Reference
Core Classes
LoggingProxy<T>
Dynamic proxy that intercepts method calls on interface T with automatic span creation.
ServiceCollectionExtensions
// Preset with auto-detection
IServiceCollection AddAspectLogging(
this IServiceCollection services,
LoggingPreset preset = LoggingPreset.Balanced)
// Preset with namespaces
IServiceCollection AddAspectLogging(
this IServiceCollection services,
LoggingPreset preset,
params string[] namespaces)
// Lambda configuration
IServiceCollection AddAspectLogging(
this IServiceCollection services,
Action<LoggingProxyOptions> configure)
// Full manual configuration
IServiceCollection AddLoggingProxyForInterfaces(
this IServiceCollection services,
Action<LoggingProxyOptions> configure)
// IConfiguration binding
IServiceCollection AddLoggingProxyForInterfaces(
this IServiceCollection services,
IConfiguration configuration,
string sectionName = "LoggingProxy")
TracingContext
Static class for tracing context management.
// Properties
static SpanInfo? Current { get; }
static string? CurrentTraceId { get; }
static string? CurrentSpanId { get; }
static int CurrentDepth { get; }
static ActivitySource ActivitySource { get; }
// Methods
static TracingScope StartHttpSpan(HttpContext context, string? serviceName)
static TracingScope StartMethodSpan(string operationName, SpanKind kind)
static void SetTag(string key, object? value)
SpanInfo
Represents a tracing span with parent/child correlation.
LoggingMiddleware
ASP.NET Core middleware for HTTP request/response logging with automatic root span creation.
Attributes
[NoLog]
Excludes methods, interfaces, or classes from logging:
[NoLog]
public interface ISensitiveService { }
public interface IUserService
{
[NoLog]
Task<string> GetPasswordHashAsync(int userId);
}
Formatters
ValueFormatter
Formats values for logging with intelligent collection truncation.
TypeFormatter
Provides human-readable type names for generic types.
System Requirements
- .NET: 8.0 or 9.0
- Dependencies:
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- Microsoft.AspNetCore.Http.Abstractions (>= 2.2.0)
Contributing
Contributions are welcome! Please read CONTRIBUTING.md for guidelines.
Development Setup
# Clone the repository
git clone https://github.com/mariodbx/aspect-logging.git
cd aspect-logging
# Restore dependencies
dotnet restore
# Build the solution
dotnet build
# Run tests (208 tests)
dotnet test
# Run the sample
cd samples/AspectLogging.Sample.WebApi
dotnet run
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
- GitHub Issues: https://github.com/mariodbx/aspect-logging/issues
- Documentation: https://github.com/mariodbx/aspect-logging
- NuGet Package: https://www.nuget.org/packages/Mariodbx.AspectLogging
Version: 3.0.0
Author: Mario De Benedictis
Repository: https://github.com/mariodbx/aspect-logging
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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 is compatible. 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 was computed. 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. |
-
net8.0
- Microsoft.AspNetCore.Http (>= 2.2.2)
- Microsoft.AspNetCore.Http.Abstractions (>= 2.2.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
-
net9.0
- Microsoft.AspNetCore.Http (>= 2.2.2)
- Microsoft.AspNetCore.Http.Abstractions (>= 2.2.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v1.1.0 - Distributed Tracing
- Add TracingContext, SpanInfo, TracingScope for span hierarchy management
- Add TracingOptions for tracing configuration
- Enhance LoggingMiddleware to create root spans for HTTP requests
- Enhance LoggingProxy to create child spans for service method calls
- OpenTelemetry compatibility via System.Diagnostics.Activity
- Comprehensive sample project with MVC frontend
v1.0.0 - Initial release
- Dynamic proxy-based method interception
- Configurable via code or appsettings.json
- Async method support
- Custom type and value formatting
- HTTP middleware for request/response logging
- Performance thresholds and filtering
- Structured logging support