Mariodbx.AspectLogging 3.1.0

dotnet add package Mariodbx.AspectLogging --version 3.1.0
                    
NuGet\Install-Package Mariodbx.AspectLogging -Version 3.1.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.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Mariodbx.AspectLogging" Version="3.1.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.1.0
                    
#r "nuget: Mariodbx.AspectLogging, 3.1.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.1.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.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Mariodbx.AspectLogging&version=3.1.0
                    
Install as a Cake Tool

AspectLogging

A powerful Aspect-Oriented Programming (AOP) library for .NET that provides transparent, non-invasive logging and distributed tracing of method calls using dynamic proxies. AspectLogging enables comprehensive observability without modifying your business logic.

NuGet Build Status License: MIT .NET

Table of Contents

Overview

AspectLogging implements Aspect-Oriented Programming principles to separate cross-cutting concerns (logging, tracing) 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.

Why AspectLogging?

  • Zero Code Changes: Add observability without modifying your services
  • Provider Agnostic: Works with any ILogger provider (Console, Serilog, NLog, etc.)
  • Distributed Tracing: Full call graph correlation with parent/child spans
  • Terminal Compatible: ASCII-based output works in all terminals
  • Production Ready: Configurable presets for different environments

Key Features

Feature Description
Dynamic Proxy Interception Automatically wraps service interfaces with logging
Async/Await Support Full support for Task and Task<T> methods
Distributed Tracing Parent/child span correlation across service calls
Timestamps Configurable timestamps in log output
HTTP Middleware Request/response logging with automatic span creation
Depth Visualization Visual indentation showing call hierarchy
OpenTelemetry Compatible Uses System.Diagnostics.Activity
Configurable Presets Development, Balanced, Production, Minimal

Installation

.NET CLI

dotnet add package Mariodbx.AspectLogging

Package Manager Console

Install-Package Mariodbx.AspectLogging

Package Reference

<PackageReference Include="Mariodbx.AspectLogging" Version="3.0.1" />

Quick Start

1. Configure Services

using AspectLogging;

var builder = WebApplication.CreateBuilder(args);

// Configure your logging provider (any ILogger-compatible provider works)
builder.Logging.AddConsole();

// Register your services
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddScoped<IOrderService, OrderService>();

// Add AspectLogging
builder.Services.AddAspectLogging(options =>
{
    options.Enabled = true;
    options.IncludeNamespacePrefixes = new[] { "MyApp.Services" };
    
    // Logging options
    options.LogEntry = true;
    options.LogExit = true;
    options.LogExceptions = true;
    
    // Tracing options
    options.Tracing.Enabled = true;
    options.Tracing.ServiceName = "MyApp";
    options.Tracing.IncludeTimestamp = true;
    options.Tracing.IncludeSpanDepth = true;
});

var app = builder.Build();

// Enable HTTP request logging
app.UseLoggingMiddleware();

app.Run();

2. Use Preset Configuration

// Simple preset-based configuration
builder.Services.AddAspectLogging(LoggingPreset.Development);

// Preset with namespace filtering
builder.Services.AddAspectLogging(
    LoggingPreset.Development, 
    "MyApp.Services", 
    "MyApp.Repositories");

Log Output Format

AspectLogging produces clear, ASCII-compatible log output:

14:32:15.123 [e0d6063f1e26:386805c03872] --> Enter IOrderService.PlaceOrderAsync (parent: N/A, depth: 0) with args []
14:32:15.125 [e0d6063f1e26:32319e266a10]   --> Enter IPricingService.CalculatePrice (parent: 386805c03872, depth: 1) with args [productId: int]
14:32:15.130 [e0d6063f1e26:b4974870b49a]     --> Enter IProductRepository.GetByIdAsync (parent: 32319e266a10, depth: 2) with args []
14:32:15.145 [e0d6063f1e26:b4974870b49a]     <-- Exit IProductRepository.GetByIdAsync (parent: 32319e266a10, depth: 2, ms=15) with return Product
14:32:15.147 [e0d6063f1e26:32319e266a10]   <-- Exit IPricingService.CalculatePrice (parent: 386805c03872, depth: 1, ms=22) with return Decimal
14:32:15.150 [e0d6063f1e26:386805c03872] <-- Exit IOrderService.PlaceOrderAsync (parent: N/A, depth: 0, ms=27) with return Order

Log Format Breakdown

Component Example Description
Timestamp 14:32:15.123 Time of the log entry (HH:mm:ss.fff)
TraceId e0d6063f1e26 Unique ID for the entire request
SpanId 386805c03872 Unique ID for this specific operation
Arrow --> / <-- Entry (-->) or Exit (<--)
Method IOrderService.PlaceOrderAsync Full method name
Parent parent: 386805c03872 Parent span for correlation
Depth depth: 1 Call hierarchy level (with indentation)
Duration ms=22 Execution time in milliseconds
Return with return Order Return type or value

Exception Logging

14:32:15.200 [e0d6063f1e26:386805c03872] [X] Exception in IOrderService.PlaceOrderAsync (parent: N/A, depth: 0, ms=50)
System.InvalidOperationException: Order validation failed
   at MyApp.Services.OrderService.PlaceOrderAsync()

Configuration

Full Configuration Options

builder.Services.AddAspectLogging(options =>
{
    // === Core Options ===
    options.Enabled = true;                                    // Enable/disable globally
    options.IncludeNamespacePrefixes = new[] { "MyApp" };     // Namespaces to intercept
    options.ExcludedServiceTypeFullNames = new[] { "MyApp.IHealthCheck" };
    options.IgnoredMethods = new[] { "ToString", "GetHashCode" };
    
    // === Logging Behavior ===
    options.LogEntry = true;                                   // Log method entry
    options.LogExit = true;                                    // Log method exit
    options.LogExceptions = true;                              // Log exceptions
    options.LogDetailedArgs = false;                           // Log argument values
    options.LogDetailedReturns = false;                        // Log return values
    options.IncludeParameterNames = true;                      // Include param names
    
    // === Log Levels ===
    options.EntryLogLevel = LogLevel.Information;
    options.ExitLogLevel = LogLevel.Information;
    options.ExceptionLogLevel = LogLevel.Error;
    
    // === Performance ===
    options.MinimumDurationMs = 0;                             // Min duration to log
    options.SlowExecutionThresholdMs = 1000;                   // Warn level threshold
    options.MaxReturnValueLength = 500;                        // Truncate long values
    
    // === Tracing Options ===
    options.Tracing.Enabled = true;                            // Enable distributed tracing
    options.Tracing.ServiceName = "MyApp";                     // Service identifier
    options.Tracing.IncludeTimestamp = true;                   // Add timestamp to logs
    options.Tracing.IncludeSpanDepth = true;                   // Show depth with indent
    options.Tracing.MaxSpanDepth = 10;                         // Max trace depth
    options.Tracing.CreateMethodSpans = true;                  // Create spans for methods
    options.Tracing.CreateHttpSpans = true;                    // Create spans for HTTP
    options.Tracing.PushToLogContext = true;                   // Add to ILogger scope
    
    // === Middleware Options ===
    options.Middleware.Enabled = true;
    options.Middleware.LogRequestStart = true;
    options.Middleware.LogRequestEnd = true;
    options.Middleware.LogHeaders = false;
    options.Middleware.LogRequestBody = false;
    options.Middleware.LogResponseBody = false;
    options.Middleware.ExcludedPaths = new[] { "/health", "/swagger*", "*.css", "*.js" };
});

JSON Configuration

{
  "AspectLogging": {
    "Enabled": true,
    "IncludeNamespacePrefixes": ["MyApp.Services", "MyApp.Repositories"],
    "LogEntry": true,
    "LogExit": true,
    "LogExceptions": true,
    "EntryLogLevel": "Information",
    "ExitLogLevel": "Information",
    "ExceptionLogLevel": "Error",
    "Tracing": {
      "Enabled": true,
      "ServiceName": "MyApp",
      "IncludeTimestamp": true,
      "IncludeSpanDepth": true,
      "MaxSpanDepth": 10,
      "PushToLogContext": true
    },
    "Middleware": {
      "Enabled": true,
      "ExcludedPaths": ["/health", "/swagger*"]
    }
  }
}
builder.Services.AddAspectLogging(builder.Configuration);

Preset Configurations

Preset Entry Exit Args Returns Exceptions Tracing Min Duration
Development 0ms
Balanced 0ms
Production 100ms
Minimal 1000ms

Distributed Tracing

Call Graph Visualization

AspectLogging tracks the complete call hierarchy:

HTTP POST /api/orders                                    [Depth 0]
└── IOrderService.PlaceOrderAsync                        [Depth 1]
    ├── IOrderValidationService.ValidateAsync            [Depth 2]
    │   ├── IProductRepository.GetByIdAsync              [Depth 3]
    │   └── IStockService.CheckAvailabilityAsync         [Depth 3]
    ├── IPricingService.CalculateTotalAsync              [Depth 2]
    │   ├── IDiscountService.ApplyDiscountsAsync         [Depth 3]
    │   └── ITaxService.CalculateTaxAsync                [Depth 3]
    └── IPaymentService.ProcessPaymentAsync              [Depth 2]

Span Properties

Property Description
TraceId Unique ID shared across all spans in a request
SpanId Unique ID for this specific operation
ParentSpanId ID of the parent span (null for root)
Depth Nesting level (0 = HTTP request)
OperationName Method or operation name
DurationMs Execution time in milliseconds

OpenTelemetry Integration

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddSource("AspectLogging")
        .AddJaegerExporter()
        .AddZipkinExporter());

HTTP Middleware

Basic Usage

app.UseLoggingMiddleware();

Middleware Output

14:32:15.100 [e0d6063f1e26:root] HTTP POST /api/orders - Request started
14:32:15.150 [e0d6063f1e26:root] HTTP POST /api/orders completed: 200 in 50ms

Middleware Configuration

options.Middleware.Enabled = true;
options.Middleware.LogRequestStart = true;
options.Middleware.LogRequestEnd = true;
options.Middleware.LogHeaders = false;           // Log request headers
options.Middleware.LogRequestBody = false;       // Log request body (careful with sensitive data)
options.Middleware.LogResponseBody = false;      // Log response body
options.Middleware.IncludeTraceId = true;
options.Middleware.ExcludedPaths = new[] 
{ 
    "/health", 
    "/swagger*", 
    "/favicon.ico",
    "*.css", 
    "*.js" 
};

Advanced Usage

Excluding Methods with [NoLog]

public interface IUserService
{
    Task<User> GetUserAsync(int id);
    
    [NoLog] // This method won't be logged
    Task<string> GetPasswordHashAsync(int userId);
}

// Exclude entire interface
[NoLog]
public interface ISensitiveDataService
{
    Task<string> GetSsnAsync(int userId);
}

Custom Tracing Tags

// In your service method
TracingContext.SetTag("customer.id", customerId);
TracingContext.SetTag("order.total", orderTotal);

Manual Span Creation

using (var scope = TracingContext.StartMethodSpan("CustomOperation"))
{
    // Your code here
    // Span automatically timed and closed
}

Accessing Current Trace Info

var traceId = TracingContext.CurrentTraceId;
var spanId = TracingContext.CurrentSpanId;
var depth = TracingContext.CurrentDepth;

Integrating with Entity Framework Logging

To see database commands between your service logs:

services.AddDbContext<YourDbContext>(options =>
{
    options.UseSqlServer(connectionString)
           .UseLoggerFactory(loggerFactory)
           .EnableSensitiveDataLogging();  // Shows parameter values
});

This produces interleaved output:

14:32:15.130 [e0d6063f1e26:b497] --> Enter IProductRepository.GetByIdAsync (parent: 3231, depth: 2)
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (5ms) SELECT * FROM Products WHERE Id = @p0
14:32:15.145 [e0d6063f1e26:b497] <-- Exit IProductRepository.GetByIdAsync (parent: 3231, depth: 2, ms=15)

Sample Project

A comprehensive sample is included: samples/AspectLogging.Sample.WebApi

cd samples/AspectLogging.Sample.WebApi
dotnet run

Navigate to http://localhost:5000 for:

  • Dashboard with statistics
  • Products listing
  • Tracing demo
  • Pricing calculator
  • Order flow demonstration

API Reference

ServiceCollectionExtensions

// Preset configuration
IServiceCollection AddAspectLogging(LoggingPreset preset)
IServiceCollection AddAspectLogging(LoggingPreset preset, params string[] namespaces)

// Lambda configuration
IServiceCollection AddAspectLogging(Action<LoggingProxyOptions> configure)

// Configuration binding
IServiceCollection AddAspectLogging(IConfiguration configuration)

TracingContext

static SpanInfo? Current { get; }
static string? CurrentTraceId { get; }
static string? CurrentSpanId { get; }
static int CurrentDepth { get; }
static ActivitySource ActivitySource { get; }

static TracingScope StartHttpSpan(HttpContext context, string? serviceName)
static TracingScope StartMethodSpan(string operationName, SpanKind kind)
static void SetTag(string key, object? value)

Attributes

[NoLog] // Exclude method or interface from logging

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.

# Clone the repository
git clone https://github.com/mariodbx/aspect-logging.git
cd aspect-logging

# Build
dotnet build

# Run tests (208 tests)
dotnet test

# Run 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.1
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 662 12/3/2025
3.0.1 665 12/3/2025
3.0.0 575 12/1/2025
2.0.0 191 11/27/2025
1.4.1 584 12/1/2025
1.0.1 196 11/27/2025
1.0.0 198 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