Serilog 日志记录适用于 ASP.NET Core。此包将 ASP.NET Core 的日志消息通过 Serilog 进行路由,使你可以将有关 ASP.NET 内部操作的信息写入与应用程序事件相同的 Serilog 接收器中。
安装并配置了 Serilog.AspNetCore 后,你可以直接通过 Serilog 或ASP.NET 注入的任何 ILogger 接口写入日志消息。所有日志记录器将使用相同的底层实现、级别和目的地。
版本控制:该包跟踪其 Microsoft.Extensions.Hosting 依赖项的版本控制和目标框架支持。大多数用户应该选择与其应用程序目标框架匹配的 Serilog.AspNetCore 版本。例如,如果你的目标是 .NET 7.x,则选择一个 7.x 版本的 Serilog.AspNetCore。如果你的目标是 .NET 8.x,则选择一个 8.x 版本的 Serilog.AspNetCore,依此类推。
使用说明
首先,将 Serilog.AspNetCore NuGet 包安装到你的应用程序中:
dotnet add package Serilog.AspNetCore
接下来,在你的应用程序的 Program.cs 文件中,首先配置 Serilog。使用 try/catch 块可以确保任何配置问题都能得到适当记录:
using Serilog;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
try
{
Log.Information("Starting web application");
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSerilog(); // <-- Add this line
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
调用将会通过你的 Serilog 管道重定向所有日志事件。
最后,通过移除默认记录器的剩余配置进行清理,包括appsettings.*.json 文件中的 “Logging” 部分(如果需要,可以用 Serilog 配置替代,如示例项目所示)。
就这样完成了!通过稍微提高日志级别,你将看到类似于以下内容的日志输出:
[12:01:43 INF] Starting web application
[12:01:44 INF] Now listening on: http://localhost:5000
[12:01:44 INF] Application started. Press Ctrl+C to shut down.
[12:01:44 INF] Hosting environment: Development
[12:01:44 INF] Content root path: serilog-aspnetcore/samples/Sample
[12:01:47 WRN] Failed to determine the https port for redirect.
[12:01:47 INF] Hello, world!
[12:01:47 INF] HTTP GET / responded 200 in 95.0581 ms
提示:要在使用 IIS 运行时在 Visual Studio 输出窗口中查看 Serilog 输出,可以选择 "ASP.NET Core Web Server" 从下拉列表中显示输出,或者在日志配置中将WriteTo.Console()替换为WriteTo.Debug()。
有关更完整的示例,包括 appsettings.json 配置,可以在这里找到示例项目。
请求日志记录
该包包括用于更智能的 HTTP 请求日志记录的中间件。ASP.NET Core 实现的默认请求日志记录非常繁琐,每个请求会发出多个事件。该中间件将这些事件合并为一个单一事件,该事件携带方法、路径、状态码和时间信息。
文本格式如下:
[16:05:54 INF] HTTP GET / responded 200 in 227.3253 ms
或作为 JSON:
{
"@t": "2019-06-26T06:05:54.6881162Z",
"@mt": "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms",
"@r": ["224.5185"],
"RequestMethod": "GET",
"RequestPath": "/",
"StatusCode": 200,
"Elapsed": 224.5185,
"RequestId": "0HLNPVG1HI42T:00000001",
"CorrelationId": null,
"ConnectionId": "0HLNPVG1HI42T"
}
要启用中间件,首先在日志配置或 appsettings.json 文件中将 ASP.NET Core 日志源的最低级别更改为 Warning:
.MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning)
提示:在控制台日志记录器的输出模板中添加 {SourceContext},以查看日志记录器的名称;这可以帮助追踪噪声日志事件的来源并进行抑制。
然后,在应用程序的 Program.cs 中,使用 UseSerilogRequestLogging() 添加中间件:
var app = builder.Build();
app.UseSerilogRequestLogging(); // <-- Add this line
// Other app configuration
重要的是 UseSerilogRequestLogging() 调用应出现在处理程序(如 MVC)之前。中间件不会对其在管道中出现之前的组件进行计时或日志记录。(可以利用这一点,通过将 UseSerilogRequestLogging() 放在它们之后来排除噪声处理程序的日志记录,例如 UseStaticFiles()。)
在请求处理期间,可以使用 IDiagnosticContext.Set() 将附加属性附加到完成事件中:
public class HomeController : Controller
{
readonly IDiagnosticContext _diagnosticContext;
public HomeController(IDiagnosticContext diagnosticContext)
{
_diagnosticContext = diagnosticContext ?? throw new ArgumentNullException(nameof(diagnosticContext));
}
public IActionResult Index()
{
// The request completion event will carry this property
_diagnosticContext.Set("CatalogLoadTime", 1423);
return View();
}
}
这种模式的优点在于减少了每个 HTTP 请求需要构建、传输和存储的日志事件数量。在同一事件上有许多属性也可以使请求细节和其他数据的关联更容易。
默认情况下,将作为属性添加以下请求信息:
• RequestMethod(请求方法)
• RequestPath(请求路径)
• StatusCode(状态码)
• Elapsed(持续时间)
你可以通过在 UseSerilogRequestLogging() 的选项回调中修改请求完成事件的消息模板、添加额外属性或更改事件级别。
app.UseSerilogRequestLogging(options =>
{
// Customize the message template
options.MessageTemplate = "Handled {RequestPath}";
// Emit debug-level events instead of the defaults
options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Debug;
// Attach additional properties to the request completion event
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
};
});
两阶段初始化
本页面顶部的示例展示了如何在应用程序启动时立即配置 Serilog。这种方式的好处是可以捕获和报告在设置 ASP.NET Core 主机期间抛出的异常。
然而,首先初始化 Serilog 的缺点是 ASP.NET Core 主机的服务,包括 appsettings.json 配置和依赖注入,还不可用。
为了解决这个问题,Serilog 支持两阶段初始化。程序启动时,首先配置一个初始的“引导”日志记录器,然后在主机加载完成后,用完全配置的日志记录器替换它。
要使用这种技术,首先将初始的 CreateLogger() 调用替换为 CreateBootstrapLogger():
using Serilog;
using Serilog.Events;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateBootstrapLogger(); // <-- Change this line!
然后,向 AddSerilog() 传递一个回调函数,该回调用于创建最终的日志记录器:
builder.Services.AddSerilog((services, lc) => lc
.ReadFrom.Configuration(builder.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.WriteTo.Console());
需要注意的是,最终的日志记录器会完全替换引导日志记录器:例如,如果你希望两者都记录到控制台,则需要在两个地方都指定 WriteTo.Console(),如示例所示。
使用 appsettings.json 配置
使用两阶段初始化时,插入上例中显示的 ReadFrom.Configuration(builder.Configuration) 调用。JSON 配置语法的文档可以在 Serilog.Settings.Configuration 的 README 中找到。
将服务注入到增强器和接收器
使用两阶段初始化时,插入上例中显示的 ReadFrom.Services(services) 调用。ReadFrom.Services() 调用会用以下服务的任何注册实现来配置日志记录管道:
• IDestructuringPolicy
• ILogEventEnricher
• ILogEventFilter
• ILogEventSink
• LoggingLevelSwitch
JSON 输出
Console()、Debug() 和 File() 接收器都原生支持 JSON 格式的输出,通过包含的 Serilog.Formatting.Compact 包来实现。
要写入以换行符分隔的 JSON,请将 CompactJsonFormatter 或 RenderedCompactJsonFormatter 传递给接收器配置方法:
.WriteTo.Console(new RenderedCompactJsonFormatter())
写入 Azure 诊断日志流
Azure 诊断日志流会将来自 D:\home\LogFiles\ 文件夹中的事件传送到 Azure。为了你的应用启用此功能,请向 LoggerConfiguration 添加一个文件 接收器,并注意设置 shared 和 flushToDiskInterval 参数:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
// Add this line:
.WriteTo.File(
System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"), "LogFiles", "Application", "diagnostics.txt"),
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: 10 * 1024 * 1024,
retainedFileCountLimit: 2,
rollOnFileSizeLimit: true,
shared: true,
flushToDiskInterval: TimeSpan.FromSeconds(1))
.CreateLogger();
将属性推送到 ILogger<T>
如果你希望在代码的特定部分向所有日志事件添加额外的属性,可以使用以下代码将它们添加到 Microsoft.Extensions.Logging 的 ILogger<T> 中。为了使这段代码生效,请确保在 .UseSerilog(...) 语句中添加了 .Enrich.FromLogContext(),如上例所示。
// Microsoft.Extensions.Logging ILogger<T>
// Yes, it's required to use a dictionary. See https://nblumhardt.com/2016/11/ilogger-beginscope/
using (logger.BeginScope(new Dictionary<string, object>
{
["UserId"] = "svrooij",
["OperationType"] = "update",
}))
{
// UserId and OperationType are set for all logging events in these brackets
}
上面的代码产生的结果与在 Serilog 的 LogContext 中推送属性是相同的。更多详细信息可以在 Serilog 官方文档 中找到。
// Serilog LogContext
using (LogContext.PushProperty("UserId", "svrooij"))
using (LogContext.PushProperty("OperationType", "update"))
{
// UserId and OperationType are set for all logging events in these brackets
}