Serilog 记录日志
从 NuGet 安装 Serilog
核心的包是 Serilog 和 Serilog.AspNetCore
建议安装 Serilog.AspNetCore,几乎包含了Serilog常用的所有包
异步写入 Serilog.Sinks.Async
写入MSSQL Serilog.Sinks.MSSqlServer
Install-Package Serilog.AspNetCore
Install-Package Serilog
Install-Package Serilog.Sinks.Async
Install-Package Serilog.Sinks.MSSqlServer
日志输出
输出到控制台
using Serilog; using Serilog.Events; Log.Logger = new LoggerConfiguration() .WriteTo.Console() //输出到控制台 .CreateLogger(); Log.Logger = new LoggerConfiguration() #if DEBUG .MinimumLevel.Debug() #else .MinimumLevel.Information() #endif .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .MinimumLevel.Override("IdentityServer4", LogEventLevel.Information) .MinimumLevel.Override("Hangfire", LogEventLevel.Information) .WriteTo.Console() .CreateLogger(); Log.Information("log");
输出本地日志文件
WriteTo.File详解(日志默认路径为当前程序路径)
- path:默认路径是程序的bin目录+path参数,当然也可以写绝对路径,只需要写入参数就可以了
- rollingInterval:创建文件的类别,可以是分钟,小时,天,月。 此参数可以让创建的log文件名 + 时间。例如log20191219.log
- outputTemplate:日志模板,可以自定义
- retainedFileTimeLimit:日志保存时间,删除过期日志
- retainedFileCountLimit:设置日志文件个数最大值,默认31,意思就是只保留最近的31个日志文件,等于null时永远保留文件
- rollOnFileSizeLimit: 是否限制单个文件的最大大小
- fileSizeLimitBytes: 单个文件最大长度
using Serilog; /* WriteTo.File详解 path:默认路径是程序的bin目录+path参数,当然也可以写绝对路径,只需要写入参数就可以了 rollingInterval:创建文件的类别,可以是分钟,小时,天,月。 此参数可以让创建的log文件名 + 时间。例如log20191219.log outputTemplate:日志模板,可以自定义 retainedFileTimeLimit:日志保存时间,删除过期日志 retainedFileCountLimit:设置日志文件个数最大值,默认31,意思就是只保留最近的31个日志文件,等于null时永远保留文件 rollOnFileSizeLimit: 是否限制单个文件的最大大小 fileSizeLimitBytes: 单个文件最大长度 */ string SerilogOutputTemplate = "{NewLine}{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100); Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() .WriteTo.File("00_Logs//log.log", rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate, retainedFileCountLimit: 31, retainedFileTimeLimit: TimeSpan.FromDays(2), rollOnFileSizeLimit: true, fileSizeLimitBytes: 52428800 // 50MB ) .CreateLogger(); //简洁版本 Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() .WriteTo.File("00_Logs//log.log", rollingInterval: RollingInterval.Day) .CreateLogger(); Log.Information("log");
异步输出本地日志文件
需要Serilog.Sinks.Async包,github详解路径:https://github.com/serilog/serilog-sinks-async
1 using Serilog;
2
3 namespace NetCoreConsoleApp
4 {
5 class Program
6 {
7 static void Main(string[] args)
8 {
9 //github详解路径:https://github.com/serilog/serilog-sinks-async
10
11 Log.Logger = new LoggerConfiguration()
12 .WriteTo.Async(a => a.File("00_Logs\\log.log", rollingInterval: RollingInterval.Day))
13 .CreateLogger();
14 Log.Information("log");
15 Log.CloseAndFlush();
16 }
17 }
18 }
日志添加自定义字段
自定义字段需要通过自定义模板配置输出,如果是数据库会添加字段
Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() .Enrich.WithProperty("SystemName", string.Empty) .Enrich.WithProperty("ServiceName", string.Empty) .Enrich.WithProperty("IP", "8.8.8.8") .Enrich.WithProperty("IP2", "9.9.9.9") .Enrich.With(serviceProvider.GetService<RequestEnricher>()) .WriteTo.Console();
RequestEnricher 代码
using Microsoft.AspNetCore.Http.Extensions; using Serilog.Core; using Serilog.Events; namespace WebApplication1 { public class RequestEnricher : ILogEventEnricher { private readonly IHttpContextAccessor _httpContextAccessor; public RequestEnricher(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("IpAddress", "127.0.0.1")); } } }
需要注入 RequestEnricher
builder.Services.AddSingleton<RequestEnricher>();
自定义模板输出日志
输出自定义日志(SystemName和IpAddress)
string SerilogOutputTemplate = "{NewLine}{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}SystemName:{SystemName}IpAddress:{IpAddress}{NewLine}{Exception}" + new string('-', 100); Log.Logger = new LoggerConfiguration() .Enrich.WithProperty("SystemName", 123546) .MinimumLevel.Debug() .WriteTo.Console() .WriteTo.File("00_Logs//log.log", outputTemplate: SerilogOutputTemplate, rollingInterval: RollingInterval.Day) .CreateLogger(); Log.Information("log");
或者JSON模板输出。需要Nuget安装 Serilog.Expressions 包
using Microsoft.Extensions.DependencyInjection; using Serilog; using Serilog.Templates; Log.Logger = new LoggerConfiguration() .Enrich.WithProperty("SystemName", 123546) .MinimumLevel.Debug() .WriteTo.Console() .WriteTo.File( formatter: new ExpressionTemplate("{ {@timestamp: @t, systemName: SystemName, serviceName: ServiceName, logType: LogType, traceId: TraceId, connectionId: ConnectionId, requestId: RequestId, spanId: SpanId, parentId: ParentId, logTime: @t, sourceContext: SourceContext, ipAddress: IpAddress, userId: UserId, userName: UserName, requestScheme: RequestScheme, requestPath: RequestPath, requestUrl: RequestUrl, requestMethod: RequestMethod, statusCode: StatusCode, elapsed: Elapsed, requestBody: RequestBody, responseBody: ResponseBody} }\n") , "00_Logs//log.log", rollingInterval: RollingInterval.Day) .CreateLogger(); Log.Information("log");
日志记录Http请求日志
自定义字段需要通过自定义模板配置输出
注入
builder.Services.AddSingleton<RequestEnricher>();
builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor();
获取请求信息,然后 Enrich.With 添加
using Microsoft.AspNetCore.Http.Extensions; using Serilog.Core; using Serilog.Events; using System.Security.Claims; namespace WebApplication1 { public class RequestEnricher : ILogEventEnricher { private readonly IHttpContextAccessor _httpContextAccessor; public RequestEnricher(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { var context = _httpContextAccessor.HttpContext; if (context == null || context.Connection == null || context.Request == null) { return; } var ipAddress = context.Connection.RemoteIpAddress?.ToString(); if (!string.IsNullOrEmpty(ipAddress)) { logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("IpAddress", ipAddress)); } var requestHost = context.Request.Host.Value; if (!string.IsNullOrEmpty(requestHost)) { logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestHost", requestHost)); } var requestScheme = context.Request.Scheme; if (!string.IsNullOrEmpty(requestScheme)) { logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestScheme", requestScheme)); } var requestUrl = context.Request.GetDisplayUrl(); if (!string.IsNullOrEmpty(requestUrl)) { logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestUrl", requestUrl)); } if (context?.User != null) { var userId = context.User.FindFirstValue("sub"); if (!string.IsNullOrEmpty(userId)) { logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("UserId", userId)); } var userName = context.User.Identity?.Name; if (!string.IsNullOrEmpty(userName)) { logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("UserName", userName)); } } } } }
Controller中发起请求
using Microsoft.AspNetCore.Mvc; namespace WebApplication1.Controllers { [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { IHttpClientFactory _httpClientFactory; HttpClient _HttpClient; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger, IHttpClientFactory httpClientFactory, HttpClient httpClient) { _logger = logger; _httpClientFactory = httpClientFactory; _HttpClient = httpClient; } [HttpGet] public string Get() { var client = _httpClientFactory.CreateClient(); var result = client.GetStringAsync("https://www.baidu.com/").Result; var response = _HttpClient.GetAsync("https://www.taobao.com").Result; _logger.LogInformation("我爱中国"); return Guid.NewGuid().ToString(); } } }
日志文件夹根据级别分类
using Serilog;
using Serilog.Events;
using System;
namespace NetCoreConsoleApp
{
class Program
{
static void Main(string[] args)
{
string LogFilePath(string LogEvent) => $@"{AppContext.BaseDirectory}00_Logs\{LogEvent}\log.log";
string SerilogOutputTemplate = "{NewLine}{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 50);
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.MinimumLevel.Debug() // 所有Sink的最小记录级别
.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Debug).WriteTo.File(LogFilePath("Debug"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Information).WriteTo.File(LogFilePath("Information"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Warning).WriteTo.File(LogFilePath("Warning"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Error).WriteTo.File(LogFilePath("Error"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Fatal).WriteTo.File(LogFilePath("Fatal"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
.CreateLogger();
Log.Information("log");
Log.Error("log");
}
}
}
使用 Serilog.Sinks.Map ,需要安装 Install-Package Serilog.Sinks.Map 包
string LogFilePath(string LogEvent) => System.IO.Path.Combine(AppContext.BaseDirectory, "00_Logs", LogEvent, "log.log"); Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() .WriteTo.Console() .MinimumLevel.Debug() // 所有Sink的最小记录级别 .WriteTo.Map(evt => evt.Level, (level, wt) => wt.File(LogFilePath(level.ToString()), rollingInterval: RollingInterval.Day)) .CreateLogger();
日志文件根据内容分类
using Serilog; var requestLogExpression = "StartsWith(SourceContext, 'Serilog.AspNetCore.RequestLoggingMiddleware')"; var systemLogExpression = "StartsWith(SourceContext, 'Microsoft.') " + "or StartsWith(SourceContext, 'IdentityServer4.') " + "or StartsWith(SourceContext, 'Hangfire.') " + "or StartsWith(SourceContext, 'Volo.Abp.')" + "or StartsWith(SourceContext, 'System.Net.')"; //可以根据不同的分类写入不同的日志文件 var logger = new LoggerConfiguration(); logger .Filter.ByIncludingOnly(requestLogExpression) .MinimumLevel.Debug() .WriteTo.File("00_Logs\\log-request.log", rollingInterval: RollingInterval.Day) .WriteTo.Console(); logger .Filter.ByIncludingOnly(systemLogExpression) .MinimumLevel.Debug() .WriteTo.File("00_Logs\\log-system.log", rollingInterval: RollingInterval.Day) .WriteTo.Console(); logger .Filter.ByExcluding($"{requestLogExpression} or {systemLogExpression}") .MinimumLevel.Debug() .WriteTo.File("00_Logs\\log-businesslog.log", rollingInterval: RollingInterval.Day) .WriteTo.Console(); Log.Logger = logger.CreateLogger();
输出到MSSQL
需要Serilog.Sinks.MSSqlServer包,github详解路径:https://github.com/serilog/serilog-sinks-mssqlserver
参考链接:https://www.cnblogs.com/lonelyxmas/p/11980881.html
using Serilog;
using Serilog.Events;
using Serilog.Sinks.MSSqlServer;
using System;
using System.Collections.ObjectModel;
using System.Data;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string connectionString = @"Server=....";string tableNameA = "LogsA";
string tableNameB = "LogsB";
string tableNameC = "LogsC";
static void Write()
{
Log.Information("测试信息");
Log.Error("Error");
Log.Write(LogEventLevel.Error, new Exception("错误"), "致命错误");
};
//A:默认配置
//autoCreateSqlTable为true 时 会自动创建日志表
Log.Logger = new LoggerConfiguration().WriteTo.MSSqlServer(connectionString, tableNameA, autoCreateSqlTable: true).CreateLogger();
Write();
//B:移除列
var options = new ColumnOptions();
options.Store.Remove(StandardColumn.Properties);
options.Store.Remove(StandardColumn.MessageTemplate);
Log.Logger = new LoggerConfiguration()
.WriteTo.MSSqlServer(connectionString, tableNameB, columnOptions: options, autoCreateSqlTable: true)
.CreateLogger();
Write();
//C:添加自定义列
options = new ColumnOptions();
options.AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn { DataType = SqlDbType.NVarChar, DataLength =-1, ColumnName = "IP" },
new SqlColumn { DataType = SqlDbType.NVarChar, DataLength =-1, ColumnName = "IP2" }
};
Log.Logger = new LoggerConfiguration()
.Enrich.WithProperty("IP", "8.8.8.8")
.Enrich.WithProperty("IP2", "9.9.9.9")
.WriteTo.MSSqlServer(connectionString, tableNameC, columnOptions: options, autoCreateSqlTable: true)
.CreateLogger();
Write();
Console.WriteLine("Hello World!");
Console.ReadKey();
}
}
}
输出到MYSQL
Serilog的Mysql插件包有两个: Serilog.Sinks.MySQL 和 Serilog.Sinks.MariaDB 。随便选一个使用即可。
代码案例:
using Serilog; using Serilog.Sinks.MariaDB.Extensions; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); builder.Host.UseSerilog((hostingContext, loggerConfiguration) => { var ConnectionString = "Database=sys;Data Source=127.0.0.1;User Id=root;Password=123456;port=3306;pooling=false;SslMode=None;old Guids=true;Charset=utf8;"; loggerConfiguration .Enrich.FromLogContext() .Enrich.WithProperty("IP", "8.8.8.8") .WriteTo.Console() .MinimumLevel.Debug() //Serilog.Sinks.MySQL .WriteTo.MySQL(ConnectionString, "LogsA") //Serilog.Sinks.MariaDB .WriteTo.MariaDB( connectionString: ConnectionString, tableName: "LogsB", autoCreateTable: true, useBulkInsert: false ); }); var app = builder.Build(); // Configure the HTTP request pipeline. app.UseAuthorization(); app.MapControllers(); app.Run();
输出到PostgreSQL
Serilog的PostgreSQL插件包有两个: Serilog.Sinks.PostgreSQL 和 Serilog.Sinks.Postgresql.Alternative 。随便选一个使用即可。
builder.Host.UseSerilog((hostingContext, loggerConfiguration) => { var ConnectionString = "Host=192.168.1.5;Port=5432;Uid=postgres;Pwd=123456;Database=postgres;"; loggerConfiguration .Enrich.FromLogContext() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .WriteTo.PostgreSQL(ConnectionString, tableName: "serilogs", //columnOptions:null,//使用Serilog.Sinks.Postgresql.Alternative包需要使用此参数,不然会报方法冲突错误 schemaName: "public", needAutoCreateTable: true ); });
过滤Net Core系统日志
using Serilog; using Serilog.Events; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Log.Logger = new LoggerConfiguration() .WriteTo.Console() //Microsoft系统日志最低等级为Warning,过滤掉了所有Information日志 //.MinimumLevel.Override("Microsoft", LogEventLevel.Warning) //记录Net Core系统和EF日志最低级别 .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Information) .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) .CreateLogger(); Log.Information("log"); } } }
ASP.NET Core 中使用 Serilog
参考链接:https://www.cnblogs.com/MaleDeer/p/10797509.html
在Program.cs程序启动时注入Serilog 加载配置
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;
namespace WebApplication1
{
public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File($"{AppContext.BaseDirectory}00_Logs\\log.log", rollingInterval: RollingInterval.Day)
.CreateLogger();
try
{
Log.Information("Starting up");
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application start-up failed");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseSerilog();
}
}
或者
using System;using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;
namespace WebApplication1
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseSerilog((hostingContext, loggerConfiguration) =>
{
loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration)
.Enrich.FromLogContext()
.WriteTo.File($"{AppContext.BaseDirectory}00_Logs\\log.log", rollingInterval: RollingInterval.Day)
.WriteTo.Console();
});
});
}
}
或者在 Program.cs 添加 builder.Host.UseSerilog(LogConfigure.ConfigureSerilog);
using Serilog.Events; using Serilog; namespace WebApplication1 { public class LogConfigure { internal static void ConfigureConsoleLogger(LoggerConfiguration logger) { logger #if DEBUG .MinimumLevel.Debug() #else .MinimumLevel.Information() #endif .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .MinimumLevel.Override("IdentityServer4", LogEventLevel.Information) .MinimumLevel.Override("Hangfire", LogEventLevel.Information) .WriteTo.Async(c => c.Console()); } public static void ConfigureSerilog(HostBuilderContext context, IServiceProvider serviceProvider, LoggerConfiguration logger) { ConfigureConsoleLogger(logger); logger .ReadFrom.Configuration(context.Configuration) .Enrich.FromLogContext() .WriteTo.File(@"00_Logs/log.log", rollingInterval: RollingInterval.Day) .WriteTo.Console(); } } }
使用 Serilog 时,直接使用 ILogger 即可,因为此服务项目应该是默认注入了,此处需要依赖关系注入知识。如你不了解依赖关系注入,请看 微软官方文档。
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace WebApplication1.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly ILogger<ValuesController> _logger;
public ValuesController(ILogger<ValuesController> logger)
{
_logger = logger;
}
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
_logger.LogInformation("test info");
return new string[] { "value1", "value2" };
}
}
}
日志请求中间件
官方中间件
app.UseSerilogRequestLogging();
自定义日志请求中间件
app.UseSerilogRequestLoggingPro();
中间件类
using System; using System.IO; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Serilog; namespace WebApplication1 { class SerilogRequestLoggingMiddleware { private readonly RequestDelegate _next; /// <inheritdoc /> public SerilogRequestLoggingMiddleware(RequestDelegate next) { _next = next; } /// <summary> /// 读取请求内容 /// </summary> private static async Task<string> ReadRequestBody(HttpRequest request) { if (request.Method == HttpMethods.Get) { return $"This is a {request.Method} request"; } if (request.ContentType == null || !request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) { return "Request content is not in json format"; } request.EnableBuffering(); var body = request.Body; var buffer = new byte[Convert.ToInt32(request.ContentLength)]; _ = await request.Body.ReadAsync(buffer, 0, buffer.Length); var bodyAsText = Encoding.UTF8.GetString(buffer); request.Body = body; request.Body.Seek(0, SeekOrigin.Begin); return bodyAsText; } /// <summary> /// 读取输出内容 /// </summary> private static async Task<string> ReadResponseBody(HttpResponse response) { response.Body.Seek(0, SeekOrigin.Begin); if (response.ContentType == null || !response.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) { return "Response content is not in json format"; } var text = await new StreamReader(response.Body).ReadToEndAsync(); response.Body.Seek(0, SeekOrigin.Begin); return text; } public async Task Invoke(HttpContext context, IDiagnosticContext diagnosticContext) { if (context == null) { return; } var requestBody = await ReadRequestBody(context.Request); diagnosticContext.Set("RequestBody", requestBody); var originalBodyStream = context.Response.Body; using var responseStream = new MemoryStream(); context.Response.Body = responseStream; await _next(context); var responseBody = await ReadResponseBody(context.Response); diagnosticContext.Set("ResponseBody", responseBody); await responseStream.CopyToAsync(originalBodyStream); } } public static class SerilogRequestBodyMiddlewareExtensions { public static IApplicationBuilder UseSerilogRequestLoggingPro(this IApplicationBuilder app) { app.UseSerilogRequestLogging(options => { options.MessageTemplate = "HTTP {RequestId} {RequestHost} {IpAddress} {StatusCode} {Elapsed:0} {RequestMethod} {RequestPath} {RequestBody} {ResponseBody}"; }); // must after `app.UseSerilogRequestLogging()` return app.UseMiddleware<SerilogRequestLoggingMiddleware>(); } } }
日志配置汇总
安装以下Nuget包
Serilog.AspNetCore
Serilog.Expressions
Serilog.Sinks.Async
Program.cs 注入
builder.Host.UseSerilog(LogConfigure.ConfigureSerilog); builder.Services.AddSingleton<RequestEnricher>(); builder.Services.AddHttpClient(); builder.Services.AddHttpContextAccessor(); .... app.UseSerilogRequestLogging();
//app.UseSerilogRequestLoggingPro();
LogConfigure 代码, RequestEnricher 参考上面
using Serilog.Events; using Serilog; using Serilog.Templates; namespace WebApplication1 { public static class LogConfigure { internal static void ConfigureConsoleLogger(LoggerConfiguration logger) { logger #if DEBUG .MinimumLevel.Debug() #else .MinimumLevel.Information() #endif .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .MinimumLevel.Override("IdentityServer4", LogEventLevel.Information) .MinimumLevel.Override("Hangfire", LogEventLevel.Information) .WriteTo.Async(c => c.Console()); } internal static void ConfigureSerilog(HostBuilderContext context, IServiceProvider serviceProvider, LoggerConfiguration logger) { ConfigureConsoleLogger(logger); var configuration = context.Configuration; var systemName = "System"; var serviceName = "Service"; logger.Enrich.FromLogContext() .Enrich.WithProperty("SystemName", systemName) .Enrich.WithProperty("ServiceName", serviceName) .Enrich.With(serviceProvider.GetService<RequestEnricher>()) .WriteTo.Console(); var requestLogExpression = "StartsWith(SourceContext, 'Serilog.AspNetCore.RequestLoggingMiddleware')"; var systemLogExpression = "StartsWith(SourceContext, 'Microsoft.') " + "or StartsWith(SourceContext, 'IdentityServer4.') " + "or StartsWith(SourceContext, 'Hangfire.') " + "or StartsWith(SourceContext, 'Volo.Abp.')" + "or StartsWith(SourceContext, 'System.Net.')"; //记录请求日志 logger.WriteTo.Logger(lc => lc .Filter.ByIncludingOnly(requestLogExpression) .Enrich.WithProperty("LogType", "requestlog") .WriteTo.Async(c => c.File( formatter: new ExpressionTemplate("{ {@timestamp: @t, systemName: SystemName, serviceName: ServiceName, logType: LogType, traceId: TraceId, connectionId: ConnectionId, requestId: RequestId, spanId: SpanId, parentId: ParentId, logTime: @t, sourceContext: SourceContext, ipAddress: IpAddress, userId: UserId, userName: UserName, requestScheme: RequestScheme, requestPath: RequestPath, requestUrl: RequestUrl, requestMethod: RequestMethod, statusCode: StatusCode, elapsed: Elapsed, requestBody: RequestBody, responseBody: ResponseBody} }\n"), path: $"logs/{systemName}-{serviceName}-requestlog-.log", rollingInterval: RollingInterval.Day, retainedFileTimeLimit: TimeSpan.FromDays(2), rollOnFileSizeLimit: true, fileSizeLimitBytes: 52428800, // 50MB retainedFileCountLimit: 2))); //记录系统日志 logger.WriteTo.Logger(lc => lc .Filter.ByIncludingOnly(systemLogExpression) .Enrich.WithProperty("LogType", "systemlog") .WriteTo.Async(c => c.File( formatter: new ExpressionTemplate("{ {@timestamp: @t, systemName: SystemName, serviceName: ServiceName, logType: LogType, traceId: TraceId, connectionId: ConnectionId, requestId: RequestId, spanId: SpanId, parentId: ParentId, logTime: @t, ipAddress: IpAddress, userId: UserId, userName: UserName, level: @l, sourceContext: SourceContext, actionId: ActionId, actionName: ActionName, message: @m, exceptions: @x} }\n"), path: $"logs/{systemName}-{serviceName}-systemlog-.log", rollingInterval: RollingInterval.Day, retainedFileTimeLimit: TimeSpan.FromDays(2), rollOnFileSizeLimit: true, fileSizeLimitBytes: 52428800, // 50MB retainedFileCountLimit: 2))); //记录自定义日志 logger.WriteTo.Logger(lc => lc .Filter.ByExcluding($"{requestLogExpression} or {systemLogExpression}") .Enrich.WithProperty("LogType", "businesslog") .WriteTo.Async(c => c.File( formatter: new ExpressionTemplate("{ {@timestamp: @t, systemName: SystemName, serviceName: ServiceName, logType: LogType, traceId: TraceId, connectionId: ConnectionId, requestId: RequestId, spanId: SpanId, parentId: ParentId, logTime: @t, ipAddress: IpAddress, userId: UserId, userName: UserName, level: @l, sourceContext: SourceContext, actionId: ActionId, actionName: ActionName, message: @m, exceptions: @x} }\n"), path: $"logs/{systemName}-{serviceName}-businesslog-.log", rollingInterval: RollingInterval.Day, retainedFileTimeLimit: TimeSpan.FromDays(2), rollOnFileSizeLimit: true, fileSizeLimitBytes: 52428800, // 50MB retainedFileCountLimit: 2))); } } }
日志记录相关堆栈信息
public static void WriteInfo(Type t, string msg) { //参数 var type = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType; var methodBase = new StackTrace().GetFrame(1).GetMethod(); //获取当前方法的命名空间 var MethodNamespace = MethodBase.GetCurrentMethod().DeclaringType.Namespace; //获取当前方法的类名称,包括命名空间 var MethodFullName = MethodBase.GetCurrentMethod().DeclaringType.FullName; //获取当前方法名称 var MethodName = MethodBase.GetCurrentMethod().Name; //获取父方法的命名空间 var BaseMethodNamespace = methodBase.DeclaringType.Namespace; //获取父方法的类名,不包括命名空间 var BaseMethodClsName = methodBase.DeclaringType.Name; //获取父方法名称 var BaseMethodName = methodBase.Name; //获取父方法的类名称,包括命名空间 var BaseMethodFullName = methodBase.DeclaringType.FullName; var message = "方法名称:" + methodBase.DeclaringType.FullName + "\r\n方法名称:" + methodBase.Name + "\r\n日志内容:" + msg; }