加入 Request logging 要求記錄
當你將 Microsoft.AspNetCore
記錄類別 (Log Category) 設定為 Warning
之後,預設 Serilog 不會將所有 Request 詳細資訊記錄下來。但是若你開到 Information
層級的話,每一個 Request 又會包含好幾條 Log 訊息,雖然資訊十分豐富,但是卻會讓 Log 更加顯的雜亂無章,不易閱讀。
此時你可以使用 app.UseSerilogRequestLogging()
這個 Middleware 來整理所有與 Request 相關的紀錄,讓你在一條 Log 中就可以取得目前 Request 所有的相關資訊。
注意:這個 Request logging 的紀錄等級為 Information
喔!
由於 Log 的欄位很多,使用 Console Sink 會比較看不出來,改用 Serilog.Formatting.Compact 來記錄 JSON 格式的 Log 訊息會清楚很多!
以下是 WriteTo
的設定內容:
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore": "Warning"
}
},
"Enrich": [ "FromLogContext" ],
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "./logs/log-.json",
"rollingInterval": "Day",
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
}
}
]
},
"AllowedHosts": "*"
}
以下是我發出 GET /WeatherForecast
API 呼叫的 Log 內容,雖然會失去 ControllerContext 相關資訊,但你可以預設取得 RequestMethod
, RequestPath
, StatusCode
, Elapsed
這四個屬性 (Properties):
{
"@t": "2021-11-29T15:42:40.1238894Z",
"@mt": "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms",
"@r": ["0.1917"],
"RequestMethod": "GET",
"RequestPath": "/WeatherForecast",
"StatusCode": 200,
"Elapsed": 0.1917,
"SourceContext": "Serilog.AspNetCore.RequestLoggingMiddleware",
"RequestId": "0HMDJ9RFLP5N0:0000000D",
"ConnectionId": "0HMDJ9RFLP5N0"
}
如果這些擴充的屬性不夠用,還是可以新增自訂的屬性到 Log 之中,你只要注入 IDiagnosticContext
物件,就可以用 Set()
方法來寫入額外的屬性。例如你想要紀錄登入的使用者,就可以用以下程式碼增加 UserID
屬性到 Structured Log 之中:
_diagnosticContext.Set("UserID", User.Identity?.Name);
以下是完整範例:
using Microsoft.AspNetCore.Mvc;
using Serilog;
namespace serilogdemo2.Controllers;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
private readonly IDiagnosticContext _diagnosticContext;
public WeatherForecastController(ILogger<WeatherForecastController> logger, IDiagnosticContext diagnosticContext)
{
_logger = logger;
_diagnosticContext = diagnosticContext;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
_diagnosticContext.Set("UserID", User.Identity?.Name);
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
如果你想一勞永逸的將每個 Request 都預設加上一些屬性,那麼你可以這樣註冊 Middleware:
app.UseSerilogRequestLogging(options =>
{
// 如果要自訂訊息的範本格式,可以修改這裡,但修改後並不會影響結構化記錄的屬性
options.MessageTemplate = "Handled {RequestPath}";
// 預設輸出的紀錄等級為 Information,你可以在此修改記錄等級
// options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Debug;
// 你可以從 httpContext 取得 HttpContext 下所有可以取得的資訊!
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
diagnosticContext.Set("UserID", httpContext.User.Identity?.Name);
};
});