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 的第三方日志记录框架:

某些第三方框架可以执行语义日志记录(又称结构化日志记录)

使用第三方框架类似于使用以下内置提供程序之一:

  1. 将 NuGet 包添加到你的项目。
  2. 调用日志记录框架提供的 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"
            }
    }
}
上一篇:celery+django的定时任务设置 - 最佳实践 - 在django管理后台配置定时任务


下一篇:ASP.NET Core SignalR (十)【下】:SignalR Javascript 客户端