.Net Core日志記錄之日志配置
一、前言
在項(xiàng)目的開發(fā)維護(hù)階段,有時(shí)候我們關(guān)注的問題不僅僅在于功能的實(shí)現(xiàn),甚至需要關(guān)注系統(tǒng)發(fā)布上線后遇到的問題能否及時(shí)的查找并解決。所以我們需要有一個(gè)好的解決方案來及時(shí)的定位錯(cuò)誤的根源并做出正確及時(shí)的修復(fù),這樣才能不影響系統(tǒng)正常的運(yùn)行狀態(tài)。
這個(gè)時(shí)候我們發(fā)現(xiàn),其實(shí)在asp.net core中已經(jīng)內(nèi)置了日志系統(tǒng),并提供了各種內(nèi)置和第三方日志記錄提供程序的日志記錄接口,在進(jìn)行應(yīng)用開發(fā)中,可以進(jìn)行統(tǒng)一配置,并且利用第三方日志框架相結(jié)合,更加有效的實(shí)現(xiàn)日志記錄。所以在這個(gè)系列中,主要是對(duì)內(nèi)置日志記錄系統(tǒng)的學(xué)習(xí),以及后續(xù)使用第三方日志框架集成我們需要的日志系統(tǒng)。
二、說明
在這一篇中主要是對(duì)日志記錄的配置進(jìn)行說明,從開始配置日志,以及后續(xù)使用配置進(jìn)行日志處理。
在新建項(xiàng)目成功之后,我們都會(huì)看到一個(gè)命名為appsettings.json配置,打開一看,短短的幾行配置,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},然后啟動(dòng)運(yùn)行的時(shí)候,程序會(huì)在調(diào)試面板和控制臺(tái)中分別輸出顯示來源如下:
在控制臺(tái)中:

在調(diào)試面板中:

這里的日志配置,在系統(tǒng)中到底都起來什么作用?讓我們來一探究竟吧!
三、開始
3.1 默認(rèn)配置
我們查看源代碼發(fā)現(xiàn),在程序的入口點(diǎn)中發(fā)現(xiàn),在初始化時(shí)候,通過CreateDefaultBuilder方法來實(shí)現(xiàn)日志記錄的默認(rèn)配置。
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在源碼中都對(duì)日志做了哪些默認(rèn)配置?
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;
}通過上面這一段源碼我們可以看到一個(gè)命名為ConfigureLogging的對(duì)象,我們根據(jù)命名的意思大致可以看出,這是一個(gè)配置日志的方法,繼續(xù)查看ConfigureLogging源碼
public static IHostBuilder ConfigureLogging(this IHostBuilder hostBuilder, Action<HostBuilderContext, ILoggingBuilder> configureLogging)
{
return hostBuilder.ConfigureServices((context, collection) => collection.AddLogging(builder => configureLogging(context, builder)));
}通過IServiceCollection注冊(cè)服務(wù)集合容器,將日志服務(wù)添加到這個(gè)服務(wù)容器,使用AddLogging方法實(shí)現(xiàn)對(duì)日志服務(wù)的注冊(cè)。
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添加到服務(wù)集合容器,先通過添加所需的配置AddOptions,通過注入的方式實(shí)現(xiàn)默認(rèn)的ILoggerFactory,ILogger ( 這個(gè)會(huì)在后續(xù)的篇章中進(jìn)行說明),再后通過LoggingBuilder完成日志對(duì)象的創(chuàng)建,
public interface ILoggingBuilder
{
IServiceCollection Services { get; }
}
internal class LoggingBuilder : ILoggingBuilder
{
public LoggingBuilder(IServiceCollection services)
{
Services = services;
}
public IServiceCollection Services { get; }
}對(duì)日志系統(tǒng)的配置,用于提供程序的接口,ILoggingBuilder后面可以對(duì)該對(duì)象進(jìn)行拓展使用。
通過以上的流程CreateDefaultBuilder方法,實(shí)現(xiàn)對(duì)預(yù)先配置的默認(rèn)值初始化,因此也發(fā)現(xiàn)了其中的ConfigureLogging也是其中要進(jìn)行默認(rèn)初始化的值,也就是系統(tǒng)默認(rèn)的日志配置。
單獨(dú)把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啟動(dòng)中,根據(jù)操作系統(tǒng)平臺(tái)適應(yīng)不同的服務(wù),在windows服務(wù)中,將EventLogLoggerProvider的默認(rèn)值設(shè)置為警告或者更高的級(jí)別。
AddConfiguration : 添加系統(tǒng)日志的全局配置。
在配置中,可以根據(jù)提供的不同類型程序來針對(duì)實(shí)現(xiàn)日志記錄的輸出方式。而這里默認(rèn)實(shí)現(xiàn)的AddConsole()、AddDebug() 和AddEventSourceLogger()分別是將日志輸出到控制臺(tái)、調(diào)試窗口中,以及提供寫入事件源。
AddConsole : 添加控制臺(tái)到工廠方法中,用來將日志記錄到控制臺(tái)中。
AddDebug : 添加Debug窗口到工廠方法中,用來將日志記錄到窗口中。
說明:asp.net core 內(nèi)置的日志接口中,實(shí)現(xiàn)了多種內(nèi)置的日志提供器,除了上面默認(rèn)實(shí)現(xiàn)的
Console、Debug和EventSource,還包括下面的這幾個(gè)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數(shù)據(jù),實(shí)現(xiàn)全局配置,
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實(shí)現(xiàn)的整體流程源碼,默認(rèn)注冊(cè)實(shí)現(xiàn)LoggerFilterConfigureOptions對(duì)配置數(shù)據(jù)的讀取,其中定義的 LogLevelKey = "LogLevel" 、DefaultCategory = "Default" 默認(rèn)字符串,以此來獲取默認(rèn)全局配置數(shù)據(jù)。
在默認(rèn)配置的文本格式appsettings.json中,Logging屬性可以具有LogLevel和日志提供程序?qū)傩浴?code>Logging 下的 LogLevel 屬性指定了用于記錄所選類別的最低級(jí)別。在本例中, Microsoft 類別在 Information 級(jí)別記錄,其他均在 Debug 級(jí)別記錄。
日志級(jí)別說明:每一個(gè)日志都有指定的日志級(jí)別值,日志級(jí)別判斷指示嚴(yán)重性或重要性。使用日志等級(jí)可以很好的過濾想要的日志,記錄日志記錄問題的同時(shí),甚至為我們提供非常詳細(xì)的日志信息。
LogLevel 嚴(yán)重性:Trace < Debug < Information < Warning < Error < Critical < None。
| 日志級(jí)別 | 常用場(chǎng)景 |
|---|---|
| Trace = 0 | 記錄一些對(duì)程序員調(diào)試問題有幫助的信息, 其中可能包含一些敏感信息, 所以應(yīng)該避免在 生產(chǎn)環(huán)境中啟用Trace日志,因此不應(yīng)該用于生產(chǎn)環(huán)境。默認(rèn)應(yīng)禁用。 |
| Debug = 1 | 記錄一些在開發(fā)和調(diào)試階段有用的短時(shí)變 量(Short-term usefulness), 所以除非為了臨時(shí)排除生產(chǎn)環(huán)境的 故障,開發(fā)人員應(yīng)該盡量避免在生產(chǎn)環(huán)境中啟用Debug日志,默認(rèn)情況下這是最詳細(xì)的日志。 |
| Information = 2 | 記錄跟蹤應(yīng)用程序的一些流程, 例如,記錄當(dāng)前api請(qǐng)求的url。 |
| Warning = 3 | 記錄應(yīng)用程序中發(fā)生出現(xiàn)錯(cuò)誤或其它導(dǎo)致程序停止的流程異常信息。 這些信息中可能包含錯(cuò)誤消息或者錯(cuò)誤產(chǎn)生的條件, 可供后續(xù)調(diào)查,例如, 文件未找到 |
| Error = 4 | 記錄應(yīng)用程序中某個(gè)操作產(chǎn)生的錯(cuò)誤和異常信息。這些消息應(yīng)該指明當(dāng)前活動(dòng)或操作(比如當(dāng)前的 HTTP 請(qǐng)求),而不是應(yīng)用程序范圍的故障。 |
| Critical = 5 | 記錄一些需要立刻修復(fù),急需被關(guān)注的問題,應(yīng)當(dāng)記錄關(guān)鍵級(jí)別的日志。例如數(shù)據(jù)丟失,磁盤空間不足等。 |
日志級(jí)別只需要簡單的通過
AddFilter對(duì)日志的過濾級(jí)別配置一下就行了。同時(shí)也可以通過自定義在在
Logging.{providername}.LogLevel中指定了級(jí)別,則這些級(jí)別將重寫Logging.LogLevel中設(shè)置的所有內(nèi)容。(在下文自定義中說明)
由此可以看出,日志記錄提供程序配置由一個(gè)或多個(gè)配置提供程序提供,如文件格式(系統(tǒng)自帶的appsettings.json)或者通過(已安裝或已創(chuàng)建的)自定義提供程序(下文會(huì)說明自定義方式)。
3.2 自定義配置
看完了上面實(shí)現(xiàn)的默認(rèn)配置之后,我們也清楚了可以修改默認(rèn)配置實(shí)現(xiàn)不同等級(jí)日志的輸出,因此,我們也可以通過自定義的方式,對(duì)默認(rèn)配置的修改,實(shí)現(xiàn)我們想要的日志記錄方式。
可以通過自行選擇添加提供程序來替換默認(rèn)配置的提供的程序。這樣就實(shí)現(xiàn)自定義。自定義的方式有很多,比如
3.2.1 代碼添加提供程序
調(diào)用ClearProviders,清除默認(rèn)之后,可添加所需的提供程序。如下:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args) //可以看出在使用模板創(chuàng)建項(xiàng)目的時(shí)候,默認(rèn)添加了控制臺(tái)和調(diào)試日志組件,并從appsettings.json中讀取配置。
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders(); //去掉默認(rèn)添加的日志提供程序
//添加控制臺(tái)輸出
logging.AddConsole();
//添加調(diào)試輸出
logging.AddDebug();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}由上可以發(fā)現(xiàn)我們可以通過在入口程序中直接對(duì)添加ConfigureLogging(在上文中源碼可以看出)拓展方法來實(shí)現(xiàn)我們的自定義配置。
3.2.2 代碼添加過濾器
過濾器AddFilter,添加過濾規(guī)則,可以為不同的日志提供者指定不同的過濾器,實(shí)現(xiàn)有效的自定義日志的輸出。如下代碼:
.ConfigureLogging(logging =>
logging.AddFilter("System", LogLevel.Debug)
.AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Trace))添加指定了全局的過濾器,作用于所有日志提供者,示例中的第二個(gè) AddFilter 使用類型名稱來指定調(diào)試提供程序。 第一個(gè) AddFilter 應(yīng)用于全部提供程序,因?yàn)樗粗付ㄌ峁┏绦蝾愋汀?/p>
這里的AddFilter其實(shí)于之前讀取配置文件信息添加配置AddConfiguration的作用相似,只是從配置文件的邏輯改成了以代碼的方式實(shí)現(xiàn)過濾篩選,到最終也是對(duì)ConfigureOptions 的配置。
3.2.3 配置文件自定義
ASP.NET Core默認(rèn)會(huì)從appSetting.json中的Logging屬性讀取日志的配置(當(dāng)然你也可以從其他文件中讀取配置),這里設(shè)置了不同的日志提供器產(chǎn)生的最低的日志級(jí)別,配置樣例如下。
{
"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 將創(chuàng)建 6 條篩選規(guī)則:Debug中1 條用于調(diào)試提供程序,Console中 4 條用于控制臺(tái)提供程序,最后一條LogLevel 用于所有提供程序。 創(chuàng)建 ILogger 對(duì)象時(shí),為每個(gè)提供程序選擇一個(gè)規(guī)則。
四、問題
雖然在這一節(jié)中只是對(duì)日志記錄的配置進(jìn)行了說明,但是在后續(xù)中也會(huì)對(duì)日志內(nèi)部的核心運(yùn)行機(jī)制進(jìn)行說明介紹。所以,在這一篇中留下幾個(gè)疑問
- 日志記錄的輸出可以在哪里查看?而又由什么實(shí)現(xiàn)決定的呢?
- 如何管理輸出不同的日志呢?都有哪些方式呢?
以上的這些內(nèi)容,會(huì)在下一篇進(jìn)行介紹說明。
好了,今天的日志配置內(nèi)容就說到這里了,希望能給大家在使用Core開發(fā)項(xiàng)目中對(duì)日志系統(tǒng)有進(jìn)一步的認(rèn)識(shí)。
五、總結(jié)
- 本篇主要是對(duì)net core3.1中內(nèi)置的系統(tǒng)日志進(jìn)行配置使用,不管是基于默認(rèn)配置的輸出方式,還是自定義形式的配置,都是為了有效的輸出日志記錄,便于我們查找發(fā)現(xiàn)問題。
- 關(guān)于日志配置,其實(shí)都是在對(duì)
ConfigureOptions的配置,只是在形式上是直接讀取配置文件或通過代碼的方式實(shí)現(xiàn)自定義來實(shí)現(xiàn)日志配置。 - 后續(xù)會(huì)對(duì)內(nèi)置的日志系統(tǒng)進(jìn)一步說明,以及內(nèi)部運(yùn)行的主要核心機(jī)制。
- 如果有不對(duì)的或不理解的地方,希望大家可以多多指正,提出問題,一起討論,不斷學(xué)習(xí),共同進(jìn)步。
- 官方源碼 和 參考資料
到此這篇關(guān)于.Net Core日志記錄之日志配置的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
MVC+EasyUI+三層新聞網(wǎng)站建立 詳情頁面制作方法(八)
這篇文章主要為大家詳細(xì)介紹了MVC+EasyUI+三層新聞網(wǎng)站建立的第八篇,教大家如何制作詳情頁面,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
asp.net core 獲取 MacAddress 地址方法示例
這篇文章主要介紹了asp.net core獲取MacAddress地址方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-02-02
.net+FusionChart實(shí)現(xiàn)動(dòng)態(tài)顯示的柱狀圖和餅狀圖
這篇文章介紹了.net+FusionChart實(shí)現(xiàn)動(dòng)態(tài)顯示柱狀圖和餅狀圖的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07
MVC+EasyUI+三層新聞網(wǎng)站建立 驗(yàn)證碼生成(三)
這篇文章主要為大家詳細(xì)介紹了MVC+EasyUI+三層新聞網(wǎng)站建立的第三篇,教大家如何生成驗(yàn)證碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
ASP.NET MVC使用EasyUI的datagrid多選提交保存教程
ASP.NET MVC使用EasyUI的datagrid多選提交保存教程,需要的朋友可以參考下。2011-12-12
Asp.Net程序目錄下文件夾或文件操作導(dǎo)致Session失效的解決方案
這篇文章主要介紹了Asp.Net程序目錄下文件夾或文件操作導(dǎo)致Session失效的解決方案,需要的朋友可以參考下2017-06-06
SQL Server數(shù)據(jù)庫連接 Web.config如何配置
以下的文章主要描述的是Web.config正確配置SQL Server數(shù)據(jù)庫連接的實(shí)際擦步驟。我們以圖文結(jié)合的方式對(duì)其有個(gè)更好的說明,需要的朋友可以參考下2015-10-10
document.getElementsByName和document.getElementById 在IE與FF中不同
今天在<asp:radiobuttonlist/>中使用教本的的時(shí)候才注意到原來 document.getElementsByName 、document.getElementById 在IE與FF中有著不同實(shí)現(xiàn)。2008-12-12

