ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。
ASP.NET Core 中的日志记录
.NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程序将日志写入不同的目标。 基本日志记录提供程序是内置的,并且还可以使用许多第三方提供程序。
文章目录
- ASP.NET Core 中的日志记录
- 配置依赖于 ILogger 的服务
- 内置日志记录提供程序
- 控制台
- 调试
- 事件来源
- 自定义日志记录提供程序
- 1. 创建自定义记录器
- 2. 自定义记录器提供程序
- 3. 服务实例注册及注入
- 第三方日志记录提供程序
- Log4Net日志
- 注册及配置
- 日志项配置
- Serilog日志
- JSON格式配置
- 编程Code方式配置
- LoggerConfiguration配置结构
- 1. Sinks接收器
- 2. MinimumLevel最小记录级别
- 3. Enrichers丰富器
- 4. Filters过滤器
- 5. WriteTo输出
- 6. Property附加属性
配置依赖于 ILogger 的服务
若要配置依赖于 ILogger的服务,请使用构造函数注入或提供工厂方法。 仅当没有其他选项时,才建议使用工厂方法方法。 例如,假设某个服务需要由 DI 提供的 ILogger 实例:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<IExampleService>(
container => new DefaultExampleService
{
Logger = container.GetRequiredService<ILogger<IExampleService>>()
});
内置日志记录提供程序
Microsoft扩展包括以下日志记录提供程序作为运行时库的一部分:
- 控制台
- 调试
- EventSource
- 事件日志
日志记录提供程序保留日志,Console 提供程序除外,该提供程序仅将日志显示为标准输出。 例如,Azure Application Insights 提供程序将日志存储在 Azure Application Insights 中。 可以启用多个提供程序。
在appsettings.config
配置Logging节点,进行指定类跟踪事件
"Logging": {
// 所有提供者,LogLevel适用于所有启用的提供者
"LogLevel": {
"Default": "Information",
// 常规ASP.NET Core诊断
"Microsoft.AspNetCore": "Warning",
//"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
// EFCore Sql执行跟踪
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
},
// 调试提供者
"Debug": {
"LogLevel": {
"Default": "Information", // Overrides preceding LogLevel:Default setting.
"Microsoft.Hosting": "Trace" // Debug:Microsoft.Hosting category.
}
},
// 事件来源提供者
"EventSource": {
"LogLevel": {
"Default": "Warning" // All categories of EventSource provider.
}
}
}
控制台
Console 提供程序将输出记录到控制台。 如需详细了解如何在开发环境中查看 Console 日志,请参阅记录来自 dotnet run 和 Visual Studio 的输出。
// 从 builder 中删除所有记录器提供程序
builder.Logging.ClearProviders();
// 控制台输出
builder.Logging.AddConsole();
调试
Debug 提供程序使用 System.Diagnostics.Debug 类写入日志输出。 对 System.Diagnostics.Debug.WriteLine 的调用写入到 Debug 提供程序。
// 调试输出
builder.Logging.AddDebug();
事件来源
EventSource 提供程序写入名称为 Microsoft.Extensions.Logging 的跨平台事件源。 在 Windows 上,提供程序使用的是 ETW。
// 事件来源输出
builder.Logging.AddEventSourceLogger();
日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性。我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger、LoggerFactory和LoggerProvider这三个核心对象组成。我们可以通过简单的配置实现对LoggerFactory的定制,以及对LoggerProvider添加。
自定义日志记录提供程序
有许多 日志记录提供程序 可用于常见日志记录需求。 当某个可用提供程序不符合应用程序需求时,可能需要实现自定义 ILoggerProvider。 本节将实现一个自定义日志提供器,以便在控制台中对日志进行着色。
实现接口结构顺序
ILogger -> ILoggerProvider -> ILoggingBuilder(LoggerFactory)
1. 创建自定义记录器
ILogger接口
泛型接口用于记录从指定的 TCategoryName 类型名称派生类别名称的位置。
using Microsoft.Extensions.Logging;
namespace ContosoUniversity.Extensions.Logger;
// 自定义记录器配置
public sealed class ColorConsoleLoggerConfiguration
{
// 事件ID
public int EventId { get; set; }
// 日志字典:包括日志级别与内容描述呈现颜色风格等
public Dictionary<LogLevel, ConsoleColor> LogLevelToColorMap { get; set; } = new()
{
[LogLevel.Information] = ConsoleColor.Green,
};
}
// ILogger 实现类别名称通常是日志记录源。例如,创建记录器的类型:
public sealed class ColorConsoleLogger(string name,
Func<ColorConsoleLoggerConfiguration> getCurrentConfig) : ILogger
{
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => default!;
// 检查 _getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel),因此每个 logLevel 都有一个唯一的记录器。
// 在此实现中,每个日志级别都需要显式配置条目才能记录
public bool IsEnabled(LogLevel logLevel) =>
getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel);
/* 实现[写入日志项]Log方法
* LogLevel: 日志级别
* EventId: 事件ID
* TState: 要写入的项或对象
* Exception?: 与此项相关的异常
* Func<TState, Exception?, string>: 函数体,用于创建state和exception的String消息
*/
public void Log<TState>(LogLevel logLevel, EventId eventId,
TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}
ColorConsoleLoggerConfiguration config = getCurrentConfig();
if (config.EventId == 0 || config.EventId == eventId.Id)
{
ConsoleColor originalColor = Console.ForegroundColor;
Console.ForegroundColor = config.LogLevelToColorMap[logLevel];
Console.Write($"{logLevel.ToString().ToLower()[..4]}: ");
Console.ForegroundColor = originalColor;
Console.WriteLine($"{name}[{eventId.Id}]");
if (config.LogLevelToColorMap[logLevel] != ConsoleColor.DarkGreen)
Console.ForegroundColor = config.LogLevelToColorMap[logLevel];
Console.Write($" {formatter(state, exception)}");
Console.ForegroundColor = originalColor;
Console.WriteLine();
}
}
}
前面的代码:
- 为每个类别名称创建一个记录器实例。
- 在
IsEnabled
中检查_getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel)
,因此每个logLevel
都有一个唯一的记录器。 在此实现中,每个日志级别都需要显式配置条目才能记录。
最好在 ILogger.Log 的实现中调用 ILogger.IsEnabled,因为 Log
可以被任何使用者调用,无法保证它以前已被检查过。 在大多数实现中,IsEnabled
方法应该非常快。
2. 自定义记录器提供程序
ILoggerProvider 接口
负责创建记录器实例。 不需要为每个类别创建记录器实例,但对于某些记录器(例如 NLog 或 log4net)来说,这很有意义。 此策略允许你为每个类别选择不同的日志记录输出目标,如以下示例所示:
using Microsoft.Extensions.Options;
using System.Collections.Concurrent;
using System.Runtime.Versioning;
namespace ContosoUniversity.Extensions.Logger;
[UnsupportedOSPlatform("browser")] //定义类范围属性,"browser" 中不支持ColorConsoleLogger类型
[ProviderAlias("ColorConsole")] //在提供程序上定义别名,控制ColorConsoleLogger的配置
public sealed class ColorConsoleLoggerProvider : ILoggerProvider
{
private readonly IDisposable? _onChangeToken;
private ColorConsoleLoggerConfiguration _currentConfig;
// 可多线程访问的键/值对的线程安全集合<日志类别名称, 日志实例ILogger>
private readonly ConcurrentDictionary<string, ColorConsoleLogger> _loggers =
new(StringComparer.OrdinalIgnoreCase);
//通过IOptionsMonitor<TOptions>接口来更新对基础ColorConsoleLoggerConfiguration对象的更改
public ColorConsoleLoggerProvider(
IOptionsMonitor<ColorConsoleLoggerConfiguration> config)
{
_currentConfig = config.CurrentValue;
_onChangeToken = config.OnChange(updatedConfig => _currentConfig = updatedConfig);
}
// 为每个类别名称创建一个ColorConsoleLogger实例,并将其存储在 ConcurrentDictionary<TKey,TValue>中
public ILogger CreateLogger(string categoryName) =>
_loggers.GetOrAdd(categoryName, name => new ColorConsoleLogger(name, GetCurrentConfig));
private ColorConsoleLoggerConfiguration GetCurrentConfig() => _currentConfig;
public void Dispose()
{
_loggers.Clear();
_onChangeToken?.Dispose();
}
}
ColorConsoleLoggerProvider
类定义两个类范围属性:
- UnsupportedOSPlatformAttribute:
"browser"
中不支持ColorConsoleLogger
类型。 - ProviderAliasAttribute:配置节可以使用
"ColorConsole"
键定义选项。
可以使用任何有效的 配置提供程序指定配置。 请考虑以下 appsettings.json 文件:
{
"Logging": {
//在此处添加 ColorConsole子节点
"ColorConsole": {
"LogLevelToColorMap": {
"Information": "DarkGreen",
"Warning": "Yellow",
"Error": "Red"
}
}
}
}
这会将日志级别配置为以下值:
- LogLevel.Information:ConsoleColor.DarkGreen
- LogLevel.Warning:ConsoleColor.Cyan
- LogLevel.Error:ConsoleColor.Red
Information 日志级别设置为 DarkGreen,这将替代 ColorConsoleLoggerConfiguration
对象中设置的默认值。
3. 服务实例注册及注入
实现ILoggingBuilder 接口
实现自定义日志记录提供程序扩展接口
using ContosoUniversity.Extensions.Logger;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging.Configuration;
namespace ContosoUniversity.Extensions;
public static class ColorConsoleLoggerExtensions
{
public static ILoggingBuilder AddColorConsoleLogger(this ILoggingBuilder builder)
{
// 添加使用ILoggerProviderConfiguration接口或所需的服务
builder.AddConfiguration();
// 尝试注册单例服务<服务类型, 服务描述及实现>
builder.Services.TryAddEnumerable(
ServiceDescriptor.Singleton<ILoggerProvider, ColorConsoleLoggerProvider>());
// 指示应将日志设置加载到ILogger实例的类型中
LoggerProviderOptions.RegisterProviderOptions
<ColorConsoleLoggerConfiguration, ColorConsoleLoggerProvider>(builder.Services);
return builder;
}
public static ILoggingBuilder AddColorConsoleLogger(this ILoggingBuilder builder,
Action<ColorConsoleLoggerConfiguration> configure)
{
builder.AddColorConsoleLogger();
builder.Services.Configure(configure);
return builder;
}
}
服务注入
按照约定,注册依赖项注入的服务作为应用程序的启动例程的一部分进行。 注册发生在 Program 类中,也可以委托给 Startup 类。 在此示例中,你将直接从 Program.cs注册。
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Logging.ClearProviders();
builder.Services.AddLogging(logBuilder =>
{
//logBuilder.AddConsole(); // 控制台日志
logBuilder.AddColorConsoleLogger();
/* 还可通过回调函数,继续对自定义记录器配置项进行添加或修改
logBuilder.AddColorConsoleLogger(configuration =>
{
// Replace warning value from appsettings.json of "Cyan"
configuration.LogLevelToColorMap[LogLevel.Warning] = ConsoleColor.DarkCyan;
// Replace warning value from appsettings.json of "Red"
configuration.LogLevelToColorMap[LogLevel.Error] = ConsoleColor.DarkRed;
});
*/
});
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(7, "Oops, there was an error.");
logger.LogTrace(5, "== 120."); //没有配置Track类型日志,输出时则会自动过滤
}
运行此简单应用程序会将颜色输出呈现到控制台窗口,如下图所示:
第三方日志记录提供程序
下面是一些适用于各种 .NET 工作负荷的第三方日志记录框架:
- EFLogger
- JSNLog
- Log4Net
- Nlog
- Serilog
某些第三方框架可以执行 语义日志记录,也称为结构化日志记录。
使用第三方框架类似于使用其中一个内置提供程序:
- 将 NuGet 包添加到项目。
- 调用日志记录框架提供的
ILoggerFactory
或ILoggingBuilder
扩展方法。
Log4Net日志
Log4net是从Java中的Log4j迁移过来的一个.Net版的开源日志框架,它的功能很强大,可以将日志分为不同的等级,以不同的格式输出到不同的存储介质中,比如:数据库、txt文件、内存缓冲区、邮件、控制台、ANSI终端、远程接收端等等。
注册及配置
# NuGet包
Install-package Microsoft.Extensions.Logging.Log4Net.AspNetCore 8.0
ILoggingBuilder服务注入
builder.Services.AddLogging(logBuilder =>
{
logBuilder.ClearProviders(); //清空默认的日志提供程序[appsettings.json中Logging节点]
logBuilder.AddConsole(); // 控制台日志
builder.Logging.AddLog4Net();
});
// 启用敏感数据以及详细错误跟踪
public partial class SchoolContext : DbContext
{
//......
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder//.LogTo(Console.WriteLine)
// 启用敏感数据
.EnableSensitiveDataLogging()
// 启用详细错误
.EnableDetailedErrors();
base.OnConfiguring(optionsBuilder);
}
}
日志项配置
日志配置文件log4net.config
,默认配置可存放在项目根下
<?xml version="1.0" encoding="utf-8"?>
<log4net>
<!-- Define some output appenders -->
<appender name="FileAppenderDefault" type="log4net.Appender.RollingFileAppender">
<!--日志输出到exe程序这个相对目录下-->
<file value= "..\logs\"/>
<!--防止多线程时不能写Log,官方说线程非安全-->
<!--实际使用时,本地测试正常,部署后没有不能写日志的情况-->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<!--追加日志内容,true后续输出的日志会追加到之前的日志文件-->
<appendToFile value="true" />
<!--可以为:Once|Size|Date|Composite-->
<!--Composite为Size和Date的组合-->
<rollingStyle value="Composite" />
<!--置为true,当前最新日志文件名永远为file节中的名字-->
<staticLogFileName value="false" />
<!--当备份文件时,为文件名加的后缀-->
<datePattern value="SqlTrack_yyyy-MM-dd'.log'" />
<!--日志最大个数,都是最新的-->
<!--rollingStyle节点为Size时,只能有value个日志-->
<!--rollingStyle节点为Composite时,每天有value个日志-->
<maxSizeRollBackups value="100" />
<!--可用的单位:KB|MB|GB-->
<maximumFileSize value="10MB" />
<!--输出级别在INFO和ERROR之间的日志-->
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="ALL" />
<param name="LevelMax" value="FATAL" />
</filter>
<!--<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%n==========
%n【日志级别】%-5level
%n【记录时间】%date
%n【执行时间】[%r]毫秒
%n【执行Log分类的名称】%logger
%n【传入信息内容】%message
%n=========="/>
</layout>-->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%p][%d{HH:mm:ss}]【%logger】 %message %n" />
</layout>
</appender>
<root>
<!--(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL (低) -->
<!--<priority value="ALL"/>-->
<level value="ALL"/>
<!-- 根据LOG4NET_CONFIG_FILE环境变量确定appender -->
<appender-ref ref="FileAppenderDefault" />
</root>
</log4net>
注解
默认配置文档log4net.config
也可以指定配置路径和配置文件名,比如
builder.Logging.AddLog4Net("Cfg\log4net_Development.config");
注意Log4Net需通过项目的appsettings.json中Logging节点配置项进行输出关键性日志。
项目运行所生成的日志内容
Serilog日志
Serilog 是一个功能强大的日志记录库,专为 .NET 平台设计。它提供了丰富的 API 和可插拔的输出器及格式化器,使得开发者能够轻松定制和扩展日志记录功能。
注意
Srilog.AspNetCore是集成 Serilog 到 ASP.NET Core项目中,其中包含了以下包而无须再手工引入
JSON格式配置
"Serilog": {
//Sink接口(NuGet包引用,为ASPCore内置时可无免指定包引用)
"Using": [ "Serilog.Enrichers.Thread", "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
//最小级别
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
},
//丰富器
"Enrich": [
{
"Name": "FromLogContext"
},
{
"Name": "host",
"Args": {
"value": "Environment.MachineName"
}
},
{
"Name": "WithThreadId" //扩充当前管理的ThreadId属性
}
],
//过滤器
//"Filter": [
// {
// "Name": "ByExcluding",
// "Args": {
// //只包括
// "expression": "StartsWith(SourceContext, 'Microsoft.EntityFrameworkCore.')"
// }
// },
// {
// "Name": "ByIncludingOnly",
// "Args": {
// //不包括
// "expression": "StartsWith(SourceContext, 'Microsoft.Hosting.Lifetime.')"
// }
// }
//],
//输出器
"WriteTo": [
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"outputTemplate": "[{Level:u4}][{Timestamp:HH:mm:ss}][{SourceContext}]{Exception}{NewLine}{Message:lj}{NewLine}"
}
},
{
"Name": "File",
"Args": {
"path": "logs/traceSql-.log",
"restrictedToMinimumlevel": "Information",
"fileSizeLimitBytes": 10485760,
//"levelSwitch": "InitialLevel",
"rollingInterval": "Day",
"rollOnFileSizeLimit": true,
"retainedFileCountLimit": 7,
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter,Serilog.Formatting.Compact",
"outputTemplate": "[{Level:u4}][{Timestamp:HH:mm:ss}][{EventId.Id}]{SourceContext}{Exception}{NewLine}{Message:lj}{NewLine}"
}
}
]
},
通过引用NuGet包Serilog.Settings.Configuration
实现JSON配置注入该服务:
// 注册服务
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(new ConfigurationBuilder()
.AddJsonFile(path: "appsettings.json", optional: false, reloadOnChange: true)
.Build())
.CreateLogger();
//DI注入
builder.Host.UseSerilog();
这是最简单服务配置方式,下面以不同环境进行配置为例
- 先定义两个json环境配置文件,如
appsettings.Development
和appsettings.Production
两个Json配置内容可以先配置为一样,比如
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Serilog": {
//......省略
},
"AllowedHosts": "*"
}
}
提示
两者最主要的区别,无非就是WriteTo输出,在appsettings.Production
生产环境实际项目中可以不需要Console
终端输出模式,包含输出方式可以是Sinks.MSSqlServer、Sinks.MySQL
数据库或Sinks.Seq、Sinks.Elasticsearch
日志服务器平台等等。
- Json配置加载及服务注入
var builder = WebApplication.CreateBuilder(args);
var appEnvironment = $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json";
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(new ConfigurationBuilder()
.AddJsonFile(appEnvironment, optional: true)
.Build())
.CreateLogger();
builder.Host.UseSerilog();
Log.Information($"当前项目环境Json配置为:【{appEnvironment}】");
重点就是通过appEnvironment变量动态获取ASPNETCORE_ENVIRONMENT
环境变量,当在Debug
调试模式下运行时,系统会动态加载appsettings.Development
配置。反之,以Publish发布后则会调用appsettings.Production
配置进行注入服务。
- 当发布到生产环境运行时,比如发布到Publist目录直接执行主程序exe文件
注意
日志文件存储路径是运行目录(编译后)的logs\xxx.log
位置,而Debug
调试模式时存放在项目根下。
编程Code方式配置
builder.Host.UseSerilog((context, loggerconfig) =>
{
string _outputTemplate = "[{Level:u4}][{Timestamp:HH:mm:ss}][<{ThreadId}>{SourceContext}]{Exception}{NewLine}{Message:lj}{NewLine}";
// formatProvider: 提供特定于区域性的格式化信息或为null
var _formatter = new MessageTemplateTextFormatter(_outputTemplate, formatProvider: null);
loggerconfig.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore.Database.Command", LogEventLevel.Information)
//.Filter.ByIncludingOnly(logEvent => logEvent.Level >= LogEventLevel.Error)
//.Filter.ByExcluding(Matching.FromSource("Microsoft.EntityFrameworkCore"))
.Enrich.FromLogContext() //扩充日志上下文,使用LogContext.PushProperty添加和移除属性
.Enrich.WithProperty("host", Environment.MachineName)
.Enrich.WithThreadId()
.WriteTo.Console(outputTemplate: _outputTemplate, theme: AnsiConsoleTheme.Literate)
.WriteTo.Async(c => c.File(
formatter: _formatter,
path: Path.Combine("logs", @"traceSql-.log"),
restrictedToMinimumLevel: LogEventLevel.Information,
fileSizeLimitBytes: 10 * 1024 * 1024, // Singlefile:10M
levelSwitch: null, //new Serilog.Core.LoggingLevelSwitch(LevelAlias.Minimum),
//outputTemplate: _outputTemplate,
//buffered: false,
//shared: false,
//flushToDiskInterval: null,
rollingInterval: RollingInterval.Day,
rollOnFileSizeLimit: true,
retainedFileCountLimit: 7
//encoding: null,
//hooks: null,
//retainedFileTimeLimit: null
));
});
var app = builder.Build();
// 启用HttpRequestLogging
app.UseForwardedHeaders();
app.UseSerilogRequestLogging(options =>
options.EnrichDiagnosticContext = RequestEnricher.LogAdditionalInfo);
Log.Verbose("This is a verbose log message.");
Log.Warning("This is a warning log message.");
Log.Error("This is an error log message.");
//Log.CloseAndFlush();
namespace ContosoUniversity.Extensions.Logger;
public static class RequestEnricher
{
//增加Client请求IP
public static void LogAdditionalInfo(IDiagnosticContext diagnosticContext, HttpContext httpContext)
{
diagnosticContext.Set(
"ClientIP",
httpContext.Connection.RemoteIpAddress?.ToString());
}
}
其中,.Enrich.WithThreadId()
丰富器扩充当前管理的子线程信息,比如在输出模板中获取子线程ID{ThreadId}
等,而.WriteTo.Async
是以异步模型写入文件,需引用NuGet包Serilog.Sinks.Async
。
LoggerConfiguration配置结构
- Sinks接收器
- MinimumLevel最小级别
- Enrich丰富器
- Filter过滤(筛选)器
- WriteTo输出器
- Property扩展属性
Serilog配置分为Sinks接收器、WriteTo输出器以及Filter过滤器等部分组成,可以通过编程方式或XML、JSON编码方式进行配置。
1. Sinks接收器
提供给.WriteTo
输出使用,需引用NeGet包(dotnet add package)支持
# 将日志输出到控制台[Serilog.AspNetCore内置]
Serilog.Sinks.Console
# 将日志写入文件中,支持按时间滚动[Serilog.AspNetCore内置]
Serilog.Sinks.File
# 将日志发送到 Seq 日志服务器,Seq 提供了强大的查询和可视化功能
Serilog.Sinks.Seq
# 将日志发送到 Elasticsearch,配合 Kibana 使用可以进行复杂的日志分析和可视化
Serilog.Sinks.Elasticsearch
# 将日志记录到 Microsoft SQL Server 数据库
Serilog.Sinks.MSSqlServer
# 将日志记录到 MySQL 数据库
Serilog.Sinks.MySQL
# 通过 HTTP POST 请求发送日志到远程 API 或服务
Serilog.Sinks.Http
# 通过电子邮件发送错误或其他关键日志信息
Serilog.Sinks.Email
# 将日志发送到 Azure Application Insights,提供性能监控和诊断工具
Serilog.Sinks.ApplicationInsights
2. MinimumLevel最小记录级别
配置将事件传递到接收器的最低级别。如果未指定,则仅 LogEventlevel.lnformation 级别的事件及以上都将通过。
3. Enrichers丰富器
丰富器是一种扩展机制,可以将额外的上下文信息添加到日志事件中。Serilog提供了一些内置的丰富器,例如WithProperty和WithProperties,可以用于添加自定义属性到日志事件中。
除了内置的丰富器,Serilog还支持自定义丰富器,开发人员可以根据自己的需求实现自己的丰富器。自定义丰富器可以从各种来源获取上下文信息,例如从请求头、数据库、配置文件等。
常见的Enrichers
- WithThreadId :为日志事件添加线程ID属性,帮助追踪线程相关的日志。
- WithEnvironmentUserName :为日志事件添加环境用户名属性,适用于需要记录用户信息的场景。
- Serilog.Enrichers.Sensitive :自动屏蔽日志中的敏感信息,如电子邮件地址和IBAN账号,确保日志既详尽又安全。
- Serilog.Enrichers.MassTransit :集成 MassTransit消息传递框架 ,记录消息和事件,便于分析和跟踪应用程序行为。
参考配置文档:丰富配置
提供给.Enrich
扩充使用,第三方丰富器需引用NeGet包(dotnet add package)支持
# 使用环境变量扩充 Serilog 事件。
Serilog.Enrichers.Context
# 使用 System.Environment 中的属性扩充 Serilog 日志事件。
Serilog.Enrichers.Environment
# Serilog 的进程丰富器。
Serilog.Enrichers.Process
# 使用当前线程中的属性扩充 Serilog 事件。
Serilog.Enrichers.Thread
# 使用客户端 IP、相关 ID 和 HTTP 请求标头丰富日志。
Serilog.Enrichers.ClientInfo
4. Filters过滤器
过滤器控制要处理的日志事件。该属性提供了用于配置过滤器的方法。
// Example
builder.Host.UseSerilog((context, loggerconfig) =>
{
loggerconfig.MinimumLevel.Information()
//ByIncludingOnly: 只包括与谓词匹配的事件
.Filter.ByIncludingOnly(logEvent => logEvent.Level >= LogEventLevel.Error)
//ByExcluding: 不包括匹配谓词的日志事件
.Filter.ByExcluding(Matching.FromSource("Microsoft.EntityFrameworkCore"));
//loggerconfig.CreateLogger();
});
5. WriteTo输出
以下以输出到文件.WriteTo.File
参数配置Serilog.Sinks.File 5.0.0
为例
/// <summary>
/// 将日志事件写入指定文件。
/// </summary>
/// <param name="sinkConfiguration">日志记录接收器配置</param>
/// <param name="path">文件路径</param>
/// <param name="restrictedToMinimumLevel">通过接收器的事件的最小级别,当指定levelSwitch时忽略
/// <param name="formatProvider">提供特定于区域性的格式化信息或为空</param>
/// <param name="outputTemplate">描述用于写入接收器的格式的消息模板,
/// 默认值为“{Timestamp:yyyy-MM-dd HH:mm:ss”。fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"</param>
/// <param name="fileSizeLimitBytes">允许日志文件增长到的大致最大大小,以字节为单位
/// 对于不受限制的增长,请传递 null。默认值为 1GB。为避免写入部分事件,即使超出限制,也会完整写入限制内的最后一个事件
/// <param name="levelSwitch">允许在运行时更改最小直通级别的开关
/// <param name="buffered">是否使用缓冲来刷新输出文件,缺省为false</param>
/// <param name="shared">允许多个进程共享日志文件,缺省为false</param>
/// <param name="flushToDiskInterval">按指定的时间间隔定期执行完全磁盘刷新,缺省为null</param>
/// <param name="rollingInterval">日志记录将滚动到新文件的时间间隔</param>
/// <param name="rollOnFileSizeLimit">如果为 true,则在达到文件大小限制时将创建新文件,
/// 文件名将以 _NNN 格式附加一个数字,第一个文件名没有编号</param>
/// <param name="retainedFileCountLimit">保留的日志文件的最大数量,包括当前日志文件
/// 缺省为null,则是无限保留(默认值为31)</param>
/// <param name="encoding">写入文本文件的字符编码,默认值为不带BOM的UTF-8</param>
/// <param name="hooks">(可选)启用挂钩日志文件生命周期事件</param>
/// <param name="retainedFileTimeLimit">间隔结束后,滚动日志文件将保留的最长时间。
/// 必须大于或等于"TimeSpan.Zero"。如果"rollingInterval"为"RollingInterval.Infinite",则忽略默认值为无限期保留文件。
/// <returns>配置对象允许方法链接</returns>
public static LoggerConfiguration File(
this LoggerSinkConfiguration sinkConfiguration,
string path,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
string outputTemplate = DefaultOutputTemplate,
IFormatProvider? formatProvider = null,
long? fileSizeLimitBytes = DefaultFileSizeLimitBytes,
LoggingLevelSwitch? levelSwitch = null,
bool buffered = false,
bool shared = false,
TimeSpan? flushToDiskInterval = null,
RollingInterval rollingInterval = RollingInterval.Infinite,
bool rollOnFileSizeLimit = false,
int? retainedFileCountLimit = DefaultRetainedFileCountLimit,
Encoding? encoding = null,
FileLifecycleHooks? hooks = null,
TimeSpan? retainedFileTimeLimit = null)
/// <summary>
/// 将日志事件写入指定文件。
/// </summary>
/// <param name="sinkConfiguration">日志记录接收器配置</param>
/// <param name="formatter">格式化程序,例如“JsonFormatter”,用于将日志事件转换为文件的文本。
/// 如果需要控制常规文本格式,请使用"File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool, TimeSpan?, RollingInterval, bool, int?, Encoding, FileLifecycleHooks, TimeSpan?)"
/// 的另一个重载,并改为指定 outputTemplate 参数</param>
/// <param name="path">文件路径</param>
/// <param name="restrictedToMinimumLevel">通过接收器的事件的最小级别,当指定levelSwitch时忽略
/// <param name="levelSwitch">允许在运行时更改最小直通级别的开关
/// <param name="fileSizeLimitBytes">允许日志文件增长到的大致最大大小,以字节为单位
/// 对于不受限制的增长,请传递 null。默认值为 1GB。为避免写入部分事件,即使超出限制,也会完整写入限制内的最后一个事件
/// will be written in full even if it exceeds the limit.</param>
/// <param name="buffered">是否使用缓冲器来刷新输出文件</param>
/// <param name="shared">允许多个进程共享日志文件,默认值为false</param>
/// <param name="flushToDiskInterval">按指定的时间间隔定期执行完全磁盘刷新,缺省为null</param>
/// <param name="rollingInterval">日志记录将滚动到新文件的时间间隔</param>
/// <param name="rollOnFileSizeLimit">如果为 true,则在达到文件大小限制时将创建新文件,
/// 如果为 true,则在达到文件大小限制时将创建新文件。文件名将以 _NNN 格式附加一个数字,第一个文件名没有编号</param>
/// <param name="retainedFileCountLimit">保留的日志文件的最大数量,包括当前日志文件
/// 缺省为null,则是无限保留(默认值为31)</param>
/// <param name="encoding">写入文本文件的字符编码,默认值为不带BOM的UTF-8</param>
/// <param name="hooks">(可选)启用挂钩日志文件生命周期事件</param>
/// <param name="retainedFileTimeLimit">间隔结束后,滚动日志文件将保留的最长时间。
/// 必须大于或等于 “TimeSpan.Zero”。
public static LoggerConfiguration File(
this LoggerSinkConfiguration sinkConfiguration,
ITextFormatter formatter,
string path,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
long? fileSizeLimitBytes = DefaultFileSizeLimitBytes,
LoggingLevelSwitch? levelSwitch = null,
bool buffered = false,
bool shared = false,
TimeSpan? flushToDiskInterval = null,
RollingInterval rollingInterval = RollingInterval.Infinite,
bool rollOnFileSizeLimit = false,
int? retainedFileCountLimit = DefaultRetainedFileCountLimit,
Encoding? encoding = null,
FileLifecycleHooks? hooks = null,
TimeSpan? retainedFileTimeLimit = null)
/// <summary>
/// 将审计日志事件写入指定文件
/// </summary>
/// <param name="sinkConfiguration">日志记录接收器配置</param>
/// <param name="path">文件路径</param>
/// <param name="restrictedToMinimumLevel">通过接收器的事件的最小级别,当指定levelSwitch时忽略
/// <param name="levelSwitch">允许在运行时更改最小直通级别的开关
/// <param name="formatProvider">提供特定于区域性的格式化信息或为空</param>
/// <param name="outputTemplate">描述用于写入接收器的格式的消息模板,
/// 默认值为“{Timestamp:yyyy-MM-dd HH:mm:ss”。fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"</param>
/// <param name="encoding">写入文本文件的字符编码,默认值为不带BOM的UTF-8</param>
/// <param name="hooks">(可选)启用挂钩日志文件生命周期事件</param>
/// <returns>配置对象允许方法链接</returns>
public static LoggerConfiguration File(
this LoggerAuditSinkConfiguration sinkConfiguration,
string path,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
string outputTemplate = DefaultOutputTemplate,
IFormatProvider? formatProvider = null,
LoggingLevelSwitch? levelSwitch = null,
Encoding? encoding = null,
FileLifecycleHooks? hooks = null)
/// <summary>
/// 将审计日志事件写入指定文件
/// </summary>
/// <param name="sinkConfiguration">日志记录接收器配置</param>
/// <param name="formatter">格式化程序,例如"JsonFormatter",用于将日志事件转换为文件的文本。
/// 如果需要控制常规文本格式,请使用
/// "File(LoggerAuditSinkConfiguration, string, LogEventLevel, string, IFormatProvider, LoggingLevelSwitch, Encoding, FileLifecycleHooks)"的另一个重载,并改为指定 outputTemplate 参数。/>
/// </param>
/// <param name="path">文件路径</param>
/// <param name="restrictedToMinimumLevel">通过接收器的事件的最小级别,当指定levelSwitch时忽略
/// <param name="levelSwitch">允许在运行时更改最小直通级别的开关
/// <param name="encoding">写入文本文件的字符编码,默认值为不带BOM的UTF-8</param>
/// <param name="hooks">(可选)启用挂钩日志文件生命周期事件</param>
/// <returns>配置对象允许方法链接</returns>
public static LoggerConfiguration File(
this LoggerAuditSinkConfiguration sinkConfiguration,
ITextFormatter formatter,
string path,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
LoggingLevelSwitch? levelSwitch = null,
Encoding? encoding = null,
FileLifecycleHooks? hooks = null)
提示
上述方法体只提供几种通用最较强的几种,过时或废弃不再列出。
6. Property附加属性
静态附加
builder.Host.UseSerilog((context, loggerconfig) =>
{
loggerconfig.Enrich.WithThreadId()
.Enrich.WithProperty("host", Environment.MachineName);
});
或者在Json中附加
"Serilog":{
"Properties": {
"Home": "testRoot"
}
}
动态附加
调用IDiagnosticContext
接口,进行附加扩展属性:
var app = builder.Build();
// ......
app.UseStaticFiles();
// 添加中间件以简化请求日志记录
app.UseSerilogRequestLogging();
public class HomeController : Controller
{
readonly IDiagnosticContext _diagnosticContext;
readonly ILogger<HomeController> _logger;
readonly SchoolContext _context;
public HomeController(IDiagnosticContext diagnosticContext,
ILogger<HomeController> logger, SchoolContext context)
{
_diagnosticContext = diagnosticContext;
_logger = logger;
_context = context;
}
public IActionResult Index()
{
// The request completion event will carry this property.
_diagnosticContext.Set("CatalogLoadTime", 1423);
return View();
}
}
还可以通过RequestLoggingOptions
设置所提供IDiagnosticContext
实例的值,我们基本上使用完全相同的方法来定制中间件所使用的方法。下面的静态帮助器类从当前HttpContext
上下文检索值,并在值可用时对其进行设置。
下面的静态helper类从当前HttpContext检索值,并在值可用时设置它们。
public static class LogHelper
{
public static void EnrichFromRequest(IDiagnosticContext diagnosticContext, HttpContext httpContext)
{
var request = httpContext.Request;
// Set all the common properties available for every request
diagnosticContext.Set("Host", request.Host);
diagnosticContext.Set("Protocol", request.Protocol);
diagnosticContext.Set("Scheme", request.Scheme);
// Only set it if available. You're not sending sensitive data in a querystring right?!
if(request.QueryString.HasValue)
{
diagnosticContext.Set("QueryString", request.QueryString.Value);
}
// Set the content-type of the Response at this point
diagnosticContext.Set("ContentType", httpContext.Response.ContentType);
// Retrieve the IEndpointFeature selected for the request
var endpoint = httpContext.GetEndpoint();
if (endpoint is object) // endpoint != null
{
diagnosticContext.Set("EndpointName", endpoint.DisplayName);
}
}
}
上面的帮助器函数从“Request”,“Response”以及其他中间件(端点名称)设置的功能中检索值。您可以扩展它,以根据需要在请求中添加其他值。
您可以通过调用UseSerilogRequestLogging
的EnrichDiagnosticContext
属性,来注册上面的帮助类:
var app = builder.Build();
// ... Other middleware
app.UseStaticFiles();
app.UseSerilogRequestLogging(opts
=> opts.EnrichDiagnosticContext = LogHelper.EnrichFromRequest);
// ... Other middleware
现在,当您发出请求时,您将看到添加到Serilog结构化日志中的所有其他属性:
只要您具有通过当前HttpContext可供中间件管道使用的值,就可以使用此方法。但是MVC的相关属性是个例外,它们是MVC中间件“内部”的特性,例如action 名称或RazorPage处理程序名称。
-持续更新-