.Net Core微服务 - Serilog+Seq记录日志
Serilog介绍
Serilog 是用于 .NET 应用程序的诊断日志记录库。它易于设置,具有干净的 API,并且可以在所有最新的 .NET 平台上运行。Serilog 通过NuGet分发,项目包括一个Seirlog核心项目Seirlog和很多接收器sinks(超过100个),这些接收是通过插件的方式来实现将日志写入到各种终端、文件、邮件、数据库或日志服务器。
在ASP.NET CORE 中使用Serilog记录日志
在asp.net core 中使用Serilog记录日志、按日志级别异步记录写入日志文件、写入控制台打印、异步写入Seq
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.2.0" />
<PackageReference Include="Serilog.Expressions" Version="3.4.1" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2" />
</ItemGroup>
Program.cs
public class Program
{
public static void Main(string[] args)
{
try
{
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "*** Program Stop ***");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
// 使用Serilog记录日志
services.AddConfigSerilog(configuration: Configuration);
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 使用Serilog记录请求日志
app.UseSerilogRequestLogging();
app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
ApplicationBuilderExtensions.cs
/// <summary>
/// ApplicationBuilder 扩展
/// </summary>
public static class ApplicationBuilderExtensions
{
/// <summary>
/// 添加使用Serilog请求日志
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
public static IApplicationBuilder UseSerilogRequestLogging(this IApplicationBuilder app)
{
//允许body重用
app.Use(next => context =>
{
context.Request.EnableBuffering();
return next(context);
});
// 添加使用Serilog记录请求日志
app.UseSerilogRequestLogging(options =>
{
// 请求日志输出模板
options.MessageTemplate = "\n[R] {RequestMethod}={_RequestPath} | Status={StatusCode} | Time={Elapsed}ms\n[R] Req={_RequestBody} | Res={_ResponseBody}";
// 发出调试级别事件而不是默认事件,将请求日志记录到:Debug日志
options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Debug;
// 将其他属性附加到请求完成事件,将请求属性附加可以在模板中使用
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("_RequestPath", WebUtility.UrlDecode(httpContext.Request.Path + httpContext.Request.QueryString));
//请求body
var requestContent = "";
var method = httpContext.Request.Method.ToLower();
if (method == "post" || method == "put")
{
httpContext.Request.Body.Position = 0;
var requestReader = new StreamReader(httpContext.Request.Body);
requestContent = requestReader.ReadToEnd();
}
diagnosticContext.Set("_RequestBody", requestContent);
diagnosticContext.Set("_Service",AppDomain.CurrentDomain.FriendlyName);
};
});
return app;
}
}
ServiceCollectionExtensions.cs
/// <summary>
/// ServiceCollection 扩展
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// 添加配置Serilog
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public static IServiceCollection AddConfigSerilog(this IServiceCollection services, IConfiguration configuration)
{
var seqApiKey = configuration["seq:apiKey"].ToString();
var seqServerUrl = configuration["seq:serverUrl"].ToString();
// 创建Serilog记录日志
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.MinimumLevel.Override("System", LogEventLevel.Debug)
.MinimumLevel.Override("Microsoft", LogEventLevel.Debug)
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Error)
.MinimumLevel.Override("Microsoft.AspNetCore.Cors.Infrastructure.CorsService", LogEventLevel.Error)
.MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Error)
.MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Error)
// 全部日志写入到Console
.WriteTo.Async(c => c.Console(
theme: AnsiConsoleTheme.Literate,
outputTemplate: "[T] {Timestamp:yyyy-MM-dd HH:mm:ss,fff} {MachineName}{NewLine}[L] [{Level}]{NewLine}[C] [{SourceContext}] {Message:lj}{NewLine}{Exception}{NewLine}"))
// Information日志写入到文件
.WriteTo.Async(c => c.File(
path: "Logs/Information/.txt",
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: 1L * 1024 * 1024 * 1024,
retainedFileCountLimit: 31,
outputTemplate: "[T] {Timestamp:yyyy-MM-dd HH:mm:ss,fff} {MachineName}{NewLine}[L] [{Level}]{NewLine}[C] [{SourceContext}] {Message:lj}{NewLine}{Exception}{NewLine}",
restrictedToMinimumLevel: LogEventLevel.Information))
// Debug日志写入到文件
.WriteTo.Async(c => c.File(
path: "Logs/Debug/.txt",
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: 1L * 1024 * 1024 * 1024,
retainedFileCountLimit: 31,
outputTemplate: "[T] {Timestamp:yyyy-MM-dd HH:mm:ss,fff} {MachineName}{NewLine}[L] [{Level}]{NewLine}[C] [{SourceContext}] {Message:lj}{NewLine}{Exception}{NewLine}",
restrictedToMinimumLevel: LogEventLevel.Debug))
// Warning日志写入到文件
.WriteTo.Async(c => c.File(
path: "Logs/Warning/.txt",
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: 1L * 1024 * 1024 * 1024,
retainedFileCountLimit: 31,
outputTemplate: "[T] {Timestamp:yyyy-MM-dd HH:mm:ss,fff} {MachineName}{NewLine}[L] [{Level}]{NewLine}[C] [{SourceContext}] {Message:lj}{NewLine}{Exception}{NewLine}",
restrictedToMinimumLevel: LogEventLevel.Warning))
// Error日志写入到文件
.WriteTo.Async(c => c.File(
path: "Logs/Error/.txt",
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: 1L * 1024 * 1024 * 1024,
retainedFileCountLimit: 31,
outputTemplate: "[T] {Timestamp:yyyy-MM-dd HH:mm:ss,fff} {MachineName}{NewLine}[L] [{Level}]{NewLine}[C] [{SourceContext}] {Message:lj}{NewLine}{Exception}{NewLine}",
restrictedToMinimumLevel: LogEventLevel.Error))
// 全部日志写入到Seq
.WriteTo.Async(c => c.Seq(
apiKey: seqApiKey,
serverUrl: seqServerUrl))
.Enrich.FromLogContext()
.Enrich.WithMachineName()
// 过滤请求:/health心跳
.Filter.ByExcluding("RequestPath like '/health%'")
.CreateBootstrapLogger();
services.AddSerilog();
return services;
}
}
RequestAuditLogFilter.cs
public class RequestAuditLogFilter : IResultFilter
{
private readonly IDiagnosticContext _diagnosticContext;
public RequestAuditLogFilter(IDiagnosticContext diagnosticContext) { _diagnosticContext = diagnosticContext; }
public void OnResultExecuted(ResultExecutedContext context)
{
var result = context.Result as ObjectResult;
var resultJson = JsonConvert.SerializeObject(result?.Value);
_diagnosticContext.Set("_ResponseBody", resultJson);
}
public void OnResultExecuting(ResultExecutingContext context) { }
}
RequestAuditLogAttribute.cs
public class RequestAuditLogAttribute : TypeFilterAttribute
{
public RequestAuditLogAttribute() : base(typeof(RequestAuditLogFilter)) { }
}
HomeController.cs
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly ILogger<HomeController> _logger;
private readonly IDiagnosticContext _diagnosticContext;
public HomeController(ILogger<HomeController> logger, IDiagnosticContext diagnosticContext)
{
_logger = logger;
_diagnosticContext= diagnosticContext;
}
[HttpGet("LogInformation")]
[RequestAuditLog]
public string LogInformation()
{
return "ok";
}
[HttpGet("LogError")]
[RequestAuditLog]
public string LogError()
{
int a = 10;
int b = 0;
int c = a / b;
return "ok";
}
[HttpPost("LogRequest")]
[RequestAuditLog]
public List<string> LogRequest([FromBody] List<string> input)
{
for (int i = 0; i < 10; i++)
{
input.Add(Guid.NewGuid().ToString());
}
return input;
}
}
博客内容仅代表个人观点,如发现阐述有误,麻烦指正,谢谢!