Skip to content

Repository files navigation

LayeredCraft.StructuredLogging

Build StatusNuGetDownloads

Simplified, structured logging for modern .NET apps — overloads, conditionals, and performance built-in.

Features

  • 🚀 High Performance - Built-in level checks and efficient parameter handling
  • 📊 Structured Logging - Rich contextual data with strongly-typed parameters
  • 🔧 Easy Integration - Drop-in replacement for standard ILogger calls
  • 🎯 Scope Management - Comprehensive scope tracking with automatic disposal
  • Performance Monitoring - Built-in timing and performance tracking
  • 🧪 Testing Support - In-memory TestLogger with assertions and search helpers
  • 📦 Multi-Target - Supports .NET 8.0, .NET 9.0, .NET 10.0, .NET Standard 2.1, and .NET Standard 2.0

Installation

dotnet add package LayeredCraft.StructuredLogging

Quick Start

usingLayeredCraft.StructuredLogging;usingMicrosoft.Extensions.Logging;// Basic logginglogger.Information("User logged in successfully");logger.Warning("Rate limit exceeded for user {UserId}",userId);logger.Error(exception,"Failed to process order {OrderId}",orderId);// Structured logging with multiple parameterslogger.Information("Order processed for user {UserId} with total {Total:C}",userId,orderTotal);// Performance monitoringusing(logger.TimeOperation("Database operation")){// Your database code here}// Automatically logs execution time// Enriched logging with contextlogger.LogWithContext(LogLevel.Information,"Starting order processing","UserId",userId);logger.InformationWithUserId(userId,"Order processing started");

Core Extensions

Log Level Extensions

All standard log levels are supported with convenient extension methods:

// Debug logginglogger.Debug("Debug information");logger.Debug("Processing item {ItemId}",itemId);// Verbose/Trace logginglogger.Verbose("Detailed trace information");logger.Verbose("Entering method {MethodName}",methodName);// Information logginglogger.Information("Operation completed successfully");logger.Information("User {UserId} performed action {Action}",userId,action);// Warning logginglogger.Warning("Performance threshold exceeded");logger.Warning("Retry attempt {AttemptNumber} for operation {OperationId}",attemptNumber,operationId);// Error logginglogger.Error("Operation failed");logger.Error(exception,"Failed to save entity {EntityId}",entityId);// Critical logginglogger.Critical("System is in critical state");logger.Critical(exception,"Database connection lost");

Scope Management

Create logging scopes for better context tracking:

// Simple scopesusing(logger.BeginScope("UserRegistration")){logger.Information("Starting user registration");// Registration logic}// Structured scopes with propertiesusing(logger.BeginScope("OrderId",orderId)){logger.Information("Processing order");// Order processing logic}// Complex scopes with multiple propertiesusing(logger.BeginScopeWith(new{UserId=userId,SessionId=sessionId})){logger.Information("User session started");// Session logic}// Caller-aware scopes (adds MemberName/FilePath/LineNumber)using(logger.BeginCallerScope()){logger.Debug("Tracing caller details");}// Timed scopes for performance monitoringusing(logger.TimeOperation("DatabaseQuery")){// Database operation}// Automatically logs execution time

Enrichment

Add contextual information to log entries:

// Context-specific logging methodslogger.LogWithUserId(LogLevel.Information,userId,"User operation completed");logger.LogWithRequestId(LogLevel.Information,requestId,"Request processed");logger.LogWithCorrelationId(LogLevel.Information,correlationId,"Service call completed");// Convenience methods for common log levelslogger.InformationWithUserId(userId,"User profile updated");logger.WarningWithRequestId(requestId,"Request took longer than expected");logger.ErrorWithCorrelationId(correlationId,"Service call failed",exception);// Custom context enrichmentlogger.LogWithContext(LogLevel.Information,"Operation completed","Duration",duration);logger.LogWithContext(LogLevel.Warning,"Rate limit approaching","UserId",userId);// Automatic caller informationlogger.LogWithCaller(LogLevel.Debug,"Method execution completed");logger.InformationWithCaller("Operation finished successfully");

Performance Monitoring

Built-in performance tracking capabilities:

// Timed operationsusing(logger.TimeOperation("DatabaseQuery")){// Your database code}// Logs: "DatabaseQuery completed in 150ms"// Synchronous timed operationsvarresult=logger.Time("CalculateSum",()=>{returnnumbers.Sum();});// Asynchronous timed operationsawaitlogger.TimeAsync("FetchUserData",async()=>{awaituserService.GetUserAsync(userId);});// Method-level timing with caller infousing(logger.TimeMethod()){// Current method is automatically timed}

Testing Support

TestLogger implements ILogger and captures entries in memory so you can assert against what was written. Extension methods help you inspect entries, check for the presence of messages, and perform simple assertions that throw InvalidOperationException when they fail.

[Fact]publicvoidShould_Log_User_Registration(){// ArrangevartestLogger=newTestLogger();varuserService=newUserService(testLogger);// ActuserService.RegisterUser("john@example.com");// AsserttestLogger.AssertLogCount(1);testLogger.AssertLogEntry(LogLevel.Information,"User registered");varentry=testLogger.GetLastLogEntry();entry!.FormattedMessage.Should().Contain("john@example.com");}[Fact]publicvoidShould_Handle_Registration_Errors(){// ArrangevartestLogger=newTestLogger();varuserService=newUserService(testLogger);// Act & AssertAssert.Throws<ValidationException>(()=>userService.RegisterUser("invalid-email"));testLogger.AssertLogEntry(LogLevel.Error,"Invalid email format");testLogger.HasLogEntryWithException<ValidationException>().Should().BeTrue();}

TestLogger Features

// Get specific log entriesvarlastEntry=testLogger.GetLastLogEntry();varsecondEntry=testLogger.GetLogEntry(1);varerrorEntries=testLogger.GetLogEntries(LogLevel.Error);varentriesWithException=testLogger.GetLogEntriesWithException<ArgumentException>();// Search log entriesvaruserEntries=testLogger.GetLogEntriesContaining("user");varhasError=testLogger.HasLogEntry(LogLevel.Error,"failed");varhasArgumentError=testLogger.HasLogEntryWithException<ArgumentException>(LogLevel.Error);// AssertionstestLogger.AssertLogEntry(LogLevel.Warning,"threshold");testLogger.AssertLogEntryAt(0,LogLevel.Information,"started");testLogger.AssertLogCount(5);testLogger.AssertLogCount(LogLevel.Error,1);testLogger.AssertNoLogEntries();// Clear logs between teststestLogger.Clear();// Optional: only record entries at or above this level (defaults to Trace)testLogger.MinimumLogLevel=LogLevel.Information;

Advanced Usage

Conditional Logging

All logging methods include built-in level checks for optimal performance:

// These methods automatically check if the level is enabledlogger.Debug("Expensive debug info: {Data}",ExpensiveOperation());// ExpensiveOperation() only called if Debug level is enabled

Exception Handling

Robust exception logging with context:

try{// Risky operation}catch(Exceptionex){logger.Error(ex,"Operation failed for user {UserId} in context {Context}",userId,operationContext);throw;}

Integration with Dependency Injection

// Program.cs / Startup.csservices.AddLogging(builder =>{builder.AddConsole();builder.AddSerilog();// or any other provider});// In your servicespublicclassOrderService{privatereadonlyILogger<OrderService>_logger;publicOrderService(ILogger<OrderService>logger){_logger=logger;}publicasyncTaskProcessOrderAsync(intorderId){using(_logger.TimeOperation("OrderProcessing")){_logger.Information("Starting order processing for {OrderId}",orderId);try{// Processing logic_logger.Information("Order {OrderId} processed successfully",orderId);}catch(Exceptionex){_logger.Error(ex,"Failed to process order {OrderId}",orderId);throw;}}}}

Configuration

The library works with any ILogger implementation and follows standard .NET logging configuration:

// appsettings.json{"Logging":{"LogLevel":{"Default":"Information","Microsoft":"Warning","System":"Warning"}}}

Performance Considerations

  • All methods include automatic level checks to avoid expensive operations
  • Structured parameters use efficient formatting
  • Scopes are implemented with minimal overhead
  • Testing framework is optimized for fast test execution

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

See CHANGELOG.md for a detailed history of changes.


Built with ❤️ by LayeredCraft

About

Simplified, structured logging for modern .NET apps — overloads, conditionals, and performance built-in.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages