zoukankan      html  css  js  c++  java
  • 从壹开始前后端分离 [.netCore 不定期更新 ] 三十五║ 完美实现全局异常日志记录

    缘起

    哈喽我是不定期更新的日常,昨天群里小伙伴问到了记录日志,当然,以前我也挖过这个坑,后来一直没有来得及填上,也想着 swagger 一直又有错误信息展示的功能,就迟迟没有添加这个功能,不过昨天夜里想了想,还是需要增加上,旨在提高框架的高效性。不定期日常就直接上代码了,我有一个小想法,就是希望大家有好的想法,可以给我说,我会整理下,添加到框架里,并在文章头里写上

    投稿作者:这里重点说明下,是参考群里小伙伴 Hello World!  的相关内容,并在他的基础上更新,添加了注入和全局,大家可以看看他的文章,支持开源,棒棒哒。

    废话不多,直接来看,最终的效果是这样的:

    更新:评论席很精彩

    1、感谢网友@梦亦晓的意见,目前是在 .net core api 中可以捕获,至于 mvc 的view视图中的错误是否可以,热心观众可以试试,感激不尽。

    2、@易墨 提出了中间件的方法,在评论席里,我感觉也不错,大家可以看看,中间件更偏重非业务操作,只不过需要注意中间件的顺序。

    3、感谢@blackheart 从官方教程中,提出指正,其实官方已经内置了接口ILogger,当然你可以直接用 官方地址

    4、关于日志记录,还有 NLog日志、Exceptionless分布式日志、官方的Logging等等都可以用,看个人喜好吧。甚至可以自己写一个loghepler,但是一定要尽量使用接口,不仅可以依赖注入,还可以以后方便替换,不会到时候破坏底层框架。

    一、添加日志接口类

    在 Blog.core 下的 Log 文件夹内,新建 ILoggerHelper.cs 

    更新:这个接口其实也没必要,你可以直接参考官方提供的ILogger,然后注入就行,当然看个人习惯吧,如果一定要自己设计,可按照我的方法设计接口

    namespace Blog.Core.Log
    {
        /// <summary>
        /// 日志接口
        /// </summary>
        public interface ILoggerHelper
        {
           
            /// <summary>
            /// 调试信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            void Debug(object source, string message);
            /// <summary>
            /// 调试信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="ps">ps</param>
            void Debug(object source, string message, params object[] ps);
            /// <summary>
            /// 调试信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            void Debug(Type source, string message);
            /// <summary>
            /// 关键信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            void Info(object source, object message);
            /// <summary>
            /// 关键信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            void Info(Type source, object message);
            /// <summary>
            /// 警告信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            void Warn(object source, object message);
            /// <summary>
            /// 警告信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            void Warn(Type source, object message);
            /// <summary>
            /// 错误信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            void Error(object source, object message);
            /// <summary>
            /// 错误信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            void Error(Type source, object message);
            /// <summary>
            /// 失败信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            void Fatal(object source, object message);
            /// <summary>
            /// 失败信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            void Fatal(Type source, object message);
    
            /* Log a message object and exception */
    
            /// <summary>
            /// 调试信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            void Debug(object source, object message, Exception exception);
            /// <summary>
            /// 调试信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            void Debug(Type source, object message, Exception exception);
            /// <summary>
            /// 关键信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            void Info(object source, object message, Exception exception);
            /// <summary>
            /// 关键信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            void Info(Type source, object message, Exception exception);
            /// <summary>
            /// 警告信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            void Warn(object source, object message, Exception exception);
            /// <summary>
            /// 警告信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            void Warn(Type source, object message, Exception exception);
            /// <summary>
            /// 错误信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            void Error(object source, object message, Exception exception);
            /// <summary>
            /// 错误信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            void Error(Type source, object message, Exception exception);
            /// <summary>
            /// 失败信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            void Fatal(object source, object message, Exception exception);
            /// <summary>
            /// 失败信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            void Fatal(Type source, object message, Exception exception);
        }
    
    }

    二、实现日志帮助类,引入 log4net 

    1、在 Log 文件夹下新建 LogHelper.cs

    namespace Blog.Core.Log
    {
        /// <summary>
        /// 日志帮助实现类
        /// </summary>
        public class LogHelper : ILoggerHelper
        {
            private readonly ConcurrentDictionary<Type, ILog> Loggers = new ConcurrentDictionary<Type, ILog>();
    
            /// <summary>
            /// 获取记录器
            /// </summary>
            /// <param name="source">soruce</param>
            /// <returns></returns>
            private ILog GetLogger(Type source)
            {
                if (Loggers.ContainsKey(source))
                {
                    return Loggers[source];
                }
                else
                {
                    ILog logger = LogManager.GetLogger(Startup.repository.Name, source);
                    Loggers.TryAdd(source, logger);
                    return logger;
                }
            }
    
            /* Log a message object */
            /// <summary>
            /// 调试信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            public void Debug(object source, string message)
            {
                Debug(source.GetType(), message);
            }
            /// <summary>
            /// 调试信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="ps">ps</param>
            public void Debug(object source, string message, params object[] ps)
            {
                Debug(source.GetType(), string.Format(message, ps));
            }
            /// <summary>
            /// 调试信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            public void Debug(Type source, string message)
            {
                ILog logger = GetLogger(source);
                if (logger.IsDebugEnabled)
                {
                    logger.Debug(message);
                }
            }
            /// <summary>
            /// 关键信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            public void Info(object source, object message)
            {
                Info(source.GetType(), message);
            }
            /// <summary>
            /// 关键信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            public void Info(Type source, object message)
            {
                ILog logger = GetLogger(source);
                if (logger.IsInfoEnabled)
                {
                    logger.Info(message);
                }
            }
            /// <summary>
            /// 警告信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            public void Warn(object source, object message)
            {
                Warn(source.GetType(), message);
            }
            /// <summary>
            /// 警告信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            public void Warn(Type source, object message)
            {
                ILog logger = GetLogger(source);
                if (logger.IsWarnEnabled)
                {
                    logger.Warn(message);
                }
            }
            /// <summary>
            /// 错误信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            public void Error(object source, object message)
            {
                Error(source.GetType(), message);
            }
            /// <summary>
            /// 错误信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            public void Error(Type source, object message)
            {
                ILog logger = GetLogger(source);
                if (logger.IsErrorEnabled)
                {
                    logger.Error(message);
                }
            }
            /// <summary>
            /// 失败信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            public void Fatal(object source, object message)
            {
                Fatal(source.GetType(), message);
            }
            /// <summary>
            /// 失败信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            public void Fatal(Type source, object message)
            {
                ILog logger = GetLogger(source);
                if (logger.IsFatalEnabled)
                {
                    logger.Fatal(message);
                }
            }
            /* Log a message object and exception */
    
            /// <summary>
            /// 调试信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            public void Debug(object source, object message, Exception exception)
            {
                Debug(source.GetType(), message, exception);
            }
            /// <summary>
            /// 调试信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            public void Debug(Type source, object message, Exception exception)
            {
                GetLogger(source).Debug(message, exception);
            }
            /// <summary>
            /// 关键信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            public void Info(object source, object message, Exception exception)
            {
                Info(source.GetType(), message, exception);
            }
            /// <summary>
            /// 关键信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            public void Info(Type source, object message, Exception exception)
            {
                GetLogger(source).Info(message, exception);
            }
            /// <summary>
            /// 警告信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            public void Warn(object source, object message, Exception exception)
            {
                Warn(source.GetType(), message, exception);
            }
            /// <summary>
            /// 警告信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            public void Warn(Type source, object message, Exception exception)
            {
                GetLogger(source).Warn(message, exception);
            }
            /// <summary>
            /// 错误信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            public void Error(object source, object message, Exception exception)
            {
                Error(source.GetType(), message, exception);
            }
            /// <summary>
            /// 错误信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            public void Error(Type source, object message, Exception exception)
            {
                GetLogger(source).Error(message, exception);
            }
            /// <summary>
            /// 失败信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            public void Fatal(object source, object message, Exception exception)
            {
                Fatal(source.GetType(), message, exception);
            }
            /// <summary>
            /// 失败信息
            /// </summary>
            /// <param name="source">source</param>
            /// <param name="message">message</param>
            /// <param name="exception">ex</param>
            public void Fatal(Type source, object message, Exception exception)
            {
                GetLogger(source).Fatal(message, exception);
            }
        }
    }

    2、引用 nuget 包 

    这个时候,你会发现还有一个错误 , 不过别慌,请往下看。

    三、配置 LogManger 自启动

    1、在启动文件中添加 Loger 日志仓库

    这个具体深入的概念,我还在研究中,有时间补充下,这里大概了解用法以及概念,留一个坑

            /// <summary>
            /// log4net 仓储库
            /// </summary>
            public static ILoggerRepository repository { get; set; }

    Repository可以说成基于一个log4net配置节创建的log4net容器,它根据log4net配置节的指示创建其他所有对象(Logger/Appender/Filter/Layout等等)并保有他们的实例,随时为你所用。

     每个Repository都有自己唯一的名字,如 root。

     一般而言一个AppDomain(或者说一个进程)有一个Repository,该AppDomain下所有程序集Assembly都可以使用这个Repository。Repository需要实现ILoggerRepository,log4net中log4net.Repository.Hierarchy.Hierarchy就通过继承LoggerRepositorySkeleton实现了ILoggerRepository,它也是log4net中唯一实现ILoggerRepository的类。     

    2、在 Startup.cs 启动文件的 构造函数中,添加日志启动

     //log4net
     repository = LogManager.CreateRepository("");//需要获取日志的仓库名,也就是你的当然项目名
    //指定配置文件,如果这里你遇到问题,应该是使用了InProcess模式,请查看Blog.Core.csproj,并删之
     XmlConfigurator.Configure(repository, new FileInfo("log4net.config"));//配置文件

     

    3、配置 log4net.config 文件

     在 Blog.core 根目录下,添加 Log4net.config

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
      </configSections>
    
      <log4net>
        <!-- 将日志以回滚文件的形式写到文件中 -->
        <!-- 按日期切分日志文件,并将日期作为日志文件的名字 -->
        <appender name="RollingFileAppenderNameByDate" type="log4net.Appender.RollingFileAppender">
          <!-- 日志文件存放位置,可以为绝对路径也可以为相对路径 -->
          <file value="C:Logs" />
          <!-- 将日志信息追加到已有的日志文件中-->
          <appendToFile value="true" />
          <!-- 最小锁定模式,以允许多个进程可以写入同一个文件 -->
          <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
          <!-- 指定按日期切分日志文件 -->
          <rollingStyle value="Date" />
          <!-- 日志文件的命名规则 -->
          <datePattern value="&quot;UtilLogs_&quot;yyyyMMdd&quot;.log&quot;" />
          <!-- 当将日期作为日志文件的名字时,必须将staticLogFileName的值设置为false -->
          <staticLogFileName value="false" />
          <!-- 日志显示模板 -->
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="【异常时间】:%date %newline%message%newline--------------------------------------------------------------------%newline" />
          </layout>
        </appender>
    
        <root>
          <!-- 控制级别,由低到高:ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF -->
          <!-- 比如定义级别为INFO,则INFO级别向下的级别,比如DEBUG日志将不会被记录 -->
          <!-- 如果没有定义LEVEL的值,则缺省为DEBUG -->
          <level value="ALL" />
          <!-- 按日期切分日志文件,并将日期作为日志文件的名字 -->
          <appender-ref ref="RollingFileAppenderNameByDate" />
        </root>
      </log4net>
    </configuration>

    最终的结构是

    这个时候,我们已经把日志添加好了,可以在任何需用用的地方去使用了,你会问了,不是全局么,总不能每个方法都写 try catch 吧,有想法,请往下看。

    四、定义全局异常过滤器

    提醒下:

    1、下边的全局处理中,网友@梦亦晓:IExceptionFilter只能抓取Action执行过程中出现的未处理的异常。如果想捕获view中的异常需要在startup中的Configure中 实现app.UseExceptionHandler();

    1、日志接口进行注入

     在 启动文件的 ConfigureServices方法中,添加服务注入

       //log日志注入
       services.AddSingleton<ILoggerHelper, LogHelper>();

    2、Blog.core 新建Filter 文件夹,添加 GlobalExceptionFilter.cs

    namespace Blog.Core.Filter
    {
        /// <summary>
        /// 全局异常错误日志
        /// </summary>
        public class GlobalExceptionsFilter : IExceptionFilter
        {
            private readonly IHostingEnvironment _env;
            private readonly ILoggerHelper _loggerHelper;
            public GlobalExceptionsFilter(IHostingEnvironment env, ILoggerHelper loggerHelper)
            {
                _env = env;
                _loggerHelper = loggerHelper;
            }
            public void OnException(ExceptionContext context)
            {
                var json = new JsonErrorResponse();
                json.Message = context.Exception.Message;//错误信息
                if (_env.IsDevelopment())
                {
                    json.DevelopmentMessage = context.Exception.StackTrace;//堆栈信息
                }
                context.Result = new InternalServerErrorObjectResult(json);
    
                //采用log4net 进行错误日志记录
                _loggerHelper.Error(json.Message, WriteLog(json.Message, context.Exception));
    
            }
    
            /// <summary>
            /// 自定义返回格式
            /// </summary>
            /// <param name="throwMsg"></param>
            /// <param name="ex"></param>
            /// <returns></returns>
            public string WriteLog(string throwMsg, Exception ex)
            {
                return string.Format("【自定义错误】:{0} 
    【异常类型】:{1} 
    【异常信息】:{2} 
    【堆栈调用】:{3}", new object[] { throwMsg,
                    ex.GetType().Name, ex.Message, ex.StackTrace });
            }
    
        }
        public class InternalServerErrorObjectResult : ObjectResult
        {
            public InternalServerErrorObjectResult(object value) : base(value)
            {
                StatusCode = StatusCodes.Status500InternalServerError;
            }
        }
        //返回错误信息
        public class JsonErrorResponse
        {
            /// <summary>
            /// 生产环境的消息
            /// </summary>
            public string Message { get; set; }
            /// <summary>
            /// 开发环境的消息
            /// </summary>
            public string DevelopmentMessage { get; set; }
        }
    
    }

    3、在启动服务中,注入全局异常

    //注入全局异常捕获
    services.AddMvc(o =>
    {
        o.Filters.Add(typeof(GlobalExceptionsFilter));
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    结构是这样的

    4、自定义错误进行测试

     我故意写错数据库连接字符串,看看 swagger 如何报错

    看看日志是如何记录的

    五、结语

    今天是不定期更新系列,也是群里小伙伴提出来的想法,我自己简单搞搞,这里希望大家可以积极提意见哟,如果技术不错,我会把你的个人主页和Git放到文章中,也算是鼓励大家的一个方式了。

    1、网友好的点子:

    1.  在.NET Core中使用Exceptionless分布式日志收集框架

    六、Github & Gitee

    https://github.com/anjoy8/Blog.Core

    https://gitee.com/laozhangIsPhi/Blog.Core

  • 相关阅读:
    201521044091《Java程序设计》第7周学习总结
    201521044091《java程序设计》第四次总结
    201521044091 《java程序设计》第八周学习总结
    201521044091 《Java程序设计》第5周学习总结
    201521044091 《Java程序设计》第2周学习总结
    201521044091 《Java程序设计》第3周学习总结
    MySQL设置字符集CHARACTER SET
    Create My MySQL configuration by Percona
    How to use jQuery to manipulate Cookies
    How to use OpenXml to import xml data to Sql server
  • 原文地址:https://www.cnblogs.com/laozhang-is-phi/p/9855836.html
Copyright © 2011-2022 走看看