ASP.NET Core 异常处理与日志记录
1.1. 异常处理
1.1.1. 异常产生的原因及处理
异常是在编程时,一个意外的事件,如无效的输入或连接丢失,当程序执行破坏的指令流时就会出现这种情况。Exception是Exceptional event的简要表达。异常的实现需要保存抛出异常捕获点的必要信息,这会一定程度上导致程序变慢,这也是人们诟病异常性能的原因。
异常(Exception)是一种非程序原因的操作失败,而错误(Error)则意味着程序有缺陷。
Exception是一种类.例外会中断执行堆栈直到被捕获.一个异常可以用来传达一个错误,但是更普遍的是用来表示出现了一些例外.
1.1.2. ASP.NET Core中启动开发人员异常页面
想要在应用程序中显示详细的异常信息,展示错误详情的页面在Microsoft.AspNetCore.Diagnostics
包中,但是自从ASP.NET Core 2.0开始Microsoft.AspNetCore.All
包里面包含了所有以Microsoft.AspNetCore
开头的包,所以不需要在额外安装Microsoft.AspNetCore.Diagnostics
包,但是需要在Startup
类的Configure
方法中进行配置
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
env.EnvironmentName = EnvironmentName.Production;
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error");
}
将UseDeveloperExceptionPage
放在你想捕获的中间件之前,如app.UseMvc
。
注:为了应用程序的安全,一般不会在生产环境中启起用开发者页面(异常页面)。
示例
HomeController.cs 手动抛出一个异常
public class HomeController : Controller
{
public IActionResult Index()
{
throw new Exception("dddd");
return View();
}
}
运行结果
这样的错误提示是很难让开发人员定位出错的位置;若要展示开发者页面,还需要将应用程序环境设置为Development
。
开发者页面有几个标签页面包含了异常信息和请求参数的信息:
堆栈的详细、查询参数、Cookies信息和请求头这些打印出来方便开发人员快速定位错误。但是程序在开发的时候可以直接这样定位出错位置,但是在生产环境中就需要借助日志将出错信息写到日志文件里面以方便开发人员定位错误。
1.2. 日志记录
1.2.1. 日志作用
程序中记录日志一般有两个目的,故障定位和显示程序运行状态。好的日志记录方式可以提供足够多定位问题的依据。
1.2.2. 日志等级
如果有良好的习惯的人平时工作的时候会将领导交待下来的工作分为:紧急重要、重要不紧急、紧急不重要、不紧急不重要等;同样ASP.NET Core也将日志定义了多个等级,从0到5总共6个等级:
- Trace = 0
这个级别只对开发人员调试有价值。这些消息可能包含敏感的应用程序数据,因此不应该在生产环境中启用。
- Debug = 1
对于在开发和调试过程中具有短期可用性的信息。如果不是出现问题在生产环境一般不建议启用。
- Information = 2
用于跟踪应用程序,这些日志有长期的价值。
- Warning = 3
用于程序中的异常或意外事件。这些可能包括错误或其他不导致应用程序停止的条件,但是可能需要进行排查。
- Error = 4
对于不能处理的错误过异常。这些消息表明当前的活动或操作(例如当前的HTTP请求)失败,而不是应用程序范围的失败。
- Critical = 5
对于那些需要立即关注的失败。示例:磁盘空间中的数据丢失场景。
开发人员可以根据日志级别将众多日志存储到不到的介质中,以供分析用户需求、定准程序错误等。
1.2.3. ASP.NET Core中的日志接口
ASP.NET Core支持可与各种日志记录提供程序配合使用的日志记录API。 内置提供程序允许您将日志发送到一个或多个目标,并且可以插入第三方日志记录框架。
1.2.4. 实践
用.NET Core内置日志接口将日志信息打印到控制台上
public class Startup
{
...
public virtual void Configure(IApplicationBuilder app)
{
...
app.UseExceptionHandler("/Home/Error");
...
}
}
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.Extensions.Logging;
public class HomeController : Controller
{
private readonly ILogger _logger;
public HomeController (ILogger<HomeController> logger) {
_logger = logger;
}
public IActionResult Index () {
throw new Exception ("dddd");
return View ();
}
public IActionResult Error()
{
var feature = HttpContext.Features.Get<IExceptionHandlerFeature>();
var error = feature?.Error;
_logger.LogError("Oops!Error Info-----:", error);
return View("~/Views/Shared/Error.cshtml", error);
}
}
Program.cs
public class Program {
public static void Main (string[] args) {
BuildWebHost (args).Run ();
}
public static IWebHost BuildWebHost (string[] args) =>
WebHost.CreateDefaultBuilder (args)
.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);
config.AddEnvironmentVariables ();
})
.ConfigureLogging ((hostingContext, logging) => {
logging.AddConfiguration (hostingContext.Configuration.GetSection ("Logging"));
logging.AddConsole ();
logging.AddDebug ();
})
.UseStartup<Startup> ()
.Build ();
}
通过ASP.NET Core内置的日志支持可以很轻松将日志打印到控制台上面
注:
IExceptionHandlerFeature
接口位于Microsoft.AspNetCore.Diagnostics
包下,所以需要引入Microsoft.AspNetCore.Diagnostics
;ILogger
接口定义在Microsoft.Extensions.Logging.Abstractions
包,并且默认实现在Microsoft.Extensions.Logging
包里面。
但是,在生产环境中日志是记录在数据库或文件当中,下面以log4net作为例子将日志记录到文件当中。
log4net 保存日志
在选择log4net的时候最好选择最新版,log4net从2.0.6开始支持.NET Core。
- 安装log4net
执行dotnet restore
命令将nuget包拉到本地。确定本地.nuget文件夹里面有log4net并且版本号也一致。
- 配置log4net.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- This section contains the log4net configuration settings -->
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout" value="%date [%thread] %-5level %logger - %message%newline" />
</appender>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<param name="Encoding" value="utf-8" />
<file value="log-file.log" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="Encoding" value="utf-8" />
<file value="logfile/" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<staticLogFileName value="false" />
<datePattern value="yyyyMMdd'.log'" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="1MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<!-- Setup the root category, add the appenders and set the default level -->
<root>
<level value="ALL" />
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="FileAppender" />
<appender-ref ref="RollingLogFileAppender" />
</root>
</log4net>
</configuration>
这里定义了三个appender,都会起作用;日志等级为ALL将会记录所有类型的日志。
Startup.cs
using System.IO;
using log4net;
using log4net.Config;
using log4net.Repository;
public class Startup
{
public static ILoggerRepository Repository;
public Startup (IHostingEnvironment env) {
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
Repository = LogManager.CreateRepository ("NETCoreRepository");
XmlConfigurator.Configure (Repository, new FileInfo ("log4net.config"));
}
HomeController.cs
public class HomeController : Controller {
private ILog log = LogManager.GetLogger (Startup.Repository.Name, typeof (HomeController));
public IActionResult Index () {
throw new Exception ("Manul Exception:");
return View ();
}
public IActionResult Error () {
var feature = HttpContext.Features.Get<IExceptionHandlerFeature> ();
var error = feature?.Error;
log.Error(error);
return View ("~/Views/Shared/Error.cshtml", error);
}
}
运行结果
访问首页时会出异常,根据跳转到错误路由Error
方法中;同时在项目中会生成两个文件:
-
FileAppender生成的log-file.log日志文件
-
RollingLogFileAppender 是按时间生成的日志文件
控件台打印出来的日志文件
RollingLogFileAppender 打印的日志文件
FileAppender打段日志文件
从上面来看三种Appender打印的三种日志是一致的,所以在实际开发的时候可以根据需要来用哪种方式进行记录日志。
1.3. 总结
本节讲解了两个知识点:
- 异常产生及异常和错误的区别,在ASP.NET Core中捕获异常,启动开发者异常页面。
- ASP.NET Core内置日志接口和使用log4net将异常日志输出到文件中
参与资料:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?tabs=aspnetcore2x
http://logging.apache.org/log4net/release/manual/configuration.html
作者:xdpie 出处: http://www.cnblogs.com/vipyoumay/p/7838069.html