NuGet Publish to NuGet License: MIT GitHub Sponsors Changelog
A modern structured logging library for .NET with support for custom enrichers, JSON output, and full Dependency Injection integration.
Why StructLog? Standard
ILogger<T>lets you log anything, but doesn't enforce a consistent structure. StructLog requires every log entry to carry an EventCode β a short, queryable identifier that makes filtering and alerting in production trivial.
- π§ Structured JSON output β every log entry is a consistent, queryable JSON object
- π·οΈ EventCode β mandatory code + description on every entry, great for monitoring and alerting
- β‘ Custom Enrichers β inject contextual data (machine name, request ID, tenant, ...) via
ILogEnricher - π DI-ready β works with
ILogger<T>andIOptions<StructLogOptions>out of the box - π Configurable via
appsettings.json - π₯οΈ Multi-host β Console, Worker Service, and ASP.NET Core apps
| Requirement | Minimum version |
|---|---|
| .NET | 9.0+ |
dotnet add package StructLog
{
"StructLog": {
"EnableEnrichers": true
}
}// With appsettings.json builder.Services.AddStructLog(builder.Configuration); // Without appsettings (uses defaults) builder.Services.AddStructLog();
[ApiController] public class UsersController : ControllerBase { private readonly IStructLog<UsersController> _logger; public UsersController(IStructLog<UsersController> logger) { _logger = logger; } [HttpGet("{id}")] public async Task<IActionResult> GetUser(int id) { var user = await GetUserFromDb(id); if (user is null) { _logger.Warning( "User not found", new EventCode("USR_001", "Get user"), new { UserId = id }); return NotFound(); } _logger.Info( "User retrieved successfully", new EventCode("USR_002", "Get user"), user); return Ok(user); } }
{
"EventCode": "USR_002",
"EventDescription": "Get user",
"Message": "User retrieved successfully",
"Context": {
"MachineName": "server-01"
},
"Data": {
"Id": 1,
"Name": "Simone",
"Surname": "Rossi",
"Birthday": "1993εΉ΄05ζ12ζ₯T00:00:00"
}
}All methods accept an IEventCode and an optional data object.
_logger.Trace("Entering method", eventCode); _logger.Debug("Cache miss", eventCode, new { Key = "users:1" }); _logger.Info("User created", eventCode, user); _logger.Warning("Retry attempt", eventCode, new { Attempt = 3 }); _logger.Error("Payment failed", eventCode, exception, new { OrderId = 42 }); _logger.Critical("DB unreachable", eventCode, exception);
Use BeginScope to attach contextual data to all logs within a block:
using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = HttpContext.TraceIdentifier, ["UserId"] = currentUserId })) { _logger.Info("Processing request", new EventCode("REQ_001", "Request")); }
EventCode is the core of StructLog. Every log entry must carry one, which makes your logs
immediately searchable and alertable in tools like Kibana, Grafana, or Azure Monitor.
// Simple inline usage var code = new EventCode("PAY_003", "Payment processing"); // Recommended: define codes as constants in a dedicated class public static class LogEvents { public static readonly EventCode UserCreated = new("USR_001", "User lifecycle"); public static readonly EventCode UserDeleted = new("USR_002", "User lifecycle"); public static readonly EventCode PaymentFailed = new("PAY_001", "Payment processing"); } // Usage _logger.Error("Payment declined", LogEvents.PaymentFailed, exception, new { OrderId = 99 });
Enrichers automatically attach contextual data to every log entry.
Implement ILogEnricher and register it with DI:
// 1. Implement the interface public class MachineNameEnricher : ILogEnricher { public void Enrich(IDictionary<string, object> context) { context["MachineName"] = Environment.MachineName; } } // 2. Register β all ILogEnricher implementations are picked up automatically builder.Services.AddSingleton<ILogEnricher, MachineNameEnricher>(); // 3. Enable in appsettings.json // "StructLog": { "EnableEnrichers": true }
You can register as many enrichers as you need. Common use cases: machine name, environment, request ID, tenant ID, application version.
| Option | Type | Default | Description |
|---|---|---|---|
EnableEnrichers |
bool |
true |
Whether enrichers are invoked for each log entry |
StructLog is part of a cohesive set of lightweight, DI-ready toolkits for ASP.NET Core:
| Package | Description |
|---|---|
| AspNetCoreAuthKit | JWT, API Key, Refresh Tokens with rotation & dynamic claim policies |
| AspNetCoreResponseKit | Standardized ApiResponse<T>, global exception handling & smart factories |
| AspNetCoreHttpKit | Typed HTTP client with result pattern & StructLog integration |
| AspNetCoreCacheKit | Group-based caching with per-entry & per-group duration |
| StructLog | Structured logging with EventCode support & custom enrichers |
Each package works independently β use one or all five.
If you find StructLog useful, consider sponsoring its development on GitHub Sponsors. Even a small contribution helps keep the project maintained and growing.
MIT β see LICENSE for details.