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();
    }

}

运行结果

2017-11-07-21-57-08

这样的错误提示是很难让开发人员定位出错的位置;若要展示开发者页面,还需要将应用程序环境设置为Development

2017-11-07-21-44-35

开发者页面有几个标签页面包含了异常信息和请求参数的信息:

2017-11-07-21-20-13

堆栈的详细、查询参数、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内置的日志支持可以很轻松将日志打印到控制台上面

2017-11-08-23-08-57

注:IExceptionHandlerFeature接口位于Microsoft.AspNetCore.Diagnostics包下,所以需要引入Microsoft.AspNetCore.DiagnosticsILogger接口定义在Microsoft.Extensions.Logging.Abstractions包,并且默认实现在Microsoft.Extensions.Logging包里面。

但是,在生产环境中日志是记录在数据库或文件当中,下面以log4net作为例子将日志记录到文件当中。

log4net 保存日志

在选择log4net的时候最好选择最新版,log4net从2.0.6开始支持.NET Core。

  • 安装log4net
2017-11-13-20-44-02

执行dotnet restore 命令将nuget包拉到本地。确定本地.nuget文件夹里面有log4net并且版本号也一致。

2017-11-13-21-46-26
  • 配置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方法中;同时在项目中会生成两个文件:

  1. FileAppender生成的log-file.log日志文件

  2. RollingLogFileAppender 是按时间生成的日志文件

2017-11-14-11-37-23

控件台打印出来的日志文件

2017-11-14-11-45-51

RollingLogFileAppender 打印的日志文件

2017-11-14-11-46-53

FileAppender打段日志文件

2017-11-14-11-47-21

从上面来看三种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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容