asp.net core 3.1 日志记录 Logging
Ilogger:包括实际执行记录日志操作的方法。
IloggerProvider:用于创建 ILogger 对象。
IloggerFactory:通过 ILoggerProvider 对象创建 ILogger 对象。
ILogger接口
要记录日志,需要使用 ILogger 接口
public interface ILogger
{
//开始逻辑操作范围。
IDisposable BeginScope<TState>(TState state);
//检查是否已启用给定 logLevel。
bool IsEnabled(LogLevel logLevel);
//写入日志项。
void Log<TState> (Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, TState state, Exception exception, Func<TState,Exception,string> formatter);
}
日志级别
Log 方法的第一个参数指明了这条信息的级别,日志级别即其重要程度。ASP.NET Core 日志系统定义了 6 个级别
LogLevel | “值” | 方法 | 描述 |
---|---|---|---|
Trace | 0 | LogTrace | 包含最详细的消息。 这些消息可能包含敏感的应用数据。 这些消息默认情况下处于禁用状态,并且不应在生产中启用。 |
调试 | 1 | LogDebug | 用于调试和开发。 由于量大,请在生产中小心使用。 |
信息 | 2 | LogInformation | 跟踪应用的常规流。 可能具有长期值。 |
警告 | 3 | LogWarning | 对于异常事件或意外事件。 通常包括不会导致应用失败的错误或情况。 |
错误 | 4 | LogError | 表示无法处理的错误和异常。 这些消息表示当前操作或请求失败,而不是整个应用失败。 |
严重 | 5 | LogCritical | 需要立即关注的失败。 例如数据丢失、磁盘空间不足。 |
无 | 6 | 指定日志记录类别不应写入任何消息。 |
除了指定日志级别以外,还需要指定 EventId、一个返回值类型为字符串的委托,该委托的意义在于根据指定的状态以及异常返回要输出的日志信息。从上面的代码中可以看出,直接使用 Log 方法来记录日志会非常麻烦。为此 ILogger 接口提供了若干个扩展方法,用来更方便地记录指定级别的日志,它们包括 LogTrace、LogDebug、LogInformation、LogWarning、LogError 和 LogCritical,这几个方法分别对应上面所提到的各个级别。因此,上面的代码可以改写为:
public class PrivacyModel : PageModel
{
private readonly ILogger<PrivacyModel> _logger;
public PrivacyModel(ILogger<PrivacyModel> logger)
{
_logger = logger;
}
public void OnGet()
{
_logger.LogInformation("GET Pages.PrivacyModel called.");
}
}
在Startup记录日志
当 ASP.NET Core 应用程序运行时,日志组件会被添加到其依赖注入容器中,因此只要在合适的位置将 ILogger 对象注入进来,即可使用它来记录日志。
public class Startup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
app.Run(async (context) =>
{
logger.LogInformation("这是一条测试日志");
await context.Response.WriteAsync("Hello, world");
});
}
}
在 Startup 类的 Configure 方法中,通过方法注入将
ILogger<Startup>
作为该方法的参数注入进来
ILogger 接口有一个派生接口 ILogger<out TCategoryName>
,其中泛型类型 TCategoryName
表示日志类别名称,它可以是任何类型,通常情况下,它的值应为当前所在类,如上面的 Startup
类。当注入 ILogger
时,必须为其指定泛型类型。
日志事件 ID
在日志的输出结果中,日志类别后有一个用中括号括起来的数字,该数字为事件标识符(Event Id) ,它的值是一个数字,默认值为 0。合理地使用这个数字能够帮助开发者对日志进一步分类,比如,某种操作的 Id 是1000,另一类操作的 Id 是 1002。注意,Event Id 的显示格式由 Provider定义,上述显示形式是由 ConsoleProvider(即控制台日志提供程序)定义的;在其他 Provider 中则不然,例如,DebugProvider 不显示 Event Id。要设置 Event Id,我们只要使用 LogInformation 方法的另一个重载形式即可。
每个日志都可指定一个事件 ID 。 示例应用使用 MyLogEvents
类来定义事件 ID:
public class MyLogEvents
{
public const int GenerateItems = 1000;
public const int ListItems = 1001;
public const int GetItem = 1002;
public const int InsertItem = 1003;
public const int UpdateItem = 1004;
public const int DeleteItem = 1005;
public const int TestItem = 3000;
public const int GetItemNotFound = 4000;
public const int UpdateItemNotFound = 4001;
}
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
return NotFound();
}
return ItemToDTO(todoItem);
}
事件 ID 与一组事件相关联。 例如,与在页面上显示项列表相关的所有日志可能是 1001。
日志记录提供程序可将事件 ID 存储在 ID 字段中,存储在日志记录消息中,或者不进行存储。 调试提供程序不显示事件 ID。 控制台提供程序在类别后的括号中显示事件 ID:
info: TodoApi.Controllers.TodoItemsController[1002]
Getting item 1
warn: TodoApi.Controllers.TodoItemsController[4000]
Get(1) NOT FOUND
一些日志记录提供程序将事件 ID 存储在一个字段中,该字段允许对 ID 进行筛选。
内置日志记录提供程序
public static IWebHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = new WebHostBuilder();
…
builder.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger();
})
…
}
CreateDefaultBuilder 方法默认添加了 3 个日志提供程序,如果不需要默认所添加的这些日志提供程序,可以调用 ILoggerProvider 接口的 ClearProviders 方法,然后再添加所需要的日志提供程序。
- 控制台
向控制台窗口输出日志 - 调试
向开发环境(IDE)的调试窗口输出日志,它会调用System.Diagnostics.Debug 类的 WriteLine 方法向外输出。 - EventSource
向事件跟踪器输出日志。 - EventLog
向 Window Event Log 输出日志,仅支持 Windows 操作系统。 - AzureAppServicesFile 和 AzureAppServicesBlob
仅在 Azure 中使用,当应用程序部署到 Azure Web服务中后, Azure App Service 日志提供程序自动会添加进来。 - ApplicationInsights
提供程序包将日志写入 Azure Application Insights。 Application Insights 是一项服务,可监视 Web 应用并提供用于查询和分析遥测数据的工具。 如果使用此提供程序,则可以使用 Application Insights 工具来查询和分析日志。
与 ILoggingBuilder 一样,ILoggerFactory 在添加 ASP.NET Core 内置的日志提供程序时,也可以使用 AddConsole 和 AddDebug 等扩展方法来添加日志提供程序。
第三方日志记录提供程序
适用于 ASP.NET Core 的第三方日志记录框架:
- elmah.io(GitHub 存储库)
- Gelf(GitHub 存储库)
- JSNLog(GitHub 存储库)
- KissLog.net(GitHub 存储库)
- Log4Net(GitHub 存储库)
- Loggr(GitHub 存储库)
- NLog(GitHub 存储库)
- PLogger(GitHub 存储库)
- Sentry(GitHub 存储库)
- Serilog(GitHub 存储库)
- Stackdriver(Github 存储库)
某些第三方框架可以执行语义日志记录(又称结构化日志记录)。
使用第三方框架类似于使用以下内置提供程序之一:
- 将 NuGet 包添加到你的项目。
- 调用日志记录框架提供的
ILoggerFactory
扩展方法。
有关详细信息,请参阅各提供程序的相关文档。 Microsoft 不支持第三方日志记录提供程序。
ILoggerFactory
ILoggerFactory 接口用于创建 ILogger 类型的对象。
public interface ILoggerFactory : IDisposable
{
//将 ILoggerProvider 添加到日志记录系统。
void AddProvider(ILoggerProvider provider);
//创建一个新的 ILogger 实例。该方法的参数 categoryName 为类日志的类别名称,它主要用来为日志指定分类名称
ILogger CreateLogger(string categoryName);
}
要显式指定类别,请调用 ILoggerFactory.CreateLogger
:
public class ContactModel : PageModel
{
private readonly ILogger _logger;
public ContactModel(ILoggerFactory logger)
{
_logger = logger.CreateLogger("MyCategory");
}
public void OnGet()
{
_logger.LogInformation("GET Pages.ContactModel called.");
}
}
分组和过滤
分组
参考:日志作用域
对于一组逻辑上相关的操作,将其日志信息分为一组是很有意义的,这需要使用 Scope 来实现。ILogger
接口有一个方法即BeginScope<TState>(TState state)
用于创建 Scope,其中 TState
指明要创建 Scope
的标识符,它可以为任何类型的数据,一般情况下,使用字符串来指明。BeginScope<TState>
方法的返回值类型为 IDisposable
,因此可以使用 using 语句块来创建 Scope,代码如下所示。
using (logger.BeginScope("获取数据"))
{
logger.LogInformation("准备获取数据");
…
if (data == null)
{
logger.LogError("数据不存在");
}
}
要在 Scope 中输出日志,除了需要创建 Scope 外,还要在ILoggerProvider 对象中启用这一功能。在添加日志提供程序时可以指定该ILoggerProvider 的一些选项。
例如,对于 ControlProvider,只要设置ConsoleLoggerOptions 的 IncludeScopes 属性为 true,即可为其启用Scope 功能,它的默认值为 false。
1.下列代码为控制台提供程序启用作用域:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
//ConsoleLoggerOptions的 IncludeScopes = true
logging.AddConsole(options => options.IncludeScopes = true);
logging.AddDebug();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
2.【推荐】以下 JSON 为控制台提供程序启用范围:
{
"Logging": {
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true, // Required to use Scopes.
"LogLevel": {
"Microsoft": "Warning",
"Default": "Information"
}
},
"LogLevel": {
"Default": "Debug"
}
}
}
输出
info: LoggingTest.Startup[0]
=> 获取数据
准备获取数据
fail: LoggingTest.Startup[0]
=> 获取数据
数据不存在
过滤
可以通过设置最低日志级别来进行日志过滤,当这样设置后,所有低于指定日志级别的日志都不会被处理,也不会显示。例如,如果设置最低日志级别为 LogLevel.Information,那么 Debug 和 Trace 级别的日志都不会显示。
1.SetMinimumLevel
要设置最低日志级别,同样需要在 ConfigureLogging 方法中进行配置,此时只要调用 ILoggingBuilder 接口的 SetMinimumLevel 方法即可。
Host.CreateDefaultBuilder(args)
.ConfigureLogging(builder =>
{
builder.ClearProviders();
builder.AddConsole(loggerOptions => loggerOptions.IncludeScopes = true);
builder.SetMinimumLevel(LogLevel.Information);
})
.UseStartup<Startup>();
LogLevel.None
除了之前提到的那些级别以外,还有一个值是 None,该值高于其他所有值。如果指定这个值为最低级别,那么所有的日志都不会输出。
2.AddFilter
除了设置最低日志级别外,ILoggerBuilder 接口还提供了 AddFilter
方法,该方法包括多个重载,它能够指定更复杂的条件,并只显示满足条件的日志。在以下方法中,将显示 LoggeringTest.Startup 类别中,等于并高于Information 级别的日志。
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
logging.AddConsole().AddFilter("LoggingTest.Startup", LogLevel.Information);
})
加载JSON的Logging配置
默认情况下,在 appsettings.json 文件中包含了对日志的配置信息,要将日志配置加载并应用到程序的日志系统中,可以调用AddConfiguration
方法
Host.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
...
})
.UseStartup<Startup>();
在 appsettings.json
配置文件的「Logging」一节则默认包含了关于记录日志的统一配置,如 LogLevel 配置项用于设置对指定类别的日志的最低输出级别,凡是在该类别中低于指定级别的日志将不会被输出。除了设置统一配置外,还可以为每一种日志提供程序提供具体的输出配置,只要在「Logging」一节为其增加相应的配置即可,如下例添加了对 Console 类型日志提供程序的配置。
{
"Logging": {
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft.AspNetCore.Mvc.Razor": "Error",
"Default": "Information"
}
},
"LogLevel": {
"Default": "Debug"
}
}
}