C Sharp/日志

在Microsoft.Extensions命名空间下,包Microsoft.Extensions.Logging包含用于记日志必需的类、接口。其中最重要的是:

  • ILogger:用于记日志,有很多扩展方法。声明了3个方法:
    • void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter); Logger对日志消息的写入实现在Log方法中。Log方法的logLevel代表写入日志消息的等级,而日志消息的原始内容通过参数state和exception这两个参数来承载承载,前者代表一个原始的日志条目(Log Entry),后者代表与之关联的异常。日志在被写入之前必须格式成一个字符串,由于日志原始信息分别由一个Object和Exception对象对象来表示,所以日志的“格式化器”自然体现为一个Func<object, Exception, string>类型的委托对象。一条写入的日志消息会关联着一个日志记录事件,后者则通过一个EventId对象来标识,Log方法的eventId参数类型就是EventId。EventId被定义成一个结构,它具有两个基本的属性Id和Name,前者代表必需的唯一标识,后者则是一个可选的名称。除此之外,整形到EventId类型之间还存在一个隐式类型转换,所以在需要使用EventId对象的地方,我们可以使用一个整数来代替。
    • bool IsEnabled(LogLevel logLevel); 对于任意一次日志消息写入请求,Logger并不会直接调用Log方法将日志消息写入对应的目的地,它会根据提供日志消息的等级判断是否应该执行写入操作,判断的逻辑实现在IsEnabled方法中,只有当这个方法返回True的时候它的Log方法才会被执行。
    • IDisposable BeginScope<TState>(TState state); 在默认的情况下,每次调用Logger的Log方法所进行的日志记录操作都是相互独立的,但是有时候我们需要将相关的多次日志记录做一个逻辑关联,或者说我们需要为多次日志记录操作创建一个共同的上下文范围。这样一个关联上下文范围可以通过BeginScope<TState>方法来创建,该方法将该上下文范围与参数state表示的对象进行关联。被创建的这个关联上下文体现为一个IDisposable对象,我们需要调用其Dispose方法将其释放回收,也就是说被创建的关联上下文的生命周期终止于Dispose方法的调用。
  • ILoggerFactory:声明了ILogger CreateLogger(string categoryName)和void AddProvider(ILoggerProvider provider)
  • ILoggerProvider:声明了ILogger CreateLogger(string categoryName)。该接口管理、在指出logging category时创建适当的logger.
  • LoggerFactory类:内建类,实现了ILoggerFactory。用于创建Logger、增加LoggerProvider.

NET Core的日志模型主要由三个核心对象构成,它们分别是Logger、LoggerProvider和LoggerFactory。总的来说,LoggerProvider提供一个具体的Logger对象将格式化的日志消息写入相应的目的地,但是我们在编程过程中使用的Logger对象则由LoggerFactory创建,这个Logger利用注册到LoggerFactory的LoggerProvider来提供真正具有日志写入功能的Logger,并委托后者来记录日志。

例子编辑

首先,需要安装nuget包:

PM> Install-Package Microsoft.Extensions.Logging
PM> Install-Package Microsoft.Extensions.Logging.Console
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;

namespace CoreApp2
{ 
    class Program
    {
        static void Main(string[] args)
        {
            

            var loggerFactory = LoggerFactory.Create(builder => {
                builder.AddFilter( a =>
                {
                    if (a == LogLevel.Warning)
                        return true;
                    if (a == LogLevel.Information)
                        return true;
                    if (a == LogLevel.Debug)
                        return true;
                    if (a == LogLevel.Trace)
                        return true;
                    if (a == LogLevel.Error)
                        return true;

                    //if (a == LogLevel.Critical)
                    //    return true;
                    return false;
                }).AddFilter("CoreApp2.Program", LogLevel.Critical)
                       .AddConsole();
            });
             

            ILogger logger = loggerFactory.CreateLogger<Program>();
            logger.LogInformation("Logging information.");
            logger.LogDebug("Logging debug information.");
            logger.LogTrace("Logging trace");
            logger.LogWarning("Logging warning.");
            logger.LogError("Logging error information.");
            logger.LogCritical("Logging critical information.");
        }
    }
}

另外一个例子:

using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;  
using System.Threading;
using System.Threading.Tasks;
namespace CoreApp2
{ 
    class Program
    {
        static void Main(string[] args)
        {
            //Console.WriteLine("Hello World!");

            CreateHostBuilder(args).Build().Run();
        }

        static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .ConfigureLogging(config =>
            {
                config.AddConsole();
            })
            .ConfigureServices((context, service) =>
            {
                service.AddHostedService<HostRunner>();
            });        
    }
    public class HostRunner : BackgroundService
    {
        private readonly ILogger<HostRunner> _logger;

        public HostRunner(ILogger<HostRunner> logger)
        {
            _logger = logger;
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            int count = 10;
            for (int i = 0; i < count; i++)
            {
                Thread.Sleep(100);
                _logger.LogInformation("这是日志消息 {序号}/{总数}。", i, count);

            }
            return Task.CompletedTask;
        }
    }
}