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

.NET 6 Web Api中Serilog ElasticSearch Sink自定义属性配置问题

问题解决步骤

1. 修复自定义Enricher的拼写错误

你的Enricher代码里存在两处拼写错误,这是配置不生效的直接原因:

  • cusotmer.name 应为 customer.name
  • cusomerName 应为 customerName

同时,为了满足外部要求的动态字段(user.name、user.phone),我们需要注入IHttpContextAccessor来获取请求上下文的用户信息,修改后的Enricher代码如下:

public class ApiEnricher : ILogEventEnricher
{
    private const string bankName = "Total Bank";
    private const decimal totalValue = 21;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public ApiEnricher(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        // 添加固定基础字段
        logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("bankName", bankName));
        logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("total.value", totalValue));
        logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("customer.name", "Adam"));

        // 从请求上下文获取动态用户字段
        var httpContext = _httpContextAccessor.HttpContext;
        if (httpContext != null)
        {
            // 根据你的认证方式调整Claims类型,示例用JWT标准Claims
            var userName = httpContext.User.Claims.FirstOrDefault(c => c.Type == "name")?.Value ?? "Anonymous";
            var userPhone = httpContext.User.Claims.FirstOrDefault(c => c.Type == "phone_number")?.Value ?? string.Empty;
            
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("user.name", userName));
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("user.phone", userPhone));
        }

        // 添加调用类信息(复用Serilog默认的SourceContext)
        if (logEvent.Properties.TryGetValue("SourceContext", out var sourceContextVal))
        {
            var className = sourceContextVal.ToString().Trim('"');
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("class", className));
        }
        else
        {
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("class", "Unknown"));
        }

        // Exception字段无需额外处理:Serilog会自动捕获日志语句中传入的Exception对象
    }
}

2. 配置Enricher仅对Elasticsearch Sink生效

要避免全局Enrich影响其他Sink(比如Console),需要把Enricher配置移到Elasticsearch的Sink节点下,同时创建扩展方法让Serilog识别该Enricher:

步骤2.1 添加Serilog扩展方法

创建一个静态类注册Enricher的扩展方法:

public static class SerilogEnricherExtensions
{
    public static LoggerConfiguration WithApiEnricher(this LoggerEnrichmentConfiguration enrichmentConfig)
    {
        if (enrichmentConfig == null)
            throw new ArgumentNullException(nameof(enrichmentConfig));
        
        return enrichmentConfig.With<ApiEnricher>();
    }
}

步骤2.2 修改appsettings.json配置

移除全局Enrich数组中的ApiEnricher,将其添加到Elasticsearch Sink的Args中:

"Serilog": {
    "Using": [ "Serilog.Sinks.Elasticsearch", "WebApi.Common" ],
    "MinimumLevel": "Debug",
    "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
    "WriteTo": [
        {
            "Name": "Console",
            "Args": {
                "outputTemplate": "[{Timestamp:HH:mm:ss.fff} {Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}"
            }
        },
        {
            "Name": "Elasticsearch",
            "Args": {
                "nodeUris": "http://host.docker.internal:9200",
                "indexFormat": "logs-myApp-example-{0:yyyy.MM}",
                "autoRegisterTemplate": true,
                "inlineFields": true,
                "numberOfReplicas": 2,
                "numberOfShards": 2,
                "enrich": [ "WithApiEnricher" ]
            }
        }
    ]
}

3. 注册依赖并初始化Serilog

在Program.cs中注册IHttpContextAccessor,并让Serilog读取依赖注入服务:

var builder = WebApplication.CreateBuilder(args);

// 注册HttpContextAccessor用于获取请求上下文
builder.Services.AddHttpContextAccessor();

// 配置Serilog,读取配置文件和依赖服务
builder.Host.UseSerilog((context, services, config) => config
    .ReadFrom.Configuration(context.Configuration)
    .ReadFrom.Services(services)
);

// 其他服务注册和中间件配置...

4. 验证要求字段的实现

  • user.name/user.phone:从当前请求的用户Claims中动态获取,确保每个请求的用户信息准确
  • class:复用Serilog默认生成的SourceContext(即日志所在类的全名,通过Logger<TCategoryName>注入时自动生成)
  • exception:只要在日志语句中传入Exception对象(如logger.LogError(ex, "操作失败")),Serilog会自动将其序列化到ES日志中
  • 固定字段(bankName、total.value、customer.name):通过Enricher自动添加,无需修改现有日志语句

内容的提问来源于stack exchange,提问作者Pan Michal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 13:50:05