[Abp 源码分析]十五、自动审计记录

Abp 框架为我们自带了审计日志功能,审计日志可以方便地查看每次请求接口所耗的时间,能够帮助我们快速定位到某些性能有问题的接口。除此之外,审计日志信息还包含有每次调用接口时客户端请求的参数信息,客户端的 IP 与客户端使用的浏览器。有了这些数据之后,我们就可以很方便地复现接口产生 BUG 时的一些环境信息。

当然如果你脑洞更大的话,可以根据这些数据来开发一个可视化的图形界面,方便开发与测试人员来快速定位问题。

PS:

如果使用了 Abp.Zero 模块则自带的审计记录实现是存储到数据库当中的,但是在使用 EF Core + MySQL(EF Provider 为 Pomelo.EntityFrameworkCore.MySql) 在高并发的情况下会有数据库连接超时的问题,这块推荐是重写实现,自己采用 Redis 或者其他存储方式。

如果需要禁用审计日志功能,则需要在任意模块的预加载方法(PreInitialize()) 当中增加如下代码关闭审计日志功能。

public class XXXStartupModule { public override PreInitialize() { // 禁用审计日志 Configuration.Auditing.IsEnabled = false; } } 1.启动流程

审计组件与参数校验组件一样,都是通过 MVC 过滤器与 Castle 拦截器来实现记录的。也就是说,在每次调用接口/方法时都会进入 过滤器/拦截器 并将其写入到数据库表 AbpAuditLogs 当中。

其核心思想十分简单,就是在执行具体接口方法的时候,先使用 StopWatch 对象来记录执行完一个方法所需要的时间,并且还能够通过 HttpContext 来获取到一些客户端的关键信息。

[Abp 源码分析]十五、自动审计记录

2.1 过滤器注入

同上一篇文章所讲的一样,过滤器是在 AddAbp() 方法内部的 ConfigureAspNetCore() 方法注入的。

private static void ConfigureAspNetCore(IServiceCollection services, IIocResolver iocResolver) { // ... 其他代码 //Configure MVC services.Configure<MvcOptions>(mvcOptions => { mvcOptions.AddAbp(services); }); // ... 其他代码 }

而下面就是过滤器的注入方法:

internal static class AbpMvcOptionsExtensions { public static void AddAbp(this MvcOptions options, IServiceCollection services) { // ... 其他代码 AddFilters(options); // ... 其他代码 } // ... 其他代码 private static void AddFilters(MvcOptions options) { // ... 其他过滤器注入 // 注入审计日志过滤器 options.Filters.AddService(typeof(AbpAuditActionFilter)); // ... 其他过滤器注入 } // ... 其他代码 } 2.2 拦截器注入

注入拦截器的地方与 DTO 自动验证的拦截器的位置一样,都是在 AbpBootstrapper 对象被构造的时候进行注册。

public class AbpBootstrapper : IDisposable { private AbpBootstrapper([NotNull] Type startupModule, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null) { // ... 其他代码 if (!options.DisableAllInterceptors) { AddInterceptorRegistrars(); } } // ... 其他代码 // 添加各种拦截器 private void AddInterceptorRegistrars() { ValidationInterceptorRegistrar.Initialize(IocManager); AuditingInterceptorRegistrar.Initialize(IocManager); EntityHistoryInterceptorRegistrar.Initialize(IocManager); UnitOfWorkRegistrar.Initialize(IocManager); AuthorizationInterceptorRegistrar.Initialize(IocManager); } // ... 其他代码 }

转到 AuditingInterceptorRegistrar 的具体实现可以发现,他在内部针对于审计日志拦截器的注入是区分了类型的。

internal static class AuditingInterceptorRegistrar { public static void Initialize(IIocManager iocManager) { iocManager.IocContainer.Kernel.ComponentRegistered += (key, handler) => { // 如果审计日志配置类没有被注入,则直接跳过 if (!iocManager.IsRegistered<IAuditingConfiguration>()) { return; } var auditingConfiguration = iocManager.Resolve<IAuditingConfiguration>(); // 判断当前 DI 所注入的类型是否应该为其绑定审计日志拦截器 if (ShouldIntercept(auditingConfiguration, handler.ComponentModel.Implementation)) { handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(AuditingInterceptor))); } }; } // 本方法主要用于判断当前类型是否符合绑定拦截器的条件 private static bool ShouldIntercept(IAuditingConfiguration auditingConfiguration, Type type) { // 首先判断当前类型是否在配置类的注册类型之中,如果是,则进行拦截器绑定 if (auditingConfiguration.Selectors.Any(selector => selector.Predicate(type))) { return true; } // 当前类型如果拥有 Audited 特性,则进行拦截器绑定 if (type.GetTypeInfo().IsDefined(typeof(AuditedAttribute), true)) { return true; } // 如果当前类型内部的所有方法当中有一个方法拥有 Audited 特性,则进行拦截器绑定 if (type.GetMethods().Any(m => m.IsDefined(typeof(AuditedAttribute), true))) { return true; } // 都不满足则返回 false,不对当前类型进行绑定 return false; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppgsg.html