在項目的開發維護階段,有時候咱們關注的問題不只僅在於功能的實現,甚至須要關注系統發佈上線後遇到的問題可否及時的查找並解決。因此咱們須要有一個好的解決方案來及時的定位錯誤的根源並作出正確及時的修復,這樣才能不影響系統正常的運行狀態。git
這個時候咱們發現,其實在asp.net core中已經內置了日誌系統,並提供了各類內置和第三方日誌記錄提供程序的日誌記錄接口,在進行應用開發中,能夠進行統一配置,而且利用第三方日誌框架相結合,更加有效的實現日誌記錄。因此在這個系列中,主要是對內置日誌記錄系統的學習,以及後續使用第三方日誌框架集成咱們須要的日誌系統。程序員
在這一篇中主要是對日誌記錄的配置進行說明,從開始配置日誌,以及後續使用配置進行日誌處理。github
在新建項目成功以後,咱們都會看到一個命名爲appsettings.json
配置,打開一看,短短的幾行配置,web
"Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } },
而後啓動運行的時候,程序會在調試面板和控制檯中分別輸出顯示來源以下:json
在控制檯中:c#
在調試面板中:windows
這裏的日誌配置,在系統中到底都起來什麼做用?讓咱們來一探究竟吧!api
咱們查看源代碼發現,在程序的入口點中發現,在初始化時候,經過CreateDefaultBuilder
方法來實現日誌記錄的默認配置。app
public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }
因此下面咱們看一下CreateDefaultBuilder
在源碼中都對日誌作了哪些默認配置?框架
public static IHostBuilder CreateDefaultBuilder(string[] args) { var builder = new HostBuilder(); builder.UseContentRoot(Directory.GetCurrentDirectory()); builder.ConfigureHostConfiguration(config => { config.AddEnvironmentVariables(prefix: "DOTNET_"); if (args != null) { config.AddCommandLine(args); } }); builder.ConfigureAppConfiguration((hostingContext, config) => { var env = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName)) { var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); if (appAssembly != null) { config.AddUserSecrets(appAssembly, optional: true); } } config.AddEnvironmentVariables(); if (args != null) { config.AddCommandLine(args); } }) .ConfigureLogging((hostingContext, logging) => { var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); if (isWindows) { logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning); } logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); logging.AddEventSourceLogger(); if (isWindows) { logging.AddEventLog(); } }) .UseDefaultServiceProvider((context, options) => { var isDevelopment = context.HostingEnvironment.IsDevelopment(); options.ValidateScopes = isDevelopment; options.ValidateOnBuild = isDevelopment; }); return builder; }
經過上面這一段源碼咱們能夠看到一個命名爲ConfigureLogging
的對象,咱們根據命名的意思大體能夠看出,這是一個配置日誌的方法,繼續查看ConfigureLogging
源碼
public static IHostBuilder ConfigureLogging(this IHostBuilder hostBuilder, Action<HostBuilderContext, ILoggingBuilder> configureLogging) { return hostBuilder.ConfigureServices((context, collection) => collection.AddLogging(builder => configureLogging(context, builder))); }
經過IServiceCollection
註冊服務集合容器,將日誌服務添加到這個服務容器,使用AddLogging
方法實現對日誌服務的註冊。
public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure) { if (services == null) { throw new ArgumentNullException(nameof(services)); } services.AddOptions(); services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>()); services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>))); services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<LoggerFilterOptions>>( new DefaultLoggerLevelConfigureOptions(LogLevel.Information))); configure(new LoggingBuilder(services)); return services; }
經過AddLogging
添加到服務集合容器,先經過添加所需的配置AddOptions
,經過注入的方式實現默認的ILoggerFactory
,ILogger
( 這個會在後續的篇章中進行說明),再後經過LoggingBuilder
完成日誌對象的建立,
public interface ILoggingBuilder { IServiceCollection Services { get; } } internal class LoggingBuilder : ILoggingBuilder { public LoggingBuilder(IServiceCollection services) { Services = services; } public IServiceCollection Services { get; } }
對日誌系統的配置,用於提供程序的接口,ILoggingBuilder
後面能夠對該對象進行拓展使用。
經過以上的流程CreateDefaultBuilder
方法,實現對預先配置的默認值初始化,所以也發現了其中的ConfigureLogging
也是其中要進行默認初始化的值,也就是系統默認的日誌配置。
單獨把ConfigureLogging
這一塊的源碼拎出來再看看:
.ConfigureLogging((hostingContext, logging) => { var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); if (isWindows) { logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning); } logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); logging.AddEventSourceLogger(); if (isWindows) { logging.AddEventLog(); } })
在asp.net core啓動中,根據操做系統平臺適應不一樣的服務,在windows服務中,將EventLogLoggerProvider
的默認值設置爲警告或者更高的級別。
AddConfiguration : 添加系統日誌的全局配置。
在配置中,能夠根據提供的不一樣類型程序來針對實現日誌記錄的輸出方式。而這裏默認實現的AddConsole()
、AddDebug
() 和AddEventSourceLogger()
分別是將日誌輸出到控制檯、調試窗口中,以及提供寫入事件源。
AddConsole : 添加控制檯到工廠方法中,用來將日誌記錄到控制檯中。
AddDebug : 添加Debug窗口到工廠方法中,用來將日誌記錄到窗口中。
說明:asp.net core 內置的日誌接口中,實現了多種內置的日誌提供器,除了上面默認實現的
Console
、Debug
和EventSource
,還包括下面的這幾個EventLog :
TraceSource
AzureAppServicesFile
AzureAppServicesBlob
ApplicationInsights
還記得上面提到的appsettings.json
配置嗎?在這裏,咱們來看看
{ "Logging": { "LogLevel": { "Default": "Debug", "Microsoft": "Information" }, "Console": { "LogLevel": { "Default": "Debug", "System": "Warning" } } } }
在AddConfiguration
中,
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
獲取配置文件的Logging
數據,實現全局配置,
public static ILoggingBuilder AddConfiguration(this ILoggingBuilder builder, IConfiguration configuration) { builder.AddConfiguration(); builder.Services.AddSingleton<IConfigureOptions<LoggerFilterOptions>>(new LoggerFilterConfigureOptions(configuration)); builder.Services.AddSingleton<IOptionsChangeTokenSource<LoggerFilterOptions>>(new ConfigurationChangeTokenSource<LoggerFilterOptions>(configuration)); builder.Services.AddSingleton(new LoggingConfiguration(configuration)); return builder; } internal class LoggerFilterConfigureOptions : IConfigureOptions<LoggerFilterOptions> { private const string LogLevelKey = "LogLevel"; private const string DefaultCategory = "Default"; private readonly IConfiguration _configuration; public LoggerFilterConfigureOptions(IConfiguration configuration) { _configuration = configuration; } public void Configure(LoggerFilterOptions options) { LoadDefaultConfigValues(options); } private void LoadDefaultConfigValues(LoggerFilterOptions options) { if (_configuration == null) { return; } options.CaptureScopes = _configuration.GetValue(nameof(options.CaptureScopes), options.CaptureScopes); foreach (var configurationSection in _configuration.GetChildren()) { if (configurationSection.Key.Equals(LogLevelKey, StringComparison.OrdinalIgnoreCase)) { // Load global category defaults LoadRules(options, configurationSection, null); } else { var logLevelSection = configurationSection.GetSection(LogLevelKey); if (logLevelSection != null) { // Load logger specific rules var logger = configurationSection.Key; LoadRules(options, logLevelSection, logger); } } } } private void LoadRules(LoggerFilterOptions options, IConfigurationSection configurationSection, string logger) { foreach (var section in configurationSection.AsEnumerable(true)) { if (TryGetSwitch(section.Value, out var level)) { var category = section.Key; if (category.Equals(DefaultCategory, StringComparison.OrdinalIgnoreCase)) { category = null; } var newRule = new LoggerFilterRule(logger, category, level, null); options.Rules.Add(newRule); } } } }
以上是AddConfiguration
實現的總體流程源碼,默認註冊實現LoggerFilterConfigureOptions
對配置數據的讀取,其中定義的 LogLevelKey = "LogLevel" 、DefaultCategory = "Default" 默認字符串,以此來獲取默認全局配置數據。
在默認配置的文本格式appsettings.json
中,Logging
屬性能夠具備LogLevel
和日誌提供程序屬性。Logging
下的 LogLevel
屬性指定了用於記錄所選類別的最低級別。在本例中, Microsoft
類別在 Information
級別記錄,其餘均在 Debug
級別記錄。
日誌級別說明:每個日誌都有指定的日誌級別值,日誌級別判斷指示嚴重性或重要性。使用日誌等級能夠很好的過濾想要的日誌,記錄日誌記錄問題的同時,甚至爲咱們提供很是詳細的日誌信息。
LogLevel 嚴重性:Trace < Debug < Information < Warning < Error < Critical < None。
日誌級別 經常使用場景 Trace = 0 記錄一些對程序員調試問題有幫助的信息, 其中可能包含一些敏感信息, 因此應該避免在 生產環境中啓用Trace日誌,所以不該該用於生產環境。默認應禁用。 Debug = 1 記錄一些在開發和調試階段有用的短時變 量(Short-term usefulness), 因此除非爲了臨時排除生產環境的 故障,開發人員應該儘可能避免在生產環境中啓用Debug日誌,默認狀況下這是最詳細的日誌。 Information = 2 記錄跟蹤應用程序的一些流程, 例如,記錄當前api請求的url。 Warning = 3 記錄應用程序中發生出現錯誤或其它致使程序中止的流程異常信息。 這些信息中可能包含錯誤消息或者錯誤產生的條件, 可供後續調查,例如, 文件未找到 Error = 4 記錄應用程序中某個操做產生的錯誤和異常信息。這些消息應該指明當前活動或操做(好比當前的 HTTP 請求),而不是應用程序範圍的故障。 Critical = 5 記錄一些須要馬上修復,急需被關注的問題,應當記錄關鍵級別的日誌。例如數據丟失,磁盤空間不足等。 日誌級別只須要簡單的經過
AddFilter
對日誌的過濾級別配置一下就好了。同時也能夠經過自定義在在
Logging.{providername}.LogLevel
中指定了級別,則這些級別將重寫Logging.LogLevel
中設置的全部內容。(在下文自定義中說明)
由此能夠看出,日誌記錄提供程序配置由一個或多個配置提供程序提供,如文件格式(系統自帶的appsettings.json
)或者經過(已安裝或已建立的)自定義提供程序(下文會說明自定義方式)。
看完了上面實現的默認配置以後,咱們也清楚了能夠修改默認配置實現不一樣等級日誌的輸出,所以,咱們也能夠經過自定義的方式,對默認配置的修改,實現咱們想要的日誌記錄方式。
能夠經過自行選擇添加提供程序來替換默認配置的提供的程序。這樣就實現自定義。自定義的方式有不少,好比
調用ClearProviders
,清除默認以後,可添加所需的提供程序。以下:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) //能夠看出在使用模板建立項目的時候,默認添加了控制檯和調試日誌組件,並從appsettings.json中讀取配置。 .ConfigureLogging((hostingContext, logging) => { logging.ClearProviders(); //去掉默認添加的日誌提供程序 //添加控制檯輸出 logging.AddConsole(); //添加調試輸出 logging.AddDebug(); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }
由上能夠發現咱們能夠經過在入口程序中直接對添加ConfigureLogging
(在上文中源碼能夠看出)拓展方法來實現咱們的自定義配置。
過濾器AddFilter
,添加過濾規則,能夠爲不一樣的日誌提供者指定不一樣的過濾器,實現有效的自定義日誌的輸出。以下代碼:
.ConfigureLogging(logging => logging.AddFilter("System", LogLevel.Debug) .AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Trace))
添加指定了全局的過濾器,做用於全部日誌提供者,示例中的第二個 AddFilter
使用類型名稱來指定調試提供程序。 第一個 AddFilter
應用於所有提供程序,由於它未指定提供程序類型。
這裏的AddFilter
其實於以前讀取配置文件信息添加配置AddConfiguration
的做用類似,只是從配置文件的邏輯改爲了以代碼的方式實現過濾篩選,到最終也是對ConfigureOptions
的配置。
ASP.NET Core默認會從appSetting.json中的Logging屬性讀取日誌的配置(固然你也能夠從其餘文件中讀取配置),這裏設置了不一樣的日誌提供器產生的最低的日誌級別,配置樣例以下。
{ "Logging": { "Debug": { "LogLevel": { "Default": "Information" } }, "Console": { "LogLevel": { "Microsoft.AspNetCore.Mvc.Razor.Internal": "Warning", "Microsoft.AspNetCore.Mvc.Razor.Razor": "Debug", "Microsoft.AspNetCore.Mvc.Razor": "Error", "Default": "Information" } }, "LogLevel": { "Default": "Debug" } } }
此 JSON 將建立 6 條篩選規則:Debug
中1 條用於調試提供程序,Console
中 4 條用於控制檯提供程序,最後一條LogLevel
用於全部提供程序。 建立 ILogger
對象時,爲每一個提供程序選擇一個規則。
雖然在這一節中只是對日誌記錄的配置進行了說明,可是在後續中也會對日誌內部的核心運行機制進行說明介紹。因此,在這一篇中留下幾個疑問
以上的這些內容,會在下一篇進行介紹說明。
好了,今天的日誌配置內容就說到這裏了,但願能給你們在使用Core開發項目中對日誌系統有進一步的認識。