ASP.NET Core 源码学习之Logging[1]:Introduction
在ASP.NET 4.X中,我們通常使用 log4net, NLog 等來記錄日志,但是當我們引用的一些第三方類庫使用不同的日志框架時,就比較混亂了。而在 ASP.Net Core 中內置了日志系統,并提供了一個統一的日志接口,ASP.Net Core 系統以及其它第三方類庫等都使用這個日志接口來記錄日志,而不關注日志的具體實現,這樣便可以在我們的應用程序中進行統一的配置,并能很好的與第三方日志框架集成。
注冊日志服務
ASP.NET Core 全部使用依賴注入,更好的規范我們的代碼。想要使用日志系統,首先要進行注冊和配置:
public void ConfigureServices(IServiceCollection services) ?{services.AddLogging(builder =>{builder.AddConfiguration(loggingConfiguration.GetSection("Logging")).AddFilter("Microsoft", LogLevel.Warning).AddConsole();}); }如上,通過?AddLogging?,將日志系統注冊到了?DI?系統中,而?AddConfiguration?是對日志系統的全局配置,?AddFilter?則是對日志過濾器的一些配置,最后?AddConsole?添加了一個?Console?的日志提供者(將日志輸出到控制臺)。
記錄日志
在我們需要記錄日志的時候,只需要通過構造函數注入ILogger<T>就可以了:
public class TodoController : Controller{ ? ?private readonly ITodoRepository _todoRepository; ? ?private readonly ILogger _logger; ? ?public TodoController(ITodoRepository todoRepository, ILogger<TodoController> logger) ? ?{_todoRepository = todoRepository;_logger = logger;}[HttpGet] ? ?public IActionResult GetById(string id) ? ?{_logger.LogInformation(LoggingEvents.GET_ITEM, "Getting item {ID}", id); ? ? ? ?var item = _todoRepository.Find(id); ? ? ? ?if (item == null){_logger.LogWarning(LoggingEvents.GET_ITEM_NOTFOUND, "GetById({ID}) NOT FOUND", id); ? ? ? ? ? ?return NotFound();} ? ? ? ?return new ObjectResult(item);} ? }ILogger<T>?中的?T?表示日記的類別,在我們查看日志時,非常有用,在本文后面會講。
日志輸出示例
使用上面的示例代碼,當我們通過控制臺來運行時,訪問?http://localhost:5000/api/todo/0?將會看到如下的日志輸出:
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]Request starting HTTP/1.1 GET http://localhost:5000/api/todo/invalididinfo: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]Executing action method TodoApi.Controllers.TodoController.GetById (TodoApi) with arguments (invalidid) - ModelState is Validinfo: TodoApi.Controllers.TodoController[1002]Getting item invalididwarn: TodoApi.Controllers.TodoController[4000]GetById(invalidid) NOT FOUNDinfo: Microsoft.AspNetCore.Mvc.StatusCodeResult[1]Executing HttpStatusCodeResult, setting HTTP status code 404info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]Executed action TodoApi.Controllers.TodoController.GetById (TodoApi) in 243.2636msinfo: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]Request finished in 628.9188ms 404如果我們訪問?http://localhost:55070/api/todo/0?,將會看到:
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:55070/api/todo/invalidid Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executing action method TodoApi.Controllers.TodoController.GetById (TodoApi) with arguments (invalidid) - ModelState is Valid TodoApi.Controllers.TodoController:Information: Getting item invalidid TodoApi.Controllers.TodoController:Warning: GetById(invalidid) NOT FOUND Microsoft.AspNetCore.Mvc.StatusCodeResult:Information: Executing HttpStatusCodeResult, setting HTTP status code 404Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action TodoApi.Controllers.TodoController.GetById (TodoApi) in 12.5003ms Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 19.0913ms 404通過這個示例,可以看到我們記錄到了 ASP.NET Core 框架自身的日志,這也是統一的日志框架才能實現的功能。
日志類別
我們創建的每一個日志器都指定了一個類別。它可以是任意的字符串,但是約定使用寫入類的完整限定名,如:“TodoApi.Controllers.TodoController”。如果要顯式的指定日志的種類,則可以使用?ILoggerFactory?中的?CreateLogger?方法:
public class TodoController : Controller{ ? ??private readonly ILogger _logger; ?
?public TodoController(ILoggerFactory logger) ? ?{_logger = logger.CreateLogger("TodoApi.Controllers.TodoController");} }
不過,大多數時候,我們還是使用?ILogger<T>,更加方便:
public class TodoController : Controller{ ?? private readonly ILogger _logger; ?
??public TodoController(ILogger<TodoController> logger) ? ?{_logger = logger;} }
這等效于使用?T?類型的完整限定名來調用?CreateLogger?方法。
日志級別
在我們記錄日志時,需要指定日志的級別,這對我們過濾日志非常有用,比如在測試環境中,我們希望提供非常的詳細的日志信息,包括一些敏感信息等,但是在生產環境中,我們希望只記錄嚴重的錯誤,這時候只需要簡單的通過?AddFilter?對日志的過濾級別配置一下就行了。
ASP.NET Core Logging 系統提供了六個日志級別,通過增加重要性或嚴重程度排序如下:
Trace?用于記錄最詳細的日志消息,通常僅用于開發階段調試問題。這些消息可能包含敏感的應用程序數據,因此不應該用于生產環境。默認應禁用。
Debug?這種消息在開發階段短期內比較有用。它們包含一些可能會對調試有所助益、但沒有長期價值的信息。默認情況下這是最詳細的日志。
Information?這種消息被用于跟蹤應用程序的一般流程。與 Verbose 級別的消息相反,這些日志應該有一定的長期價值。
Warning?當應用程序出現錯誤或其它不會導致程序停止的流程異常或意外事件時使用警告級別,以供日后調查。在一個通用的地方處理警告級別的異常。
Error?當應用程序由于某些故障停止工作則需要記錄錯誤日志。這些消息應該指明當前活動或操作(比如當前的 HTTP 請求),而不是應用程序范圍的故障。
Critical?當應用程序或系統崩潰、遇到災難性故障,需要立即被關注時,應當記錄關鍵級別的日志。如數據丟失、磁盤空間不夠等。
日志事件ID
每次寫日志的時候,我們可以指定一個?event ID:
public class LoggingEvents{ ?? ? public const int GET_ITEM = 1002;
?? ?public const int GET_ITEM_NOTFOUND = 4000; }
??
public IActionResult GetById(string id){_logger.LogInformation(LoggingEvents.GET_ITEM, "Getting item {ID}", id); ? ?var item = _todoRepository.Find(id); ?
??if (item == null){_logger.LogWarning(LoggingEvents.GET_ITEM_NOTFOUND, "GetById({ID}) NOT FOUND", id); ? ? ?
???return NotFound();} ?
????return new ObjectResult(item); }
event ID?是一個整數,它可以將一組日志事件關聯到一起。與日志類別類似,但是更加細化。而它的輸出取決于日志提供者,Console?提供者輸出格式如下,在日志類別后面,并用一對中括號包裹著:
info: TodoApi.Controllers.TodoController[1002]Getting item invalididwarn: TodoApi.Controllers.TodoController[4000]GetById(invalidid) NOT FOUND日志格式化字符串
每次記錄日志時,都會提供一條文本消息,而在這個消息字符串中,我們可以使用命名占位符:
public IActionResult GetById(string id){_logger.LogInformation(LoggingEvents.GET_ITEM, "Getting item {ID}", id); ? ?var item = _todoRepository.Find(id); ??if (item == null){_logger.LogWarning(LoggingEvents.GET_ITEM_NOTFOUND, "GetById({ID}) NOT FOUND", id); ?
?? ? ?return NotFound();} ?
?? ? ??return new ObjectResult(item); }
但是占位符的順序決定了使用哪個參數,而不是它的名字,如下示例:
string p1 = "parm1";string p2 = "parm2"; _logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);輸出結果為:
Parameter values: parm1, parm2那這樣做有什么意義呢?
日志框架使用這種消息格式化方式,使日志提供者能夠實現?語義化日志,也稱結構化日志。因為參數本身被傳遞到日志系統中,而不僅僅是格式化的字符串,因此日志提供者可以將參數的值作為字段存儲單獨的存儲。比如:如果使用?Azure Table Storage,我們可以使用如下方法來記錄日志:
_logger.LogInformation("Getting item {ID} at {RequestTime}", id, DateTime.Now);每一個?Azure Table?都可以有?ID?和?RequestTime?屬性,這將簡化對日志數據的查詢,你可以查找指定?RequestTime?范圍內的所有日志,而不必花費解析文本消息的開銷。
過濾器
過濾器可以讓你根據日志的級別和類別來選擇是輸出,還是忽略。我們可以為不同的日志提供者指定不同的過濾器,如下代碼所示,讓?Console?提供者忽略低于?warning?級別的日志,而?Debug?提供者則忽略?TodoApi?類別的日志。
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory){loggerFactory.AddConsole(LogLevel.Warning).AddDebug((category, logLevel) => (category.Contains("TodoApi") && logLevel >= LogLevel.Trace)); }而我們還可以指定全局過濾器,作用于所有的日志提供者,如下示例,我們對于以 "Microsoft" 和 "System" 開頭的日志類別忽略掉低于?Warning?級別的日志:
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory){loggerFactory.AddFilter("Microsoft", LogLevel.Warning).AddFilter("System", LogLevel.Warning).AddFilter("SampleApp.Program", LogLevel.Debug).AddDebug(); }作用域
我們可以將一組邏輯操作放在一個有序的?Scope?中,將?Scope?的標識附加到范圍內的所有日志中。例如,我們可以在處理事務的時候,使事務內的每一個操作日志都包含這個事務的ID。
使用ILgger.BeginScope<TState>?方法創建一個?Scope,并返回一個?IDisposable?類型,當我們?Dispose的時候,這個?Scope?也就結束了,非常適合于使用?using?的方式:
public IActionResult GetById(string id){TodoItem item; ? ?using (_logger.BeginScope("Message attached to logs created in the using block")){_logger.LogInformation(LoggingEvents.GET_ITEM, "Getting item {ID}", id);item = _todoRepository.Find(id); ? ? ? ?if (item == null){_logger.LogWarning(LoggingEvents.GET_ITEM_NOTFOUND, "GetById({ID}) NOT FOUND", id); ? ? ? ? ? ?return NotFound();}} ? ?return new ObjectResult(item); }每一個日志將包括?Scope?的信息:
info: TodoApi.Controllers.TodoController[1002]=> RequestId:0HKV9C49II9CK RequestPath:/api/todo/0 => TodoApi.Controllers.TodoController.GetById (TodoApi) => Message attached to logs created in the using blockGetting item 0 warn: TodoApi.Controllers.TodoController[4000]=> RequestId:0HKV9C49II9CK RequestPath:/api/todo/0 => TodoApi.Controllers.TodoController.GetById (TodoApi) => Message attached to logs created in the using blockGetById(0) NOT FOUND總結
ASP.NET Core 提供了統一的日志框架,能方便地通過?Startup?類進行配置,靈活的集成第三方日志框架,并使用依賴注入的方式在應用程序中使用。本文整體的概述了一下?Logging?系統,在下一章中,會來分析一下?Logging?中配置的源碼。
參考微軟官方文檔:Introduction to Logging in ASP.NET Core
相關文章:?
ASP.NET Core 源碼學習之 Options[1]:Configure
ASP.NET Core 源碼學習之 Options[2]:IOptions
ASP.NET Core 源碼學習之 Options[3]:IOptionsSnapshot
ASP.NET Core 源碼學習之 Options[4]:IOptionsMonitor
ASP.NET Core MVC 源碼學習:詳解 Action 的匹配
asp.net core源碼飄香:從Hosting開始
asp.net core源碼飄香:Configuration組件
asp.net core源碼飄香:Options組件
asp.net core源碼飄香:Logging組件
原文地址:http://www.cnblogs.com/RainingNight/p/asp-net-core-logging-introduction.html
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的ASP.NET Core 源码学习之Logging[1]:Introduction的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在Linux上使用VIM进行.Net
- 下一篇: .NET Core 2.0 正式发布信息