.NET Core 3.1 WebAPI中Serilog日志ClientIp字段为空排查
问题原因
你配置的ClientIp、ClientAgent字段为空并非语法错误,是两处核心配置遗漏导致:
Serilog.Enrichers.ClientInfo组件依赖IHttpContextAccessor获取请求上下文信息,.NET Core 3.1默认不注册该服务,组件拿不到HttpContext实例自然返回空值。- 你原有配置用了嵌套子Logger结构,根节点配置的Enrich规则不会自动被子Logger继承,控制台、MSSqlServer两个输出目标实际根本没有执行IP和UA的取值逻辑。
- 补充说明:应用启动阶段的日志(比如监听端口、服务启动提示)本身没有关联HTTP请求上下文,这部分日志的ClientIp为空是正常现象,只有进入请求处理管道后产生的日志才会携带对应值。
修复步骤
1. 注入HttpContextAccessor服务
打开项目的Startup.cs,在ConfigureServices方法中添加服务注册:
public void ConfigureServices(IServiceCollection services) { // 新增这行,注册IHttpContextAccessor services.AddHttpContextAccessor(); // 保留原有其他服务注册逻辑,比如AddControllers、AddDbContext等 }
2. 简化修正Serilog配置
原配置中嵌套两层Logger的写法完全多余,不仅容易丢失Enrich规则,还增加配置复杂度,直接去掉嵌套,将输出目标平铺在WriteTo节点即可,修正后的Serilog配置参考:
"Serilog": { "MinimumLevel": { "Default": "Debug", "Override": { "Microsoft": "Warning", "System": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "Enrich": [ "FromLogContext", "WithMachineName", "WithClientIp", "WithClientAgent" ], "WriteTo": [ { "Name": "Console", "Args": { "outputTemplate": "[{Level:u3} {Timestamp:o}] {ClientIp} {ClientAgent} ({SourceContext}) {Message} {NewLine}" } }, { "Name": "MSSqlServer", "Args": { "connectionString": "TestDbContext", "tableName": "ErrorLogs", "batchPostingLimit": 50, "autoCreateSqlTable": true, "restrictedToMinimumLevel": "Error", "columnOptionsSection": { "removeStandardColumns": [ "MessageTemplate" ], "customColumns": [ { "ColumnName": "ClientIp", "DataType": "VARCHAR", "AllowNull": true, "DataLength": 50 }, { "ColumnName": "ClientAgent", "DataType": "VARCHAR", "AllowNull": true, "DataLength": 500 } ] } } } ] }
配置说明:用
restrictedToMinimumLevel: "Error"即可实现只写入Error、Fatal级别日志到数据库,比原来的Filter配置更简洁,也不会丢失Enrich生成的属性。原配置的自定义列只加了ClientIp,如果需要把UA也存到数据库,记得补上ClientAgent的列定义。
3. 反向代理场景额外配置
如果服务部署在Nginx、IIS等反向代理之后,默认拿到的会是代理服务器的IP,需要在Startup.cs的Configure方法最顶部添加转发头中间件,才能获取真实客户端IP:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 放在所有中间件的最前面 app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); // 保留原有其他中间件配置,比如UseRouting、UseAuthorization、UseEndpoints等 }
验证方法
修改完成后重启应用,发起任意接口请求,请求生命周期内产生的日志会正常打印ClientIp和ClientAgent值,Error级别的日志也会自动将ClientIp写入ErrorLogs表对应字段。启动阶段无请求上下文的日志ClientIp为空属于正常情况,无需额外处理。
内容的提问来源于stack exchange,提问作者DevSa
相关产品推荐
相关产品推荐

