一、日志的做用: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