.NET 6 Web Api中Serilog ElasticSearch Sink自定义属性配置问题
问题解决步骤
1. 修复自定义Enricher的拼写错误
你的Enricher代码里存在两处拼写错误,这是配置不生效的直接原因:
cusotmer.name应为customer.namecusomerName应为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
相关产品推荐
相关产品推荐

