缘起

哈喽我是不定期更新的日常,昨天群里小伙伴问到了记录日志,当然,以前我也挖过这个坑,后来一直没有来得及填上,也想着 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} \r\n【异常类型】:{1} \r\n【异常信息】:{2} \r\n【堆栈调用】:{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

从壹开始前后端分离 [.netCore 不定期更新 ] 三十五║ 完美实现全局异常日志记录的更多相关文章

  1. 采用异步来实现重新连接服务器或者重新启动服务 C#中类的属性的获取 SignalR2简易数据看板演示 C#动态调用泛型类、泛型方法 asp .net core Get raw request. 从壹开始前后端分离[.NetCore 不定期更新] 38 ║自动初始化数据库

    采用异步来实现重新连接服务器或者重新启动服务 开启异步监听,不会导致主线程的堵塞,在服务异常断开后一直检测重新连接服务,成功连接服务后通知各个注册的客户端! #region 检测断线并重连OPC服务 ...

  2. k.tt 研究下生成的逻辑代码:从壹开始前后端分离 [.netCore 填坑 ] 三十二║ 四种方法快速实现项目的半自动化搭建

    更新 1.更新小伙伴 @大龄Giser 提出好点子:试试VS的插件扩展:VSIX.ItemProject等,将T4模板给制作插件,这里先记下,有懂的小伙伴可以自己先试试,我会在以后更新. 2.感谢小伙 ...

  3. 从壹开始前后端分离 [.netCore 填坑 ] 三十四║Swagger:API多版本控制,带来的思考

    前言 大家周二好呀,.net core + Vue 这一系列基本就到这里差不多了,今天我又把整个系列的文章下边的全部评论看了一下(我是不是很负责哈哈),提到的问题基本都解决了,还有一些问题,已经在QQ ...

  4. 从壹开始前后端分离[.netCore 不定期 ] 36 ║解决JWT自定义中间件授权过期问题

    缘起 哈喽,老张的不定期更新的日常又开始了,在咱们的前后端分离的.net core 框架中,虽然已经实现了权限验证<框架之五 || Swagger的使用 3.3 JWT权限验证[修改]>, ...

  5. 从壹开始前后端分离 [.netCore 填坑 ] 三十三║ ⅖ 种方法实现完美跨域

    缘起 哈喽大家周四好,趁着大家在团建的时候花一个下午学点儿东西,也是督促大家学习哟,希望大家看到老张的文章,可以有一丢丢的学习动力.不过话说过来,该吃的团建还是要去的,不能学我呀 [ /(ㄒoㄒ)/~ ...

  6. 从壹开始前后端分离[.NetCore] 37 ║JWT完美实现权限与接口的动态分配

    缘起 本文已经有了对应的管理后台,地址:https://github.com/anjoy8/Blog.Admin 哈喽大家好呀!又过去一周啦,这些天小伙伴们有没有学习呀,已经有一周没有更新文章了,不过 ...

  7. 从壹开始前后端分离[.NetCore ] 38 ║自动初始化数据库(不定期更新)

    缘起 哈喽大家好呀,我们又见面啦,这里先祝大家圣诞节快乐哟,昨天的红包不知道有没有小伙伴抢到呢.今天的这篇内容灰常简单,只是对我们的系统的数据库进行CodeFirst,然后就是数据处理,因为这几个月来 ...

  8. [转帖]从壹开始前后端分离【重要】║最全的部署方案 & 最丰富的错误分析

    从壹开始前后端分离[重要]║最全的部署方案 & 最丰富的错误分析 https://www.cnblogs.com/laozhang-is-phi/p/beautifulPublish-most ...

  9. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之七 || API项目整体搭建 6.2 轻量级ORM

    更新 1.在使用的时候,特别是更新数据的时候,如果不知道哪里有问题,可以查看数据库 和 实体类 的字段,是否大小写一致,比如 name 和 Name 2.在使用Sqlsugar 的 CodeFirst ...

随机推荐

  1. 如何打开JSP文件/JS和JSP的区别/Servlet的本质是什么,是如何工作的?

    一:如何打开JSP文件 1.安装JAVA 2.安装TOMCAT——免费开源的JAVAWEB服务器 3.安装ECLIPSE 二:JS和JSP区别 名字: JS:JavaScript JSP:Java S ...

  2. git学习(持续踩坑中🤣)

    https://segmentfault.com/q/1010000002457936 常见指令: 一.创建版本库 $ mkdir learngit 创建文件夹 $ cd learngit 进入文件夹 ...

  3. 拿到6个重磅offer的大神,超详细面试经验总结

    本人是国内某财经院校的小本,学校不算很有名,也没有很好的硬件条件,但是很幸运的在这样的就业大环境里拿了6个OFFER,其中包括一些股份制商业银行的总行,4大国有商业银行的省行,以及国内最一流国企的省公 ...

  4. java没有firendly访问类型

    java中只有public.private.protected.default这几种修饰符,没有friendly修饰符,没加修饰符就是friendly.friendly只是一种说法,把它认为是defa ...

  5. 对象和XML文件的转换

    很多时候,我们开发程序都需要使用到对象的XML序列化和反序列化,对象的XML序列化和反序列化,既可以使用XML对象(XmlDocument)进行操作,也可以使用XmlSerializer进行操作,两个 ...

  6. Guava新增集合类型-Multiset

    Guava新增集合类型-Multiset Guava引进了JDK里没有的,但是非常有用的一些新的集合类型.所有这些新集合类型都能和JDK里的集合平滑集成.Guava集合非常精准地实现了JDK定义的接口 ...

  7. 在vs中编写代码常用的快捷键

    作为一个程序员,能够熟悉使用各种快捷键,可以增加我们编写和调试代码的速度,下面我就对常使用的快捷键做一些总结,下面这些快捷键基本适用于所有版本的vs: 最给力: Ctrl+K+F   快速整理代码格式 ...

  8. bzoj3812&uoj37 主旋律

    正着做不好做,于是我们考虑反着来,如何计算一个点集s的答案呢,一定是所有的方案减去不合法的方案,不合法的方案一定是缩完点后是一个DAG,那么就一定有度数为0的scc,于是我们枚举s的子集,就是说这些点 ...

  9. 【最小生成树】Bzoj1601[Usaco2008 Oct]灌水

    Description Farmer John已经决定把水灌到他的n(1<=n<=300)块农田,农田被数字1到n标记.把一块土地进行灌水有两种方法,从其他农田饮水,或者这块土地建造水库. ...

  10. python常见的报错提示

    在运行或编写一个程序时常会遇到错误异常,这时python会给你一个错误提示类名,告诉出现了什么样的问题(Python是面向对象语言,所以程序抛出的异常也是类).能很好的理解这些错误提示类名所代表的意思 ...