一、日誌的做用:html
程序中記錄日誌通常有兩個目的,故障定位和顯示程序運行狀態。好的日誌記錄方式能夠提供足夠多定位問題的依據。git
二、日誌的等級:github
有良好工做習慣的人,工做的時候會將領導交待下來的工做分爲:緊急重要、重要不緊急、緊急不重要、不緊急不重要等;一樣 ASP.NET Core 定義瞭如下日誌級別(按嚴重性從低到高排列)。數據庫
每次寫入日誌時都需指定其 LogLevel。 日誌級別指示嚴重性或重要程度。 例如,若是方法正常結束則寫入 Information
日誌,若是方法返回 404 返回代碼則寫入 Warning
日誌,若是捕獲未知異常則寫入 Error
日誌。 可使用日誌級別控制寫入到特定存儲介質或顯示窗口的日誌輸出量。 例如在生產中,可將全部 Information
及如下級別的日誌放在卷數據存儲,將全部 Warning
及以上級別的日誌放在值數據存儲。 在開發期間,一般要將嚴重性爲 Warning
或以上級別的日誌發送到控制檯。 須要進行故障排除時,可添加 Debug
級別。json
表示僅對於開發人員調試問題有價值的信息。 這些消息可能包含敏感應用程序數據,所以不得在生產環境中啓用它們。 默認狀況下禁用。 示例:Credentials: {"User":"someuser", "Password":"P@ssword"}windows
表示在開發和調試過程當中短時間有用的信息。 示例:Entering method Configure with flag set to true.。除非要排查問題,不然一般不會在生產中啓用 Debug 級別日誌,由於日誌數量過多。api
用於跟蹤應用程序的常規流。 這些日誌一般有長期價值。 示例:Request received for path /api/todo服務器
表示應用程序流中的異常或意外事件。 可能包括不會中斷應用程序運行但仍需調查的錯誤或其餘條件。 Warning 日誌級別經常使用於已處理的異常。 示例:FileNotFoundException for file quotes.txt.app
表示沒法處理的錯誤和異常。 這些消息指示的是當前活動或操做(如當前 HTTP 請求)中的失敗,而不是應用程序範圍的失敗。日誌消息示例:Cannot insert record due to duplicate key violation.asp.net
須要當即關注的失敗。 例如數據丟失、磁盤空間不足。
微軟官方文檔:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1
由於Logger是asp.net core 的內置service,因此咱們就不須要在ConfigureService裏面註冊了。若是是asp.net core 1.0版本的話,咱們須要配置一個或者多個Logger,可是asp.net core 2.0的話就不須要作這個工做了,由於在CreateDefaultBuilder方法裏默認給配置了輸出到Console和Debug窗口的Logger。這是源碼:
public static IWebHostBuilder CreateDefaultBuilder(string[] args) { var builder = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .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()) { 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) => { logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); }) .UseIISIntegration() .UseDefaultServiceProvider((context, options) => { options.ValidateScopes = context.HostingEnvironment.IsDevelopment(); }); if (args != null) { builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build()); } return builder; }
咱們能夠在ProductController裏面注入ILoggerFactory而後再建立具體的Logger。可是還有更好的方式,Container能夠直接提供一個ILogger<T>的實例,這時候呢Logger就會使用T的名字做爲日誌的類別:
namespace CoreBackend.Api.Controllers { [Route("api/[controller]")] public class ProductController : Controller { private ILogger<ProductController> _logger; public ProductController(ILogger<ProductController> logger) { _logger = logger; } ......
若是經過Constructor注入的方式不可用,那麼咱們也能夠直接從Container請求來獲得它:HttpContext.RequestServices.GetService(typeof(ILogger<ProductController>)); 若是你在Constructor寫這句話可能會空指針,由於這個時候HttpContext應該是null吧。
不過仍是建議使用Constructor注入的方式!!!
而後咱們記錄一些日誌吧:
[Route("{id}", Name = "GetProduct")] public IActionResult GetProduct(int id) { var product = ProductService.Current.Products.SingleOrDefault(x => x.Id == id); if (product == null) { _logger.LogInformation($"Id爲{id}的產品沒有被找到.."); return NotFound(); } return Ok(product); }
Log記錄時通常都分幾個等級,這點我假設你們都知道吧,就不介紹了。
而後試一下:經過Postman訪問一個不存在的產品:‘/api/product/22’,而後看看Debug輸出窗口:
嗯,出現了,前邊是分類,也就是ILogger<T>裏面T的名字,而後是級別 Information,而後就是咱們記錄的Log內容。
再Log一個Exception:
[Route("{id}", Name = "GetProduct")] public IActionResult GetProduct(int id) { try { throw new Exception("來個異常!!!"); var product = ProductService.Current.Products.SingleOrDefault(x => x.Id == id); if (product == null) { _logger.LogInformation($"Id爲{id}的產品沒有被找到.."); return NotFound(); } return Ok(product); } catch (Exception ex) { _logger.LogCritical($"查找Id爲{id}的產品時出現了錯誤!!", ex); return StatusCode(500, "處理請求的時候發生了錯誤!"); } }
記錄Exception就建議使用LogCritical了,這裏須要注意的是Exception的發生就表示服務器發生了錯誤,咱們應該處理這個exception並返回500。使用StatusCode這個方法返回特定的StatusCode,而後能夠加一個參數來解釋這個錯誤(這裏通常不建議返回exception的細節)。
運行試試:
OK。
Log到Debug窗口或者Console窗口仍是比較方便的,可是正式生產環境中這確定不夠用。
正式環境應該Log到文件或者數據庫。雖然asp.net core 的log內置了記錄到Windows Event的方法,可是因爲Windows Event是windows系統獨有的,因此這個方法沒法跨平臺,也就不建議使用了。
官方文檔上列出了這幾個建議使用的第三發Log Provider:
把這幾個Log provider註冊到asp.net core的方式幾乎是一摸同樣的,因此介紹一個就行。咱們就用比較火的NLog吧。
官方文檔:https://github.com/NLog/NLog.Web/wiki/Getting-started-with-ASP.NET-Core-2
首先經過nuget安裝Nlog:
注意要勾上include prerelease,目前還不是正式版。
裝完以後,咱們就須要爲Nlog添加配置文件了。默認狀況下Nlog會在根目錄尋找一個叫作nlog.config的文件做爲配置文件。那麼咱們就手動改添加一個nlog.config:
<?xml version="1.0" encoding="utf-8" ?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <targets> <target name="logfile" xsi:type="File" fileName="logs/${shortdate}.log" /> </targets> <rules> <logger name="*" minlevel="Info" writeTo="logfile" /> </rules> </nlog>
而後設置該文件的屬性以下:
對於Nlog的配置就不進行深刻介紹了。具體請看官方文檔的.net core那部分。
而後須要把Nlog集成到asp.net core,也就是把Nlog註冊到ILoggerFactory裏面。因此打開Startup.cs,首先注入ILoggerFactory,而後對ILoggerFactory進行配置,爲其註冊NLog的Provider:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { // loggerFactory.AddProvider(new NLogLoggerProvider()); loggerFactory.AddNLog(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler(); } app.UseStatusCodePages(); app.UseMvc(); }
針對LoggerFactory.AddProvider()這種寫法,Nlog一個簡單的ExtensionMethod作了這個工做,就是AddNlog();
添加完NLog,其他的代碼都不須要改,而後咱們試下:
在如圖所示的位置出現了log文件。內容以下:
官方文檔: Asp.net core https://github.com/serilog/serilog-aspnetcore
Sinks-file: https://github.com/serilog/serilog-sinks-file
Sinks-Console:https://github.com/serilog/serilog-sinks-console
參考資料:
https://www.cnblogs.com/cgzl/p/7652413.html;
https://v.qq.com/x/page/m0762gzo2l6.html
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1