Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 444|回复: 0

aspnetcore配置log4net并添加全局异常处理

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-5-3 17:41:37 | 显示全部楼层 |阅读模式

    第一步:在NuGet中引用log4net

    第二步:创建log4net.config

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <log4net>
        <!-- 错误日志类-->
        <logger name="Error">
          <level value="ALL" />
          <appender-ref ref="ErrorAppender" />
        </logger>
        <!-- 错误日志附加介质-->
        <appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender">
          <!--日志文件路径-->
          <param name="File" value="Logs\\Error\\" />
          <!--是否是向文件中追加日志-->
          <param name="AppendToFile" value="true" />
          <!--log保留天数-->
          <param name="MaxSizeRollBackups" value="1000" />
          <!--最大文件大小-->
          <param name="MaxFileSize" value="10240" />
          <!--日志文件名是否是固定不变的-->
          <param name="StaticLogFileName" value="false" />
          <!--日志文件名格式为:2008-08-31.log-->
          <param name="DatePattern" value="yyyy-MM-dd.'log'" />
          <!--日志根据日期滚动-->
          <param name="RollingStyle" value="Date" />
          <!--信息日志布局-->
          <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%n==========%n【日志级别】:%-5level%n【记录时间】:%date %n【执行时间】:[%r]毫秒%n%message%n" />
          </layout>
        </appender>
    
        <!-- 信息日志类 -->
        <logger name="Info">
          <level value="ALL" />
          <appender-ref ref="InfoAppender" />
        </logger>
        <!-- 信息日志附加介质-->
        <appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
          <!--日志文件路径-->
          <param name="File" value="Logs\\Info\\" />
          <!--是否是向文件中追加日志-->
          <param name="AppendToFile" value="true" />
          <!--log保留天数-->
          <param name="MaxSizeRollBackups" value="100" />
          <param name="MaxFileSize" value="1" />
          <!--日志文件名是否是固定不变的-->
          <param name="StaticLogFileName" value="false" />
          <!--日志文件名格式为:2008-08-31.log-->
          <param name="DatePattern" value="yyyy-MM-dd.'log'" />
          <!--日志根据日期滚动-->
          <param name="RollingStyle" value="Date" />
          <!--信息日志布局-->
          <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%n==========%n【日志级别】:%-5p%n【记录时间】:%d [%t]%n【信息详情】:%m%n"  />
          </layout>
        </appender>
      </log4net>
    </configuration>

    第三步:新建Log4NetConfig.cs类,这里我是把工厂名放在配置文件中获取,一般情况下不需要这样操作,直接在代码内写死即可。

    using log4net;
    using log4net.Config;
    using Microsoft.Extensions.Configuration;
    using System;
    using System.IO;
    
    namespace Taoxue.SchoolRoll.Website
    {
        public class Log4NetConfig
        {
            public static string RepositoryName { get; set; }
    
            public static void Init(IConfiguration configuration)
            {
                var repositoryName = configuration.GetSection("Log4Net:RepositoryName").Value;
                if (string.IsNullOrWhiteSpace(repositoryName))
                {
                    throw new Exception("必须在配置文件中添加 Log4Net > RepositoryName 节点");
                }
    
                RepositoryName = repositoryName;
    
                var configFilePath = configuration.GetSection("Log4Net:ConfigFilePath").Value;
                if (string.IsNullOrWhiteSpace(configFilePath))
                {
                    configFilePath = "log4net.config";
                }
    
                var file = new FileInfo(configFilePath);
                var repository = LogManager.CreateRepository(repositoryName);
                XmlConfigurator.Configure(repository, file);
            }
        }
    }

    第四步:在startup.cs中初始化配置

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
        Log4NetConfig.Init(Configuration);
    }

    第五步:在appsettings.json中创建Log4net节点,注意,这里我是把log4net.config放在  站点根目录/Log4Net 目录下

    "Log4Net": {
        "RepositoryName": "NETCoreRepository",
        "ConfigFilePath": "Log4Net/log4net.config" 
    } 

    第六步:创建Log4NetUtil.cs文件

    using log4net;
    using System;
    
    namespace Taoxue.SchoolRoll.Website
    {
        public class Log4NetUtil
        {
            private static readonly ILog ErrorLog = LogManager.GetLogger(Log4NetConfig.RepositoryName, "Error");
    
            private static readonly ILog InfoLog = LogManager.GetLogger(Log4NetConfig.RepositoryName, "Info");
    
            /// <summary>
            /// 全局异常错误记录持久化
            /// </summary>
            /// <param name="throwMsg"></param>
            /// <param name="ex"></param>
            public static void LogError(string throwMsg, Exception ex)
            {
                var errorMsg =
                    $"【抛出信息】:{throwMsg} \r\n【异常类型】:{ex.GetType().Name} \r\n【异常信息】:{ex.Message} \r\n【堆栈调用】:\r\n{ex.StackTrace}";
                ErrorLog.Error(errorMsg);
            }
    
            public static void LogInfo(string msg)
            {
                InfoLog.Info(msg);
            }
        }
    }

    至此,Log4Net配置完成,下面添加一个全局异常处理类测试下

    using HZC.Core;
    using HZC.Utils.Mvc;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Filters;
    using System;
    
    namespace Taoxue.SchoolRoll.Website.Extensions
    {
        public class MvcGlobalExceptionFilter : IExceptionFilter
        {
            public void OnException(ExceptionContext context)
            {
                try
                {
                    Log4NetUtil.LogError("Mvc全局异常", context.Exception);
                    if (context.HttpContext.Request.IsAjax())
                    {
                        context.Result = new JsonResult(ResultUtil.Fail(context.Exception.Message));
                    }
                    else
                    {
                        context.Result = new RedirectToActionResult("Error", "Home", new { });
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
    context.ExceptionHandled = true; // 注意:如果不添加这句代码,程序不会自动断路,会继续向下进行。 } } }

    在  startup.cs 的 ConfigureServices 中使用如下代码

    services.AddMvc(option =>
        {
             option.Filters.Add<MvcGlobalExceptionFilter>();
        })             
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    修改  HomeController 的 Index ,手动抛出异常

    public IActionResult Index()
    {
        throw new Exception("异常测试");
        return View();
    }

    执行程序,在  根目录/Logs/Error 文件夹下查看生成的日志文件,发现已经写入如下内容

    ==========
    【日志级别】:ERROR
    【记录时间】:2019-05-16 00:40:49,071 
    【执行时间】:[1750]毫秒
    【抛出信息】:Mvc全局异常 
    【异常类型】:Exception 
    【异常信息】:异常测试 
    【堆栈调用】:
       at Taoxue.SchoolRoll.Website.Controllers.HomeController.Index() in E:\项目\CSharp\xx\xxxx\Taoxue.SchoolRoll.Website\Controllers\HomeController.cs:line 19
       at lambda_method(Closure , Object , Object[] )
       at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
       at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
       at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync()
       at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()
       at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
       at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
       at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync()

    到这里,log4net配置和全局异常捕捉就完成了。

    需要注意的是,这里所谓的全局是指进入mvc中间件后产生的异常,如果在其他中间件中异常了,上面的方法是捕捉不到的,下面来模拟一下:

    在 startup.cs 的 Configure 方法中 app.UserMvc(...) 之前添加如下代码

    app.Use(async (context, next) =>
    {
        if (context.Request.Path.Value.Contains("test"))
        {
            throw new Exception("中间件异常测试");
        }
        await next();
    });

    上面代码的作用是,当访问地址中带有 test 则抛出异常。

    执行程序,访问  http://localhost:5000/test 会发现,程序报错,这时查看日志会发现,异常并没有被记录,也就是说,异常没有被我们的MvcGlobalExceptionFilter捕捉到,这显然不是我们想要的。

    如果要捕捉到这种异常需要怎么处理呢?其实程序中已经给出了提示:

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    这是 startup.cs  Configure 中自动帮我们添加的代码,就是用于异常的捕获和处理,一种方式是对 /Home/Error 进行处理,在该action内记录异常,这种方法比较简单,这里就不详细说了。

    接下来,我们用自定义中间件的方式来实现一下。

    新建  GlobalExceptionMiddleware.cs 文件

    using HZC.Core;
    using HZC.Utils.Mvc;
    using Microsoft.AspNetCore.Http;
    using Newtonsoft.Json;
    using System;
    using System.Threading.Tasks;
    
    namespace Taoxue.SchoolRoll.Website.Extensions
    {
        public class GlobalExceptionMiddleWare
        {
            public readonly RequestDelegate Next;
    
            public GlobalExceptionMiddleWare(RequestDelegate next)
            {
                Next = next;
            }
    
            public async Task Invoke(HttpContext context)
            {
                try
                {
                    await Next(context);
                }
                catch (Exception e)
                {
                    context.Response.Clear();
                    context.Response.StatusCode = StatusCodes.Status200OK;
    
                    Log4NetUtil.LogError("全局异常", e);
    
                    if (context.Request.IsAjax())
                    {
                        context.Response.ContentType = ResponseContentTypes.Json;
                        await context.Response.WriteAsync(JsonConvert.SerializeObject(new { Code = 500, Message = e.Message }));
                    }
                    else
                    {
                        context.Response.Redirect("/Home/Error");
                    }
                }
            }
        }
    }

    建议在 Invoke 方法中 catch 代码块中再包一层 try..catch ,以防止处理代码发生错误时出现异常。

    接下来,修改 startup.cs 的 Configure 方法,注释掉系统自带的异常处理代码,添加我们自定义的中间件

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        //if (env.IsDevelopment())
        //{
        //    app.UseDeveloperExceptionPage();
        //}
        //else
        //{
        //    app.UseExceptionHandler("/Home/Error");
        //    app.UseHsts();
        //}
        app.UseMiddleware<GlobalExceptionMiddleWare>();
    
        app.Use(async (context, next) =>
        {
            if (context.Request.Path.Value.Contains("test"))
            {
                throw new Exception("中间件异常测试");
            }
            await next();
        });
    
        app.UseHttpsRedirection();
        app.UseStaticFiles();
    
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

    再次执行程序,访问  http://localhost:5000/test 会发现我们定义的逻辑被执行,并且该异常也被记录到了日志文件中

    ==========
    【日志级别】:ERROR
    【记录时间】:2019-05-16 01:20:56,051 
    【执行时间】:[7830]毫秒
    【抛出信息】:全局异常 
    【异常类型】:Exception 
    【异常信息】:中间件异常测试 
    【堆栈调用】:
       at Taoxue.SchoolRoll.Website.Startup.<>c.<<Configure>b__5_0>d.MoveNext() in E:\项目\CSharp\xx\xxxx\Taoxue.SchoolRoll.Website\Startup.cs:line 77
    --- End of stack trace from previous location where exception was thrown ---
       at Taoxue.SchoolRoll.Website.Extensions.GlobalExceptionMiddleWare.Invoke(HttpContext context) in E:\项目\CSharp\xx\xxxx\Taoxue.SchoolRoll.Website\Extensions\GlobalExceptionMiddleWare.cs:line 23

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-19 20:57 , Processed in 0.068530 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表