
假设你有一个执行关键任务的应用程序,并添加了日志记录:
internal sealed class Handler(ILogger<Handler> logger) : IHandler
{
public async Task InvokeAsync(CancellationToken cancellationToken)
{
logger.LogInformation("处理器 {HandlerName} 开始工作", nameof(Handler));
// 执行异步操作
await Task.Delay(TimeSpan.FromMicroseconds(), cancellationToken);
logger.LogInformation("处理器 {HandlerName} 完成工作", nameof(Handler));
}
}标准日志输出:
info: LevelUpLogs.Handler[0]
处理器 Handler 开始工作
info: LevelUpLogs.Handler[0]
处理器 Handler 完成工作核心问题: 当需要追踪特定请求时,现有日志缺乏关联标识,无法跨条目跟踪完整链路。
在 appsettings.json 开启 IncludeScopes:
{
"Logging": {
"Console": {
"IncludeScopes": true // <-- 关键配置
}
}
}优化后日志:
info: LevelUpLogs.Handler[0]
=> SpanId:64b2903e475a475b, TraceId:cfccdb7d8f91e4a3468abd1e870cd469...
处理器 Handler 开始工作
info: LevelUpLogs.Handler[0]
=> SpanId:64b2903e475a475b, TraceId:cfccdb7d8f91e4a3468abd1e870cd469...
处理器 Handler 完成工作优势:通过 SpanId/TraceId 实现请求链路追踪
internal interfaceICorrelationIdProvider
{
string? CorrelationId { get; set; }
}
internalsealedclassCorrelationIdProvider : ICorrelationIdProvider
{
publicstring? CorrelationId { get; set; }
}
// 注册为作用域服务
builder.Services.AddScoped<ICorrelationIdProvider, CorrelationIdProvider>();internal sealed class CorrelationIdMiddleware(RequestDelegate next)
{
public async Task Invoke(HttpContext context, ICorrelationIdProvider provider)
{
conststring HeaderName = "X-Correlation-ID";
// 从请求头获取或生成新ID
context.Request.Headers.TryGetValue(HeaderName, outvar headerValue);
provider.CorrelationId = !string.IsNullOrEmpty(headerValue)
? headerValue.ToString()
: Guid.NewGuid().ToString();
await next(context);
}
}internal sealed class Handler(
ILogger<Handler> logger,
ICorrelationIdProvider provider) : IHandler
{
public async Task InvokeAsync(CancellationToken ct)
{
// 创建日志范围
using (logger.BeginScope(provider.CorrelationId!))
{
logger.LogInformation("处理器 {HandlerName} 开始工作", nameof(Handler));
await Task.Delay(TimeSpan.FromMicroseconds(), ct);
logger.LogInformation("处理器 {HandlerName} 完成工作", nameof(Handler));
}
}
}日志输出:
info: LevelUpLogs.Handler[0]
=> ... => 255fb6aa-4cae-4fc4-876a-35e0c059bb8a
处理器 Handler 开始工作
info: LevelUpLogs.Handler[0]
=> ... => 255fb6aa-4cae-4fc4-876a-35e0c059bb8a
处理器 Handler 完成工作{
"Logging": {
"Console": {
"IncludeScopes": false // 禁用范围输出
}
}
}internal sealed class Handler(ILogger<Handler> logger, ICorrelationIdProvider provider) : IHandler
{
privateconstint _eventId = ;
// 预编译日志模板
privatestaticreadonly Action<ILogger, string, string, Exception?> _logStart =
LoggerMessage.Define<string, string>(LogLevel.Information,
new EventId(_eventId, nameof(LogStart)),
"[{CorrelationId}] 处理器 {HandlerName} 开始工作");
privatestaticreadonly Action<ILogger, string, string, Exception?> _logEnd =
LoggerMessage.Define<string, string>(LogLevel.Information,
new EventId(_eventId, nameof(LogEnd)),
"[{CorrelationId}] 处理器 {HandlerName} 完成工作");
public async Task InvokeAsync(CancellationToken ct)
{
// 高性能日志调用
_logStart(logger, provider.CorrelationId!, nameof(Handler), null);
await Task.Delay(TimeSpan.FromMicroseconds(), ct);
_logEnd(logger, provider.CorrelationId!, nameof(Handler), null);
}
}优化后日志:
info: LevelUpLogs.Handler[100]
[43f26b8e-e9a8-411b-9865-f88ad667d59c] 处理器 Handler 开始工作
info: LevelUpLogs.Handler[100]
[43f26b8e-e9a8-411b-9865-f88ad667d59c] 处理器 Handler 完成工作三大优势:
方案 | 追踪能力 | 性能 | 可读性 |
|---|---|---|---|
基础日志 | ❌ | ⭐⭐⭐⭐ | ⭐⭐ |
日志范围 | ⭐⭐⭐ | ⭐⭐⭐ | ⭐ |
关联ID+日志范围 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
关联ID+预编译 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
Q:何时必须使用关联ID? A:当你的应用涉及:
Q:两种方案如何选择?
点击下方卡片关注DotNet NB