为什么需要日志上下文?
想象一下:你的系统每天产生上百万条日志,当用户报告一个错误时,你如何快速定位到该用户的操作轨迹?如果没有上下文信息(比如用户ID或请求ID),你可能需要花费数小时甚至数天来筛选日志。
通过添加日志追踪ID和业务相关字段,你可以将一次请求的所有日志串联起来,实现精准追踪。这就是结构化日志的核心价值。
准备工作:选择日志框架
在C#中,我们推荐使用 Microsoft.Extensions.Logging 配合 Serilog 或 NLog。本文以 Serilog 为例,因为它对结构化日志支持非常友好。
首先,通过 NuGet 安装以下包:
Install-Package SerilogInstall-Package Serilog.AspNetCoreInstall-Package Serilog.Sinks.Console
步骤一:配置Serilog并启用结构化日志
在 Program.cs 中初始化 Serilog:
using Serilog;var builder = WebApplication.CreateBuilder(args);// 配置SerilogLog.Logger = new LoggerConfiguration() .WriteTo.Console( outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}") .Enrich.FromLogContext() // 关键!启用日志上下文 .CreateLogger();builder.Host.UseSerilog(); // 使用Serilog作为日志提供者var app = builder.Build();app.Run();
步骤二:在代码中注入上下文信息
使用 LogContext.PushProperty 方法可以临时将属性推入当前日志上下文。这些属性会自动附加到后续所有日志中,直到作用域结束。
using (LogContext.PushProperty("UserId", userId))using (LogContext.PushProperty("RequestId", Guid.NewGuid().ToString())){ _logger.LogInformation("用户正在执行登录操作"); // 其他业务逻辑... _logger.LogInformation("登录成功");}
输出效果(控制台):
2024-06-15 10:30:45 [INF] 用户正在执行登录操作 {UserId: "12345", RequestId: "a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8"}2024-06-15 10:30:46 [INF] 登录成功 {UserId: "12345", RequestId: "a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8"}
步骤三:在ASP.NET Core中自动注入请求级上下文
对于Web应用,我们通常希望每个HTTP请求都有唯一的追踪ID。可以通过中间件自动实现:
// 在Program.cs中添加中间件app.Use(async (context, next) =>{ var requestId = context.Request.Headers["X-Request-ID"].FirstOrDefault() ?? Guid.NewGuid().ToString(); using (LogContext.PushProperty("RequestId", requestId)) { await next(); }});
这样,每个请求的日志都会自动带上 RequestId,便于全链路追踪。
高级技巧:自定义日志上下文类
为了复用和规范,可以封装一个上下文管理器:
public static class LogContextHelper{ public static IDisposable SetContext(string key, object value) => LogContext.PushProperty(key, value); public static IDisposable SetUserContext(string userId, string userName) { return LogContext.PushProperties( new PropertyEnricher("UserId", userId), new PropertyEnricher("UserName", userName)); }}
使用方式更简洁:
using (LogContextHelper.SetUserContext("12345", "张三")){ _logger.LogInformation("用户操作记录");}
总结:日志最佳实践
通过本文,你已经掌握了在C#中添加日志上下文的核心方法。记住以下日志最佳实践:
- 始终为每个请求生成唯一追踪ID(Trace ID)
- 敏感信息(如密码)不要写入日志
- 使用结构化日志而非拼接字符串
- 合理使用日志级别(Info/Warning/Error)
掌握这些技巧后,你的C#应用将具备强大的可观测性,故障排查效率提升数倍!
关键词回顾:C#日志上下文、结构化日志、日志追踪ID、日志最佳实践。