一、简要说明
ABP vNext 当中的审计模块早在 依赖注入与拦截器一文中有所提及,但没有详细的对其进行分析。
审计模块是 ABP vNext 框架的一个基本组件,它能够提供一些实用日志记录。不过这里的日志不是说系统日志,而是说接口每次调用之后的执行情况(执行时间、传入参数、异常信息、请求 IP)。
除了常规的日志功能以外,关于 实体 和 聚合 的审计字段接口也是存放在审计模块当中的。(创建人、创建时间、修改人、修改时间、删除人、删除时间)
二、源码分析
2.1. 审计日志拦截器
2.1.1 审计日志拦截器的注册
Volo.Abp.Auditing 的模块定义十分简单,主要是提供了 审计日志拦截器 的注册功能。下面代码即在组件注册的时候,会调用 AuditingInterceptorRegistrar.RegisterIfNeeded
方法来判定是否为实现类型(ImplementationType) 注入审计日志拦截器。
public class AbpAuditingModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(AuditingInterceptorRegistrar.RegisterIfNeeded);
}
}
跳转到具体的实现,可以看到内部会结合三种类型进行判断。分别是 AuditedAttribute
、IAuditingEnabled
、DisableAuditingAttribute
。
前两个作用是,只要类型标注了 AuditedAttribute
特性,或者是实现了 IAuditingEnable
接口,都会为该类型注入审计日志拦截器。
而 DisableAuditingAttribute
类型则相反,只要类型上标注了该特性,就不会启用审计日志拦截器。某些接口需要 提升性能 的话,可以尝试使用该特性禁用掉审计日志功能。
public static class AuditingInterceptorRegistrar
{
public static void RegisterIfNeeded(IOnServiceRegistredContext context)
{
// 满足条件时,将会为该类型注入审计日志拦截器。
if (ShouldIntercept(context.ImplementationType))
{
context.Interceptors.TryAdd<AuditingInterceptor>();
}
}
private static bool ShouldIntercept(Type type)
{
// 首先判断类型上面是否使用了辅助类型。
if (ShouldAuditTypeByDefault(type))
{
return true;
}
// 如果任意方法上面标注了 AuditedAttribute 特性,则仍然为该类型注入拦截器。
if (type.GetMethods().Any(m => m.IsDefined(typeof(AuditedAttribute), true)))
{
return true;
}
return false;
}
//TODO: Move to a better place
public static bool ShouldAuditTypeByDefault(Type type)
{
// 下面就是根据三种辅助类型进行判断,是否为当前 type 注入审计日志拦截器。
if (type.IsDefined(typeof(AuditedAttribute), true))
{
return true;
}
if (type.IsDefined(typeof(DisableAuditingAttribute), true))
{
return false;
}
if (typeof(IAuditingEnabled).IsAssignableFrom(type))
{
return true;
}
return false;
}
}
2.1.2 审计日志拦截器的实现
审计日志拦截器的内部实现,主要使用了三个类型进行协同工作。它们分别是负责管理审计日志信息的 IAuditingManager
,负责创建审计日志信息的 IAuditingHelper
,还有统计接口执行时常的 Stopwatch
。
整个审计日志拦截器的大体流程如下:
- 首先是判定 MVC 审计日志过滤器是否进行处理。
- 再次根据特性,和类型进行二次验证是否应该创建审计日志信息。
- 根据调用信息,创建
AuditLogInfo
和AuditLogActionInfo
审计日志信息。 - 调用
StopWatch
的计时方法,如果出现了异常则将异常信息添加到刚才构建的AuditLogInfo
对象中。 - 无论是否出现异常,都会进入
finally
语句块,这个时候会调用StopWatch
实例的停止方法,并统计完成执行时间。
public override async Task InterceptAsync(IAbpMethodInvocation invocation)
{
if (!ShouldIntercept(invocation, out var auditLog, out var auditLogAction))
{
await invocation.ProceedAsync();
return;
}
// 开始进行计时操作。
var stopwatch = Stopwatch.StartNew();
try
{
await invocation.ProceedAsync();
}
catch (Exception ex)
{
// 如果出现了异常,一样的将异常信息添加到审计日志结果中。
auditLog.Exceptions.Add(ex);
throw;
}
finally
{
// 统计完成,并将信息加入到审计日志结果中。
stopwatch.Stop();
auditLogAction.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
auditLog.Actions.Add(auditLogAction);
}
}
可以看到,只有当 ShouldIntercept()
方法返回 true
的时候,下面的统计等操作才会被执行。
protected virtual bool ShouldIntercept(
IAbpMethodInvocation invocation,
out AuditLogInfo auditLog,
out AuditLogActionInfo auditLogAction)
{
auditLog = null;
auditLogAction = null;
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Auditing))
{
return false;
}
// 如果没有获取到 Scop,则返回 false。
var audi