翼度科技»论坛 编程开发 .net 查看内容

5.使用日志+自定义全局异常过滤器

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
刚开始写文章,封装Base基类的时候,添加了trycatch异常块,不过当时没有去记录日志,直接return了。有小伙伴劝我不要吃了Exception

 其实没有啦,项目刚开始,我觉得先做好整体结构比较好。像是盖楼一样。先把楼体建造出来,然后再一步一步的美化完善。
基础的仓储模式已经ok,Autofac已经注入了项目的实现层。上篇文章新建了一个Test类主要用于测试,加了4个接口增删改查,执行也是完全没有问题的。这篇文章开始就是逐步完善优化项目。
关于日志有很多选择,我记得上篇我也提到过几个,好,那我就再重复一下:Nlog,Log4,Serilog……
我这里使用的是Nlog,以前用的Log4,都是自己封装一个Helper类,后来学NetCore的时候看到Nlog使用着好简单,而且后面也搜了下大佬们对两者的对比,综合对比我觉得Nlog更好一点~性能现在应该差不多,因为那篇文章14年的,放个当时大佬的对比(现在应该也差不多少吧,看个人习惯了,用习惯了怎么都好用)
项目log4netnlog流行程度胜负易用性负胜动态配置平平输出目标平平跨平台负胜开源持续维护负胜日志性能负胜 
先稍微梳理一下逻辑。日志这东西一般程序都会有,但是你真没有的话,其实也不影响程序跑对吧~再者依赖注入你肯定要在程序层有Nlog的直接或间接引用的,要么你哪个地方(类库)用到就安装一个Nlog包,要么在最底层仓储层安装一个Nlog包,间接引用到程序层。好像也没啥影响,但我就是觉得怪怪的,感觉这样会不会增加耦合? 唉,这方面我还真是学的不到家,希望关于这块儿有了解的可以讨论一下,我再学学~
我的做法呢,就是把Nlog安装到Common公共类库中。这个类库是在上个文章新建的。用意就是专门弄一些很多地方都会用到的东西,但是我即便不用也不影响程序跑的东西。比如Autofac……所以日志我也打算加到此处……
在Common类库安装Nlog程序包:NLog.Extensions.Logging

 在Common类库下新建一个文件  Logs\Nlog\NlogModule.cs 。我搞这么多文件夹一方面看着比较容易看得懂,另一方面,后面打算添加一些其他的日志给大家一个参考。
NlogModule是静态类,添加静态方法AddNlogModule,注入Nlog:
  1.         public static void AddNlogModule(this IServiceCollection Services)
  2.         {
  3.             Services.AddLogging(logging =>
  4.             {
  5.                 logging.AddNLog();
  6.             });
  7.         }
复制代码
然后,好像没然后咯~
上篇文章中,将Common添加到IRepository层的项目引用。可以说已经贯穿了整个项目(直接或间接引用)。所以只需要在API程序层的Programs中添加Nlog模块就好。
  1.         builder.Services.AddNlogModule();//Nlog
复制代码
(拍头)忘了一个重要的地方。Nlog的config配置:主要配置你的日志怎么记录,记录到哪里,记录格式等

 配置如下,也无需花心思在这上面,官网和网上都有模板,自己简单配置一下,保存,以后用到了直接拷贝就好,我做了简单的注释,应该都看的明白,所以不多说什么啦~
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
  3.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  4.     <targets async="true">
  5.         
  6.         <target name="LogDebug" xsi:type="File" maxArchiveDays="7" archiveAboveSize="10485760"  fileName="Logs/Debug信息/【${shortdate}】.txt" layout="【${date}】【${logger}】${newline}${message:withexception=true}${newline}${newline} " />
  7.         <target name="LogInfo" xsi:type="File" maxArchiveDays="7" archiveAboveSize="10485760"  fileName="Logs/Info信息/【${shortdate}】.txt" layout="【${date}】【${logger}】${newline}${message:withexception=true}${newline}${newline} " />
  8.         <target name="LogError" xsi:type="File" maxArchiveDays="7" archiveAboveSize="10485760"  fileName="Logs/Error信息/【${shortdate}】.txt" layout="【${date}】【${logger}】${newline}${message:withexception=true}${newline}${newline} " />
  9.     </targets>
  10.     <rules>
  11.         
  12.         <logger name="Microsoft.*"  writeTo="" final="true" />
  13.         
  14.         <logger name="*" level="Error" writeTo="LogError" final="true" />
  15.         <logger name="*" level="Info"  writeTo="LogInfo" final="true" />
  16.         <logger name="*" level="Debug" writeTo="LogDebug" final="true" />
  17.     </rules>
  18. </nlog>
复制代码
 到这里已经完成了。在你需要的地方通过构造函数注入即可,例如:

 这种Ilogger是泛型接口,可以通过尖括号里的类名找到相关的依赖,简单来说记录日志的时候可以输出错误信息是在哪个地方产生的。也可以根据这个来设置过滤,或输出日志到不同的地方。
上上篇文章的代码生成器是默认是没有添加Nlog的,所以构造函数里也没有添加,如果你需要在服务层或仓储层记录详细信息Info或者Debug的话,根据自己的需求去构造注入吧。正常情况下,记录异常就足够用了。
欸,那问题就来了,你说我仓储层服务层没有构造函数注入,那我异常了怎么记录呢?有的人说,throw丢出去呀,丢到程序层……确实可以,但是稍微微有点麻烦吧?仓储层裹一层,丢到服务层,服务层裹一层丢到程序层,然后程序层再记录日志?so~封装一个全局异常记录模块儿~
这个是官方的接口,同时程序一定要对异常进行处理,这个是必要的。不然既不好排查问题,生产使用的时候一报错就挂掉,那可就凉了……所以这个全局异常处理我将添加在程序层下
新建一个类(自己随便起名哈,里面的内容一样就行)GlobalExceptionFilter,继承接口IExceptionFilter:异常过滤器
  1. public class GlobalExceptionFilter : IExceptionFilter
  2.     {
  3.         //构造注入Nlog
  4.         private readonly ILogger<GlobalExceptionFilter> logger;
  5.         public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> _)
  6.         {
  7.             logger = _;
  8.         }
  9.         //实现接口,对异常进行处理
  10.         public void OnException(ExceptionContext context)
  11.         {
  12.             //记录异常到日志
  13.             StringBuilder exMsg = new();
  14.             exMsg.AppendLine($"【异常方法:】{context.HttpContext.Request.Path}");
  15.             exMsg.AppendLine($"【请求类型:】{context.HttpContext.Request.Method}");
  16.             exMsg.AppendLine($"【异常错误:】{context.Exception.Message}");
  17.             exMsg.AppendLine($"【堆栈跟踪:】{context.Exception.StackTrace}");
  18.             logger.LogError(exMsg.ToString());
  19.             // 创建自定义错误响应
  20.             var result = new ObjectResult(new { error = context.Exception.Message })
  21.             {
  22.                 StatusCode = 400, // 设置适当的HTTP状态码
  23.                 DeclaredType = typeof(object) // 声明类型,以确保内容被正确序列化
  24.             };
  25.             // 取消异常的传播,以防止默认的500错误
  26.             context.ExceptionHandled = true;
  27.             // 设置响应结果
  28.             context.Result = result;
  29.         }
  30.     }
复制代码
Programs里面添加注入,将其注入进控制器服务。松耦合,控制器相关的依赖,只要里面有trycatch都可以接收并处理,简单来说你这程序里面的异常最终都到这个方法里面进行处理,并且可以重写返回结果,不会只报一个500错误和一堆的堆栈错误信息。注入的代码如下,我还是单独弄成模块类,感觉这样拆除比较方便,不知道我这种做法是否合理,还是多余。但我自己看着还是比较顺眼的,希望有更多的建议~~~
  1.     public static class ExceptionModule
  2.     {
  3.         public static void AddExceptionModule(this IServiceCollection Services)
  4.         {
  5.             Services.AddControllers(Ex =>
  6.             {
  7.                 Ex.Filters.Add<GlobalExceptionFilter>();
  8.             });
  9.         }
  10.     }
复制代码
  1. builder.Services.AddExceptionModule();//全局异常
复制代码
撒花~~~结束~~~手动搞个错误信息看下返回内容并且看下记录的日志~


 欢迎留言,虚心听取大家建议,听人劝吃饱饭~~~掰掰~~

来源:https://www.cnblogs.com/zhang-3/archive/2023/09/22/17722360.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具