ASP.NET 6.0中如何按API版本分目录存储Serilog日志文件?
按API版本分目录存储Serilog日志(ASP.NET 6.0)
针对你的需求,核心是利用Serilog的属性路由能力,基于日志Properties中的ApiVersion字段将日志分发到对应版本的目录。以下是两种可行方案及最佳实践:
方案一:使用WriteTo.Map(推荐)
Map是Serilog专门用于按属性值动态分配日志目标的工具,适合版本数量不固定或可能新增的场景,无需手动逐个配置版本。
在Program.cs中配置Serilog:
Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() // 确保日志上下文的属性能被捕获 .WriteTo.Map( // 指定要匹配的属性名 keyPropertyName: "ApiVersion", // 为每个不同的ApiVersion值创建专属日志文件 configureMapTarget: (apiVersion, writeTo) => writeTo.File( path: $"Logs/V{apiVersion}/logfile.json", formatter: new Serilog.Formatting.Json.JsonFormatter(), // 可选:添加滚动策略,避免文件过大 rollingInterval: RollingInterval.Day, retainedFileCountLimit: 30 ), // 处理无ApiVersion的日志(如启动日志、非API请求) configureDefaultTarget: writeTo => writeTo.File( path: "Logs/UnknownVersion/logfile.json", formatter: new Serilog.Formatting.Json.JsonFormatter() ) ) .CreateLogger(); // 注册Serilog到宿主 builder.Host.UseSerilog();
说明
- Serilog会自动创建不存在的目录,无需手动处理文件夹创建逻辑。
- 新增API版本时,无需修改Serilog配置,会自动生成对应目录的日志文件。
方案二:使用WriteTo.Conditional
如果你的API版本数量固定(比如仅v1.0和v2.0),可以用Conditional逐个判断属性值,逻辑更直观:
Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() // 匹配v1.0版本的日志 .WriteTo.Conditional( evt => evt.Properties.TryGetValue("ApiVersion", out var version) && version.ToString().Trim('"') == "1.0", wt => wt.File( path: "Logs/V1.0/logfile.json", formatter: new Serilog.Formatting.Json.JsonFormatter() ) ) // 匹配v2.0版本的日志 .WriteTo.Conditional( evt => evt.Properties.TryGetValue("ApiVersion", out var version) && version.ToString().Trim('"') == "2.0", wt => wt.File( path: "Logs/V2.0/logfile.json", formatter: new Serilog.Formatting.Json.JsonFormatter() ) ) // 兜底处理无版本的日志 .WriteTo.File( path: "Logs/UnknownVersion/logfile.json", formatter: new Serilog.Formatting.Json.JsonFormatter() ) .CreateLogger(); builder.Host.UseSerilog();
说明
- 适合版本数量少且固定的场景,但新增版本时需要手动添加新的
Conditional规则。
关键前置配置
确保ApiVersion全局注入到日志上下文:
如果仅在控制器中能拿到版本号,建议通过中间件或动作过滤器将版本信息全局添加到LogContext,这样所有请求日志(包括框架日志)都会带上ApiVersion属性:
// 自定义中间件 public class ApiVersionLoggingMiddleware { private readonly RequestDelegate _next; public ApiVersionLoggingMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { // 从请求端点元数据中获取API版本 if (context.GetEndpoint()?.Metadata.GetMetadata<ApiVersionMetadata>() is { } metadata) { var apiVersion = metadata.ApiVersions.FirstOrDefault()?.ToString(); if (!string.IsNullOrEmpty(apiVersion)) { // 将版本信息推入日志上下文,后续所有日志都会包含该属性 using (LogContext.PushProperty("ApiVersion", apiVersion)) { await _next(context); } return; } } await _next(context); } } // 在Program.cs中注册中间件(放在路由配置之前) app.UseMiddleware<ApiVersionLoggingMiddleware>();
最佳实践
- 优先选择
WriteTo.Map:扩展性更强,新增版本无需修改配置,适合长期维护。 - 统一版本格式:确保
ApiVersion的格式一致(如统一用"1.0"而非"v1.0"或"1"),避免映射逻辑出错。 - 添加日志滚动策略:配置
rollingInterval和retainedFileCountLimit,防止日志文件过大,同时自动清理旧日志。 - 全局注入版本属性:通过中间件而非控制器手动添加,保证所有日志(包括框架自动生成的请求日志)都带上版本信息,日志更完整。
- 验证日志路由:启动不同版本的API接口,检查对应目录是否生成日志文件,确认
ApiVersion属性正确写入日志。
内容的提问来源于stack exchange,提问作者Scottish Smile
相关产品推荐
相关产品推荐

