理解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 預覽版中新增了一個稱為“編譯時日志記錄源生成”的功能,該功能非常實用,有興趣的可以先去了解一下
  • 最后,給大家列舉一些常用的日志開源中間件:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM