首先想一想,如果有这么一个需求,要记录整个项目的接口和调用情况,当然如果只是控制器的话,还是挺简单的,直接用一个过滤器或者一个中间件,还记得咱们开发Swagger拦截权限验证的中间件么,那个就很方便的把用户调用接口的名称记录下来,当然也可以写成一个切面,但是如果想看下与Service或者Repository层的调用情况呢,好像目前咱们只能在Service层或者Repository层去写日志记录了,那样的话,不仅工程大(当然你可以用工厂模式),而且耦合性瞬间就高了呀,想象一下,如果日志要去掉,关闭,修改,需要改多少地方!您说是不是,好不容易前边的工作把层级的耦合性降低了。别慌,这个时候就用到了AOP和Autofac的Castle结合的完美解决方案了。
下面是我自己根据老张博客实现的记录日志功能和体会,只针对于实现记录日志这一功能,其他的涉及还不深
正文开始
一开始我记录日志是Nlog,采用autofac注入。然后在服务层仓储层或者你想记录的地方去注入然后记录日志,NLog的使用教程我也有记录,可以翻看一下:
但是这样每一个方法每一个类都要进行注入。上边 的引用所说的 耦合性增加,因为我是通过Autofac程序集依赖注入的,耦合性倒还好。最大的问题可能在于,每一个方法我都要单独的去写日志记录。就很麻烦,因此采用这种AOP拦截记录日志,方便很多。
专业术语很多,我也不是很理解,感觉挺牛的。不过单独针对于记录日志这方面,还是挺简单的。
老张的项目结构式有一个扩展的类库,专门来搞这些。我添加到了Common公共类库里,操作完之后,感觉对于我自己的项目直接写在API底下应该更方便,因为一开始使用autofac是安装到仓储层了。这样一层层的调用 API>IService>IRepository 可以少安装Get包和少一些引用。不过也没什么关系了,Common本身我打算的就是任何地方都可以去引用(不过因为没有Nlog包所有多安装了一个,以后精通了再修改完善吧)。
创建一个类,继承[IInterceptor] 我这里是记录日志的,叫LogAOP
继承之后,实现这个类的唯一方法Intercept
记录日志的过程在方法Intercept里实现,我会配合我自己的代码来写对应的注释。
public class LogAOP : IInterceptor { protected readonly ILogger<LogAOP> Nlog; /// <summary> /// 采用Nlog记录日志,同样使用构造函数注入Nlog /// </summary> /// <param name="logger"></param> public LogAOP(ILogger<LogAOP> logger) { Nlog = logger; } /// <summary> /// 实现继承IInterceptor的方法 /// </summary> /// <param name="invocation"></param> public void Intercept(IInvocation invocation) { //记录日志的String,在请求接口的时候会先进行拦截,可以获取到方法的名称,方法在哪个位置,所带参数等 var dataIntercept = "\n\t" + $"【当前执行方法:{invocation.Method.Name}】\n\t" + $"【携带的参数有:{string.Join(",", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray())}】\n\t" + $"【当前执行方法的位置:{invocation.InvocationTarget}】\n\t"; try { //这个Proceed是执行这个方法,不写的话就204,不会处理该请求 invocation.Proceed(); //如果Proceed成功执行,则返回到请求的结果 dataIntercept += ($"【执行完成结果:】{JsonSerializer.Serialize(invocation.ReturnValue)}"); } catch (Exception e) { //如果Proceed里面异常了,就会在此处获取到异常信息。 dataIntercept += ($"【方法异常:】{e.Message + e.InnerException}"); } //为了在日志里划分区域范围,容易查看 dataIntercept += ("\n\n======================================================================="); //这个地方目前我不知道怎么解释,但是记录日志要写在里面 Parallel.For(0, 1, e => { Nlog.LogInformation(dataIntercept); }); } }
然后就是将这个类注入到程序内。红框里的是注入aop拦截新加 的,其余的就是注入仓储和服务层的,这种比较偏底层,目前我无法解释太清楚,如果有大佬看到了可以解释一二。我的理解呢就是
builder.RegisterType<LogAOP>();//将拦截器注册,但此时还没有确定注入到哪个地方……
EnableInterfaceInterceptors:添加一个拦截,表示给你的程序哪个位置添加拦截,比如我要添加我的Service和Repository层
InterceptedBy() :将XX拦截功能注册到服务,这个就是要将第一步你注入的拦截器添加到指定的位置