• 理解ASP.NET Core 日志(Logging)


    注:本文隶属于《理解ASP.NET Core》系列文章,请查看置顶博客或点击此处查看全文目录

    快速上手

    添加日志提供程序

    在文章主机(Host)中,讲到Host.CreateDefaultBuilder方法,默认通过调用ConfigureLogging方法添加了ConsoleDebugEventSourceEventLog(仅Windows)共四种日志记录提供程序(Logger Provider),然后在主机Build过程中,通过AddLogging()注册了日志相关的服务。

    .ConfigureLogging((hostingContext, logging) =>
    {
        bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
    
        if (isWindows)
        {
            logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
        }
    
        // 添加 Logging 配置
        logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
        
        // ConsoleLoggerProvider
        logging.AddConsole();
        // DebugLoggerProvider
        logging.AddDebug();
        // EventSourceLoggerProvider
        logging.AddEventSourceLogger();
    
        if (isWindows)
        {
            // 在Windows平台上,添加 EventLogLoggerProvider
            logging.AddEventLog();
        }
    
        logging.Configure(options =>
        {
            options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId
                                                | ActivityTrackingOptions.TraceId
                                                | ActivityTrackingOptions.ParentId;
        });
    })
    
    public class HostBuilder : IHostBuilder
    {
        private void CreateServiceProvider()
        {
            var services = new ServiceCollection();
            
            // ...
            
            services.AddLogging();
        
            // ...
        }
    }
    

    如果不想使用默认添加的日志提供程序,我们可以通过ClearProviders清除所有已添加的日志记录提供程序,然后添加自己想要的,如Console

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging =>
            {
                logging.ClearProviders()
                    .AddConsole();
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
    

    记录日志

    日志记录提供程序均实现了接口ILoggerProvider,该接口可以创建ILogger实例。

    通过注入服务ILogger<TCategoryName>,就可以非常方便的进行日志记录了。

    该服务需要指定日志的类别,可以是任意字符串,但是我们约定使用所属类的名称,通过泛型体现。例如,在控制器ValuesController中,日志类别就是ValuesController类的完全限定类型名。

    public class ValuesController : ControllerBase
    {
        private readonly ILogger<ValuesController> _logger;
    
        public ValuesController(ILogger<ValuesController> logger)
        {
            _logger = logger;
        }
    
        [HttpGet]
        public string Get()
        {
            _logger.LogInformation("ValuesController.Get");
            return "Ok";
        }
    }
    

    当请求Get方法后,你就可以在控制台中看到看到输出的“ValuesController.Get”

    如果你想要显式指定日志类别,则可以使用ILoggerFactory.CreateLogger方法:

    public class ValuesController : ControllerBase
    {
        private readonly ILogger _logger1;
    
        public ValuesController(ILoggerFactory loggerFactory)
        {
            _logger1 = loggerFactory.CreateLogger("MyCategory");
        }
    }
    

    配置日志

    默认模板中,日志的配置如下(在appsettings.{Environment}.json文件中):

    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      }
    }
    

    针对所有日志记录提供程序进行配置

    LogLevel,顾名思义,就是指要记录的日志的最低级别(即要记录大于等于该级别的日志),想必大家都不陌生。下方会详细介绍日志级别。

    LogLevel中的字段,如上面示例中的“Default”、“Microsoft”等,表示日志的类别,也就是咱们上面注入ILogger时指定的泛型参数。可以为每种类别设置记录的最小日志级别,也就是这些类别所对应的值。

    下面详细解释一下示例中的三种日志类别。

    Default

    默认情况下,如果分类没有进行特别配置(即没有在LogLevel中配置),则应用Default的配置。

    Microsoft

    所有分类以Microsoft开头的日志均应用Microsoft的配置。例如,Microsoft.AspNetCore.Routing.EndpointMiddleware类别的日志就会应用该配置。

    Microsoft.Hosting.Lifetime

    所有分类以Microsoft.Hosting.Lifetime开头的日志均应用Microsoft.Hosting.Lifetime的配置。例如,分类Microsoft.Hosting.Lifetime就会应用该配置,而不会应用Microsoft,因为Microsoft.Hosting.LifetimeMicrosoft更具体。

    OK,以上三种日志类别就说这些了。

    回到示例,你可能没有注意到,这里面没有针对某个日志记录提供程序进行单独配置(如:Console只记录Error及以上级别日志,而EventSource则需要记录记录所有级别日志)。像这种,如果没有针对特定的日志记录提供程序进行配置,则该配置将会应用到所有日志记录提供程序。

    Windows EventLog 除外。EventLog必须显式地进行配置,否则会使用其默认的LogLevel.Warning

    针对指定的日志记录提供程序进行配置

    接下来看一下如何针对指定的日志记录提供程序进行配置,先上示例:

    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        },
        "Console": {
          "LogLevel": {
            "Default": "Error"
          }
        },
        "Debug": {
          "LogLevel": {
            "Microsoft": "None"
          }
        },
        "EventSource": {
          "LogLevel": {
            "Default": "Trace",
            "Microsoft": "Trace",
            "Microsoft.Hosting.Lifetime": "Trace"
          }
        }
      }
    }
    

    就像appsettings.{Environment}.jsonappsettings.json之间的关系一样,Logging.{Provider}.LogLevel中的配置将会覆盖Logging.LogLevel中的配置。

    例如Logging.Console.LogLevel.Default将会覆盖Logging.LogLevel.DefaultConsole日志记录器将默认记录Error及其以上级别的日志。

    刚才提到了,Windows EventLog比较特殊,它不会继承Logging.LogLevel的配置。EventLog默认日志级别为LogLevel.Warning,如果想要修改,则必须显式进行指定,如:

    {
      "Logging": {
        "EventLog": {
          "LogLevel": {
            "Default": "Information"
          }
        }
      }
    }
    

    配置的筛选原理

    当创建ILogger<TCategoryName>的对象实例时,ILoggerFactory根据不同的日志记录提供程序,将会:

    1. 查找匹配该日志记录提供程序的配置。如果找不到,则使用通用配置。
    2. 然后匹配拥有最长前缀的配置类别。如果找不到,则使用Default配置。
    3. 如果匹配到了多条配置,则采用最后一条。
    4. 如果没有匹配到任何配置,则使用MinimumLevel,这是个配置项,默认是LogLevel.Information

    可以在ConfigureLogging扩展中使用SetMinimumLevel方法设置MinimumLevel

    Log Level

    日志级别指示了日志的严重程度,一共分为7等,从轻到重为(最后的None较为特殊):

    日志级别 描述
    Trace 0 追踪级别,包含最详细的信息。这些信息可能包含敏感数据,默认情况下是禁用的,并且绝不能出现在生产环境中。
    Debug 1 调试级别,用于开发人员开发和调试。信息量一般比较大,在生产环境中一定要慎用。
    Information 2 信息级别,该级别平时使用较多。
    Warning 3 警告级别,一些意外的事件,但这些事件并不对导致程序出错。
    Error 4 错误级别,一些无法处理的错误或异常,这些事件会导致当前操作或请求失败,但不会导致整个应用出错。
    Critical 5 致命错误级别,这些错误会导致整个应用出错。例如内存不足等。
    None 6 指示不记录任何日志

    日志记录提供程序

    Console

    日志将输出到控制台中。

    Debug

    日志将通过System.Diagnostics.Debug类进行输出,可以通过VS输出窗口查看。

    在 Linux 上,可以在/var/log/message/var/log/syslog下找到

    EventSource

    跨平台日志记录,在Windows上则使用 ETW

    Windows EventLog

    仅在Windows系统下生效,可通过“事件查看器”进行日志查看。

    默认情况下

    • LogName为“Application”
    • SourceName为“NET Runtime”
    • MachineName为本地计算机的名称。

    这些字段都可以通过EventLogSettings进行修改:

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging =>
            {
                logging.AddEventLog(settings =>
                {
                    settings.LogName = "My App";
                    settings.SourceName = "My Log";
                    settings.MachineName = "My Computer";
                })
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
    

    日志记录过滤器

    通过日志记录过滤器,允许你书写复杂的逻辑,来控制是否要记录日志。

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging =>
            {
                logging
                    // 针对所有 LoggerProvider 设置 Microsoft 最小日志级别,建议通过配置文件进行配置
                    .AddFilter("Microsoft", LogLevel.Trace)
                    // 针对 ConsoleLoggerProvider 设置 Microsoft 最小日志级别,建议通过配置文件进行配置
                    .AddFilter<ConsoleLoggerProvider>("Microsoft", LogLevel.Debug)
                    // 针对所有 LoggerProvider 进行过滤配置
                    .AddFilter((provider, category, logLevel) =>
                    {
                        // 由于下面单独针对 ConsoleLoggerProvider 添加了过滤配置,所以 ConsoleLoggerProvider 不会进入该方法
                    
                        if (provider == typeof(ConsoleLoggerProvider).FullName
                            && category == typeof(ValuesController).FullName
                            && logLevel <= LogLevel.Warning)
                        {
                            // false:不记录日志
                            return false;
                        }
    
                        // true:记录日志
                        return true;
                    })
                    // 针对 ConsoleLoggerProvider 进行过滤配置
                    .AddFilter<ConsoleLoggerProvider>((category, logLevel) =>
                    {
                        if (category == typeof(ValuesController).FullName
                            && logLevel <= LogLevel.Warning)
                        {
                            // false:不记录日志
                            return false;
                        }
    
                        // true:记录日志
                        return true;
                    });
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
    

    日志消息模版

    应用开发过程中,对于某一类的日志,我们希望它们的消息格式保持一致,仅仅是某些参数发生变化。这就要用到日志消息模板了。

    举个例子:

    [HttpGet("{id}")]
    public int Get(int id)
    {
        _logger.LogInformation("Get {Id}", id);
    
        return id;
    }
    

    其中Get {Id}就是一个日志消息模板,{Id}则是模板参数(注意,请在里面书写名称,而不是数字,这样更容易理解参数含义)。

    不过,需要注意的是,{Id}这个模板参数,仅仅是用于让人容易理解其含义的,和后面的参数名没有任何关系,模板值关心参数的顺序。例如:

    [HttpGet("{id}")]
    public int Get(int id)
    {
        _logger.LogInformation("Get {Id} at {Time}", DateTime.Now, id);
    
        return id;
    }
    

    假设传入id = 1,它的输出是:Get 11/02/2021 11:42:14 at 1

    日志消息模板是一项非常重要的功能,在众多开源日志中间件中,均有使用。

    主机构建期间的日志记录

    ASP.NET Core框架不直接支持在主机构建期间进行日志记录。但是可以通过独立的日志记录提供程序进行日志记录,例如,使用第三方日志记录提供程序:Serilog

    安装Nuget包:Install-Package Serilog.AspNetCore

    public static void Main(string[] args)
    {
        // 从appsettings.json和命令行参数中读取配置
        var config = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddCommandLine(args)
            .Build();
    
        // 创建Logger
        Log.Logger = new LoggerConfiguration()
            .WriteTo.Console()                          // 输出到控制台
            .WriteTo.File(config["Logging:File:Path"])  // 输出到指定文件
            .CreateLogger();
        try
        {
            CreateHostBuilder(args).Build().Run();
        }
        catch(Exception ex)
        {
            Log.Fatal(ex, "Host terminated unexpectedly");
    
            throw;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
    

    appsettings.json

    {
      "Logging": {
        "File": {
          "Path": "logs/host.log"
        }
      }
    }
    

    控制台日志格式配置

    控制台日志记录提供程序是我们开发过程中必不可少的,通过上面我们已经得知可以通过AddConsole()进行添加。不过它的局限性比较大,日志格式我们都无法进行自定义。

    因此,在.NET 5中,对控制台日志记录提供程序进行了扩展,预置了三种日志输出格式:Json、Simple、Systemd。

    实际上,之前也有枚举ConsoleLoggerFormat提供了Simple和Systemd格式,不过不能进行自定义,已经弃用了。

    这些 Formatter 均继承自抽象类ConsoleFormatter,该抽象类构造函数接收一个“名字”参数,要求其实现类必须拥有名字。你可以通过静态类ConsoleFormatterNames获取到内置的三种格式的名字。

    public abstract class ConsoleFormatter
    {
        protected ConsoleFormatter(string name)
        {
            Name = name ?? throw new ArgumentNullException(nameof(name));
        }
    
        public string Name { get; }
    
        public abstract void Write<TState>(in LogEntry<TState> logEntry, IExternalScopeProvider scopeProvider, TextWriter textWriter);
    }
    
    public static class ConsoleFormatterNames
    {
        public const string Simple = "simple";
    
        public const string Json = "json";
    
        public const string Systemd = "systemd";
    }
    

    你可以在使用AddConsole()时,配置ConsoleLoggerOptionsFormatterName属性,以达到自定义格式的目的,其默认值为“simple”。不过,为了方便使用,.NET 框架已经把内置的三种格式帮我们封装好了。

    这些 Formatter 的选项类均继承自选项类ConsoleFormatterOptions,该选项类包含以下三个属性:

    public class ConsoleFormatterOptions
    {
        // 启用作用域,默认 false
        public bool IncludeScopes { get; set; }
    
        // 设置时间戳的格式,显示在日志消息开头
        // 默认为 null,不展示时间戳
        public string TimestampFormat { get; set; }
    
        // 是否将时间戳时区设置为 UTC,默认是false,即本地时区
        public bool UseUtcTimestamp { get; set; }
    }
    

    SimpleConsoleFormatter

    通过扩展方法AddSimpleConsole()可以添加支持Simple格式的控制台日志记录提供程序,默认行为与AddConsole()一致。

    .ConfigureLogging(logging =>
    {
        logging.ClearProviders()
            .AddSimpleConsole();
    }
    

    示例输出:

    info: Microsoft.Hosting.Lifetime[0]
          Now listening on: http://localhost:5000
    info: Microsoft.Hosting.Lifetime[0]
          Application started. Press Ctrl+C to shut down.
    info: Microsoft.Hosting.Lifetime[0]
          Hosting environment: Development
    info: Microsoft.Hosting.Lifetime[0]
          Content root path: C:\Repos\WebApplication
    

    另外,你可以通过SimpleConsoleFormatterOptions进行一些自定义配置:

    .ConfigureLogging(logging =>
    {
        logging.ClearProviders()
            .AddSimpleConsole(options => 
            {
                // 一条日志消息展示在同一行
                options.SingleLine = true;
                options.IncludeScopes = true;
                options.TimestampFormat = "yyyy-MM-dd HH:mm:ss ";
                options.UseUtcTimestamp = false;
            });
    }
    
    

    示例输出:

    2021-11-02 15:53:33 info: Microsoft.Hosting.Lifetime[0] Now listening on: http://localhost:5000
    2021-11-02 15:53:33 info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down.
    2021-11-02 15:53:33 info: Microsoft.Hosting.Lifetime[0] Hosting environment: Development
    2021-11-02 15:53:33 info: Microsoft.Hosting.Lifetime[0] Content root path: C:\Repos\WebApplication
    

    SystemdConsoleFormatter

    通过扩展方法AddSystemdConsole()可以添加支持Systemd格式的控制台日志记录提供程序。如果你熟悉Linux,那你对它也一定不陌生。

    .ConfigureLogging(logging =>
    {
        logging.ClearProviders()
            .AddSystemdConsole();
    }
    

    示例输出:

    <6>Microsoft.Hosting.Lifetime[0] Now listening on: http://localhost:5000
    <6>Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down.
    <6>Microsoft.Hosting.Lifetime[0] Hosting environment: Development
    <6>Microsoft.Hosting.Lifetime[0] Content root path: C:\Repos\WebApplication
    

    前面的<6>表示日志级别info,如果你有兴趣了解Systemd,可以访问阮一峰老师的Systemd 入门教程:命令篇

    JsonConsoleFormatter

    通过扩展方法AddJsonConsole()可以添加支持Json格式的控制台日志记录提供程序。

    .ConfigureLogging(logging =>
    {
        logging.ClearProviders()
            .AddJsonConsole(options =>
            {
                options.JsonWriterOptions = new JsonWriterOptions
                {
                    // 启用缩进,看起来更舒服
                    Indented = true
                };
            });
    }
    

    示例输出:

    {
      "EventId": 0,
      "LogLevel": "Information",
      "Category": "Microsoft.Hosting.Lifetime",
      "Message": "Now listening on: http://localhost:5000",
      "State": {
        "Message": "Now listening on: http://localhost:5000",
        "address": "http://localhost:5000",
        "{OriginalFormat}": "Now listening on: {address}"
      }
    }
    {
      "EventId": 0,
      "LogLevel": "Information",
      "Category": "Microsoft.Hosting.Lifetime",
      "Message": "Application started. Press Ctrl\u002BC to shut down.",
      "State": {
        "Message": "Application started. Press Ctrl\u002BC to shut down.",
        "{OriginalFormat}": "Application started. Press Ctrl\u002BC to shut down."
      }
    }
    {
      "EventId": 0,
      "LogLevel": "Information",
      "Category": "Microsoft.Hosting.Lifetime",
      "Message": "Hosting environment: Development",
      "State": {
        "Message": "Hosting environment: Development",
        "envName": "Development",
        "{OriginalFormat}": "Hosting environment: {envName}"
      }
    }
    {
      "EventId": 0,
      "LogLevel": "Information",
      "Category": "Microsoft.Hosting.Lifetime",
      "Message": "Content root path: C:\\Repos\\WebApplication",
      "State": {
        "Message": "Content root path: C:\\Repos\\WebApplication",
        "contentRoot": "C:\\Repos\\WebApplication",
        "{OriginalFormat}": "Content root path: {contentRoot}"
      }
    }
    

    如果你同时添加了多种格式的控制台记录程序,那么只有最后一个添加的生效。

    以上介绍的是通过代码进行控制台日志记录提供程序的设置,不过我想大家应该更喜欢通过配置去设置日志记录提供程序。下面是一个简单地配置示例:

    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        },
        "Console": {
          "FormatterName": "json",
          "FormatterOptions": {
            "SingleLine": true,
            "IncludeScopes": true,
            "TimestampFormat": "yyyy-MM-dd HH:mm:ss ",
            "UseUtcTimestamp": false,
            "JsonWriterOptions": {
              "Indented": true
            }
          }
        }
      }
    }
    

    ILogger<TCategoryName>对象实例的创建

    讲到这里,不知道你会不会对ILogger<TCategoryName>对象实例的创建有疑惑:它到底是如何被new出来的呢?

    要解决这个问题,我们先从AddLogging()扩展方法入手:

    public static class LoggingServiceCollectionExtensions
    {
        public static IServiceCollection AddLogging(this IServiceCollection services)
        {
            return AddLogging(services, builder => { });
        }
    
        public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure)
        {
            services.AddOptions();
    
            // 注册单例 ILoggerFactory
            services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
            // 注册单例 ILogger<>
            services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));
    
            // 批量注册单例 IConfigureOptions<LoggerFilterOptions>
            services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<LoggerFilterOptions>>(
                new DefaultLoggerLevelConfigureOptions(LogLevel.Information)));
    
            configure(new LoggingBuilder(services));
            return services;
        }
    }
    

    你可能也猜到了,这个Logger<>不会是LoggerFactory创建的吧?要不然注册个这玩意干嘛呢?

    别着急,咱们接着先查看ILogger<>服务的实现类Logger<>

    public interface ILogger
    {
        void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);
    
        // 检查能否记录该日志等级的日志
        bool IsEnabled(LogLevel logLevel);
    
        IDisposable BeginScope<TState>(TState state);
    }
    
    public interface ILogger<out TCategoryName> : ILogger
    {
    }
        
    public class Logger<T> : ILogger<T>
    {
        // 接口实现内部均是使用该实例进行操作
        private readonly ILogger _logger;
    
        // 果不其然,注入了 ILoggerFactory 实例
        public Logger(ILoggerFactory factory)
        {
            // 还记得吗?上面提到显式指定日志类别时,也是这样创建 ILogger 实例的
            _logger = factory.CreateLogger(TypeNameHelper.GetTypeDisplayName(typeof(T), includeGenericParameters: false, nestedTypeDelimiter: '.'));
        }
        
        // ...
    }
    

    没错,你猜对了,那就来看看这个LoggerFactory吧(只列举核心代码):

    public interface ILoggerFactory : IDisposable
    {
        ILogger CreateLogger(string categoryName);
    
        void AddProvider(ILoggerProvider provider);
    }
    
    public class LoggerFactory : ILoggerFactory
    {
        // 用于单例化 Logger<>
        private readonly Dictionary<string, Logger> _loggers = new Dictionary<string, Logger>(StringComparer.Ordinal);
        // 存放 ILoggerProviderRegistrations
        private readonly List<ProviderRegistration> _providerRegistrations = new List<ProviderRegistration>();
        private readonly object _sync = new object();
    
        public LoggerFactory(IEnumerable<ILoggerProvider> providers, IOptionsMonitor<LoggerFilterOptions> filterOption, IOptions<LoggerFactoryOptions> options = null)
        {
            // ...
    
            // 注册 ILoggerProviders
            foreach (ILoggerProvider provider in providers)
            {
                AddProviderRegistration(provider, dispose: false);
            }
    
            // ...
        }
    
        public ILogger CreateLogger(string categoryName)
        {
            lock (_sync)
            {
                // 如果不存在,则 new
                if (!_loggers.TryGetValue(categoryName, out Logger logger))
                {
                    logger = new Logger
                    {
                        Loggers = CreateLoggers(categoryName),
                    };
    
                    (logger.MessageLoggers, logger.ScopeLoggers) = ApplyFilters(logger.Loggers);
    
                    // 单例化 Logger<>
                    _loggers[categoryName] = logger;
                }
    
                return logger;
            }
        }
        
        private void AddProviderRegistration(ILoggerProvider provider, bool dispose)
        {
            _providerRegistrations.Add(new ProviderRegistration
            {
                Provider = provider,
                ShouldDispose = dispose
            });
            
            // ...
        }
        
        private LoggerInformation[] CreateLoggers(string categoryName)
        {
            var loggers = new LoggerInformation[_providerRegistrations.Count];
            // 循环遍历所有 ILoggerProvider
            for (int i = 0; i < _providerRegistrations.Count; i++)
            {
                loggers[i] = new LoggerInformation(_providerRegistrations[i].Provider, categoryName);
            }
            return loggers;
        }
    }
    

    注意

    • 若要在Startup.Configure方法中记录日志,直接在参数上注入ILogger<Startup>即可。
    • 不支持在Startup.ConfigureServices方法中使用ILogger,因为此时DI容器还未配置完成。
    • 没有异步的日志记录方法。日志记录动作执行应该很快,不值的牺牲性能使用异步方法。如果日志记录动作比较耗时,如记录到MSSQL中,那么请不要直接写入MSSQL。你应该考虑先将日志写入到快速存储介质,如内存队列,然后通过后台工作线程将其从内存转储到MSSQL中。
    • 无法使用日志记录 API 在应用运行时更改日志记录配置。不过,一些配置提供程序(如文件配置提供程序)可重新加载配置,这可以立即更新日志记录配置。

    小结

    • Host.CreateDefaultBuilder方法中,默认添加了ConsoleDebugEventSourceEventLog(仅Windows)共四种日志记录提供程序(Logger Provider)。
    • 通过注入服务ILogger<TCategoryName>,可以方便的进行日志记录。
    • 可以通过代码或配置对日志记录提供程序进行设置,如LogLevelFormatterName等。
    • 可以通过扩展方法AddFilter添加日志记录过滤器,允许你书写复杂的逻辑,来控制是否要记录日志。
    • 支持日志消息模板。
    • 对于控制台记录日志程序,.NET框架内置了Simple(默认)、SystemdJson三种日志输出格式。
    • .NET 6 预览版中新增了一个称为“编译时日志记录源生成”的功能,该功能非常实用,有兴趣的可以先去了解一下
    • 最后,给大家列举一些常用的日志开源中间件:
  • 相关阅读:
    阿里早期Android加固代码的实现分析
    如何利用C++的time头文件获取系统时间
    Python编写基于socket的非阻塞多人聊天室程序(单线程&多线程)
    Dalvik模式下在Android so库文件.init段、.init_array段构造函数上下断点
    手动绕过百度加固Debug.isDebuggerConnected反调试的方法
    request使用代理
    requests爬取豆瓣热门电视剧
    scrapy-继承默认的user-agent 中间件
    scrapy-下载器中间件 随机切换user_agent
    scrapy 直接在编辑器运行
  • 原文地址:https://www.cnblogs.com/xiaoxiaotank/p/15525052.html
Copyright © 2020-2023  润新知