Roadmap › Logging & Custom Logging in .NET — ILogger, Serilog, NLog, log4net and Your Own Logger › Module: Logging Fundamentals
Lessons in this course (8)
3. Scopes, correlation ids and high-performance logging
Intermediate★ Must knowLesson 3 of 8
Scopes attach properties to every log written inside a block:
csharp
using (logger.BeginScope(new Dictionary<string, object> { ["OrderId"] = order.Id, ["CustomerId"] = order.CustomerId }))
{
logger.LogInformation("Validating"); // includes OrderId and CustomerId
logger.LogInformation("Charging"); // same
}ASP.NET Core automatically adds RequestId, RequestPath, TraceId, SpanId scopes (when the provider includes scopes, e.g. "IncludeScopes": true for console).
Correlation id — one id that follows a request through UI → API → other services → logs:
csharp
app.Use(async (ctx, next) =>
{
var id = ctx.Request.Headers["X-Correlation-ID"].FirstOrDefault() ?? Activity.Current?.TraceId.ToString() ?? Guid.NewGuid().ToString();
ctx.Response.Headers["X-Correlation-ID"] = id;
using (ctx.RequestServices.GetRequiredService<ILogger<Program>>().BeginScope(new Dictionary<string, object> { ["CorrelationId"] = id }))
await next();
});With OpenTelemetry/W3C trace context, the TraceId already plays this role across services automatically.
High-performance logging — the source generator (.NET 6+):
csharp
public static partial class Log
{
[LoggerMessage(EventId = 1001, Level = LogLevel.Information, Message = "Order {OrderId} placed for {Total}")]
public static partial void OrderPlaced(ILogger logger, int orderId, decimal total);
[LoggerMessage(EventId = 5001, Level = LogLevel.Error, Message = "Payment failed for order {OrderId}")]
public static partial void PaymentFailed(ILogger logger, Exception ex, int orderId);
}
Log.OrderPlaced(logger, order.Id, order.Total); // no boxing, no template parsing at runtime, level checked firstAlso: if (logger.IsEnabled(LogLevel.Debug)) before expensive message construction; avoid logging in tight loops; let sinks batch asynchronously.