一、全局日志
在第一章介绍项目结构时,有提到.NET Core启动时默认加载了日志服务,且在appsetting.json文件配置了一些日志的设置,根据设置的日志等级的不同可以进行不同级别的信息的显示,但它无法做到输出固定格式的log信息至本地磁盘或是数据库,所以需要我们自己手动实现,而我们可以借助日志框架实现。
ps:在第7章节中我们记录的是数据处理层方法调用的日志信息,这里记录的则是ASP.NET Core WebAPI层级的日志信息,两者有所差异
1、引入日志框架
.NET程序中常用的日志框架有log4net,serilog 和Nlog,这里我们使用Serilog来实现相关功能,在BlogSystem.Core层使用NuGet安装Serilog.AspNetCore,同时还需要搜索Serilog.Skins安装希望支持的功能,这里我们希望添加对文件和控制台的输出,所以选择安装的是Serilog.Skins.File和Serilog.Skins.Console
需要注意的是Serilog是不受appsetting.json的日志设置影响的,且它可以根据命名空间重写记录级别。还有一点需要注意的是需要手动对Serilog对象进行资源的释放,否则在系统运行期间,无法打开日志文件。
2、系统添加
在BlogSystem.Core项目中添加一个Logs文件夹,并在Program类中进行Serilog对象的添加和使用,如下:
3、全局添加
1、这个时候其实系统已经使用Serilog替换了系统自带的log对象,如下图,Serilog会根据相关信息进行高亮显示:
2、这个时候问题就来了,我们怎么才能进行全局的添加呢,总不能一个方法一个方法的添加吧?还记得之前我们介绍AOP时提到的过滤器Filter吗?ASP.NET Core中一共有五类过滤器,分别是:
授权过滤器Authorization Filter:优先级最高,用于确定用户是否获得授权。如果请求未被授权,则授权过滤器会使管道短路;
资源过滤器Resource Filter:授权后运行,会在Authorization之后,Model Binding之前执行,可以实现类似缓存的功能;
方法过滤器Action Filter:在控制器的Action方法执行之前和之后被调用,可以更改传递给操作的参数或更改从操作返回的结果;
异常过滤器Exception Filter:当Action方法执行过程中出现了未处理的异常,将会进入这个过滤器进行统一处理;
结果过滤器Result Filter:执行操作结果之前和之后运行,仅在action方法成功执行后才运行;
过滤器的具体执行顺序如下:
3、这里我们可以借助异常过滤器实现全局日志功能的添加;在在BlogSystem.Core项目添加一个Filters文件夹,添加一个名为ExceptionFilter的类,继承IExceptionFilter接口,这里是参考老张的哲学的简化版本,实现如下:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Serilog;
using System;
namespace BlogSystem.Core.Filters
{
public class ExceptionsFilter : IExceptionFilter
{
private readonly ILogger<ExceptionsFilter> _logger;
public ExceptionsFilter(ILogger<ExceptionsFilter> logger)
{
_logger = logger;
}
public void OnException(ExceptionContext context)
{
try
{
//错误信息
var msg = context.Exception.Message;
//错误堆栈信息
var stackTraceMsg = context.Exception.StackTrace;
//返回信息
context.Result = new InternalServerErrorObjectResult(new { msg, stackTraceMsg });
//记录错误日志
_logger.LogError(WriteLog(context.Exception));
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
finally
{
//记得释放,否则运行时无法打开日志文件
Log.CloseAndFlush();
}
}
//返回500错误
public class InternalServerErrorObjectResult : ObjectResult
{
public InternalServerErrorObjectResult(object value) : base(value)
{
StatusCode = StatusCodes.Status500InternalServerError;
}
}
//自定义格式内容
public string WriteLog(Exception ex)
{
return $"【异常信息】:{ex.Message} \r\n 【异常类型】:{ex.GetType().Name} \r\n【堆栈调用】:{ex.StackTrace}";
}
}
}
4、在Startup类的ConfigureServices方法中进行异常处理过滤器的注册,如下:
5、我们在控制器方法中抛出一个异常,分别查看效果如下,如果觉得信息太多,可调整日志记录级别: