You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>();

最佳实践

  1. 优先选择WriteTo.Map:扩展性更强,新增版本无需修改配置,适合长期维护。
  2. 统一版本格式:确保ApiVersion的格式一致(如统一用"1.0"而非"v1.0"或"1"),避免映射逻辑出错。
  3. 添加日志滚动策略:配置rollingInterval和retainedFileCountLimit,防止日志文件过大,同时自动清理旧日志。
  4. 全局注入版本属性:通过中间件而非控制器手动添加,保证所有日志(包括框架自动生成的请求日志)都带上版本信息,日志更完整。
  5. 验证日志路由:启动不同版本的API接口,检查对应目录是否生成日志文件,确认ApiVersion属性正确写入日志。

内容的提问来源于stack exchange,提问作者Scottish Smile

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 19:02:43