Mariodbx.AspectLogging 3.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Mariodbx.AspectLogging --version 3.0.0
                    
NuGet\Install-Package Mariodbx.AspectLogging -Version 3.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Mariodbx.AspectLogging" Version="3.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Mariodbx.AspectLogging" Version="3.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Mariodbx.AspectLogging" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Mariodbx.AspectLogging --version 3.0.0
                    
#r "nuget: Mariodbx.AspectLogging, 3.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Mariodbx.AspectLogging@3.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Mariodbx.AspectLogging&version=3.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Mariodbx.AspectLogging&version=3.0.0
                    
Install as a Cake Tool

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.

NuGet Build Status License: MIT

Table of Contents

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


Version: 3.0.0
Author: Mario De Benedictis
Repository: https://github.com/mariodbx/aspect-logging

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.1.0 701 12/3/2025
3.0.1 679 12/3/2025
3.0.0 590 12/1/2025
2.0.0 209 11/27/2025
1.4.1 600 12/1/2025
1.0.1 209 11/27/2025
1.0.0 209 11/25/2025

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