一、环境概述
-
SDK:.NET Core 8.0
-
IDE:Visual Studio 2022
-
项目骨架:标准ASP.NET Core Web API骨架
-
日志实现:Serilog
二、项目依赖
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.8.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
<PackageReference Include="Serilog.Expressions" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.0" />
</ItemGroup>
</Project>
三、项目结构

四、Serilog配置文件
这里没有把日志放在appsetting.json中统一配置,而是拆分为了serilog.json(用于不同环境通用配置)和serilog.Development.json(开发环境专属配置),以满足不同环境对日志的不同需要,比如开发需要DEBUG级别日志,但是生产可能只需要INFO级别就可以了,以下是示例配置,具体请参考Serilog官方文档:Serilog — simple .NET logging with fully-structured events。
serilog.json
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{ "Name": "Console" },
{
"Name": "File",
"Args": {
"path": "logs/app-.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName" ]
}
}
serilog.Development.json
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Debug",
"Swashbuckle.AspNetCore": "Debug"
}
}
}
}
五、Serilog配置
IHostBuilderExtension.cs
using Serilog;
namespace Online.Admin.Serilog.Extensions.Serilog
{
public static class IHostBuilderExtension
{
public static WebApplicationBuilder AddAppSerilog(this WebApplicationBuilder builder)
{
builder.Configuration
.AddJsonFile("serilog.json", optional: false, reloadOnChange: true)
.AddJsonFile($"serilog.{builder.Environment.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables()
.Build();
builder.Host.UseSerilog((hostingContext, services, loggerConfiguration) =>
{
loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration)
.ReadFrom.Services(services);
});
return builder;
}
}
}
六、请求日志中间件
ASP.NET Core Web API其实内置了一个日志中间件,但是那个不满足需求,这里就自定义了一个,这里只是做一下示例,具体要看业务需求需要记录什么。
RequestLoggingMiddleware.cs
using Serilog.Context;
namespace Online.Admin.Serilog.MiddleWare
{
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggingMiddleware> _logger;
public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var startTime = DateTime.UtcNow;
var requestId = Guid.NewGuid().ToString("N")[..8];
using (LogContext.PushProperty("RequestId", requestId))
using (LogContext.PushProperty("HttpMethod", context.Request.Method))
using (LogContext.PushProperty("Url", context.Request.Path))
using (LogContext.PushProperty("ClientIp", context.Connection.RemoteIpAddress?.ToString()))
{
try
{
await _next(context);
}
finally
{
var durationMs = (DateTime.UtcNow - startTime).TotalMilliseconds;
_logger.LogInformation(
"HTTP {HttpMethod} {Url} responded {StatusCode} in {DurationMs} ms",
context.Request.Method,
context.Request.Path,
context.Response.StatusCode,
durationMs);
}
}
}
}
}
七、启动日志
Program.cs
using Online.Admin.Serilog.Extensions.Serilog;
using Online.Admin.Serilog.MiddleWare;
using Serilog;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("logs/bootstrap-.log", rollingInterval: RollingInterval.Day)
.CreateLogger();
try
{
var builder = WebApplication.CreateBuilder(args);
builder.AddAppSerilog();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseMiddleware<RequestLoggingMiddleware>();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Logger.LogInformation("{AppName} 启动成功,环境: {Env}", typeof(Program).Assembly.GetName().Name, app.Environment.EnvironmentName);
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "应用启动失败");
}
finally
{
Log.CloseAndFlush();
}
八、运行效果

