如何覆盖Microsoft.AspNetCore.Hosting.Diagnostics日志消息的格式或内容?
嘿,这个问题我之前踩过坑!默认的Microsoft.AspNetCore.Hosting.Diagnostics那些请求日志确实有点“固执”,要么不带上我们自定义的Correlation ID,要么格式就是改不动。不过有几个实用的办法能解决,我给你捋捋:
先检查中间件顺序,别让Correlation ID迟到
你有没有注意到,"Request Started"日志是请求刚进来就触发的?如果你的Correlation ID中间件注册得太晚——比如放在UseRouting()或者UseAuthorization()之后——那生成这条日志的时候,你的Correlation ID还没存入LogContext呢!
赶紧把你的Correlation ID中间件往前提,最好放在app.UseHttpsRedirection()甚至最开头,这样请求一进来,Correlation ID就准备好了,默认日志自然就能带上它。用Serilog重写日志事件,强行给默认日志“加属性”
如果只是想给默认日志补上Correlation ID或者调整格式,用Serilog的Rewrite功能就很方便。你可以过滤出Microsoft.AspNetCore.Hosting.Diagnostics的日志,然后给它们添加自定义属性,或者修改消息模板。
举个实际代码例子:Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() // 过滤出目标日志,添加Correlation ID属性 .Rewrite.ByIncluding(evt => evt.SourceContext == "Microsoft.AspNetCore.Hosting.Diagnostics", evt => { if (LogContext.Keys.Contains("CorrelationId")) { var correlationId = LogContext.GetPropertyValue("CorrelationId"); evt.AddOrUpdateProperty(new LogEventProperty("CorrelationId", new ScalarValue(correlationId))); } }) // 自定义输出模板,把Correlation ID显示出来 .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} | CorrelationId: {CorrelationId}{NewLine}{Exception}") .CreateLogger();直接禁用默认日志,自己写才最自由
要是你完全看默认日志不顺眼,干脆禁用它,自己在Correlation ID中间件里写请求日志,格式、内容全由你说了算。
第一步先关掉默认日志:// 在Program.cs里配置日志过滤 builder.Logging.AddFilter("Microsoft.AspNetCore.Hosting.Diagnostics", LogLevel.None);然后在你的Correlation ID中间件里手动输出日志:
public async Task InvokeAsync(HttpContext context) { // 生成或获取Correlation ID var correlationId = context.Request.Headers["X-Correlation-Id"].FirstOrDefault() ?? Guid.NewGuid().ToString(); // 存入LogContext和HttpContext using (LogContext.PushProperty("CorrelationId", correlationId)) { context.Items["CorrelationId"] = correlationId; // 自己写Request Started日志 Log.Information("Request Started | {Method} {Path} | CorrelationId: {CorrelationId}", context.Request.Method, context.Request.Path, correlationId); try { await _next(context); } finally { // 自己写Request Finished日志 Log.Information("Request Finished | {Method} {Path} | StatusCode: {StatusCode} | CorrelationId: {CorrelationId}", context.Request.Method, context.Request.Path, context.Response.StatusCode, correlationId); } } }这样出来的日志完全是你想要的样子,再也不用受默认格式的限制。
订阅Diagnostic事件,从源头接管日志
其实这些默认日志是通过DiagnosticSource发布的,我们可以直接订阅对应的事件,自己处理日志输出。
先写一个自定义的订阅者:public class CustomHostingDiagnosticsObserver : IObserver<KeyValuePair<string, object?>> { public void OnCompleted() { } public void OnError(Exception error) { } public void OnNext(KeyValuePair<string, object?> value) { // 捕获请求开始事件 if (value.Key == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start") { if (value.Value is { } payload && payload.GetType().GetProperty("HttpContext")?.GetValue(payload) is HttpContext context) { var correlationId = context.Items["CorrelationId"]?.ToString(); Log.Information("Request Started [CorrId: {CorrelationId}]: {Method} {Path}", correlationId, context.Request.Method, context.Request.Path); } } // 捕获请求结束事件 else if (value.Key == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop") { if (value.Value is { } payload && payload.GetType().GetProperty("HttpContext")?.GetValue(payload) is HttpContext context) { var correlationId = context.Items["CorrelationId"]?.ToString(); Log.Information("Request Finished [CorrId: {CorrelationId}]: {Method} {Path} | Status: {StatusCode}", correlationId, context.Request.Method, context.Request.Path, context.Response.StatusCode); } } } }然后在Program.cs里注册这个订阅者:
builder.Services.AddSingleton<IObserver<KeyValuePair<string, object?>>, CustomHostingDiagnosticsObserver>(); builder.Services.AddSingleton<DiagnosticListener>(sp => { var listener = new DiagnosticListener("Microsoft.AspNetCore"); var observer = sp.GetRequiredService<IObserver<KeyValuePair<string, object?>>>(); // 只订阅我们关心的请求事件 listener.Subscribe(observer, (name, _) => name.StartsWith("Microsoft.AspNetCore.Hosting.HttpRequestIn")); return listener; });这样就能从源头接管请求日志的生成,完全按照你的需求输出。
内容来源于stack exchange

