结合 AOP 轻松处理事件发布处理日志

Intro

前段时间,实现了 EventBus 以及 EventQueue 基于 Event 的事件处理,但是没有做日志(EventLog)相关的部分,原本想增加两个接口, 处理事件发布日志和事件处理日志,最近用了 AOP 的思想处理了 EntityFramework 的数据变更自动审计,于是想着事件日志也用 AOP 的思想来实现,而且可能用 AOP 来处理可能会更好一些,最近自己造了一个 AOP 的轮子 —— FluentAspects,下面的示例就以它来演示了,你也可以换成自己喜欢的 AOP 组件,思想是类似的

事件发布日志

事件发布日志只需要拦截事件发布的方法调用即可,在发布事件时进行拦截,在拦截器中根据需要进行日志记录即可

事件发布者接口定义:

public interface IEventPublisher
{
/// <summary>
/// publish an event
/// </summary>
/// <typeparam name="TEvent">event type</typeparam>
/// <param name="event">event data</param>
/// <returns>whether the operation succeed</returns>
bool Publish<TEvent>(TEvent @event) where TEvent : class, IEventBase; /// <summary>
/// publish an event async
/// </summary>
/// <typeparam name="TEvent">event type</typeparam>
/// <param name="event">event data</param>
/// <returns>whether the operation succeed</returns>
Task<bool> PublishAsync<TEvent>(TEvent @event) where TEvent : class, IEventBase;
}

事件发布日志拦截器:

public class EventPublishLogInterceptor : AbstractInterceptor
{
public override async Task Invoke(IInvocation invocation, Func<Task> next)
{
Console.WriteLine("-------------------------------");
Console.WriteLine($"Event publish begin, eventData:{invocation.Arguments.ToJson()}");
var watch = Stopwatch.StartNew();
try
{
await next();
}
catch (Exception ex)
{
Console.WriteLine($"Event publish exception({ex})");
}
finally
{
watch.Stop();
Console.WriteLine($"Event publish complete, elasped:{watch.ElapsedMilliseconds} ms");
}
Console.WriteLine("-------------------------------");
}
}

事件处理日志

事件处理器接口定义:

public interface IEventHandler
{
Task Handle(object eventData);
}

事件处理日志拦截器定义:

public class EventHandleLogInterceptor : IInterceptor
{
public async Task Invoke(IInvocation invocation, Func<Task> next)
{
Console.WriteLine("-------------------------------");
Console.WriteLine($"Event handle begin, eventData:{invocation.Arguments.ToJson()}");
var watch = Stopwatch.StartNew();
try
{
await next();
}
catch (Exception ex)
{
Console.WriteLine($"Event handle exception({ex})");
}
finally
{
watch.Stop();
Console.WriteLine($"Event handle complete, elasped:{watch.ElapsedMilliseconds} ms");
}
Console.WriteLine("-------------------------------");
}
}

AOP 配置

Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(builder =>
{
builder.UseStartup<Startup>();
})
.UseFluentAspectServiceProviderFactory(options =>
{
// 拦截器配置 // 拦截 `IEventPublisher` 日志,注册事件发布日志拦截器
options
.InterceptType<IEventPublisher>()
.With<EventPublishLogInterceptor>(); // 拦截 `IEventHandler`,注册事件处理日志拦截器
options.InterceptType<IEventHandler>()
.With<EventHandleLogInterceptor>();
}, builder =>
{
// 默认使用默认实现来生成代理,现在提供了 Castle 和 AspectCore 的扩展,也可以自己扩展实现自定义代理生成方式
// 取消注释使用 Castle 来生成代理
//builder.UseCastleProxy();
}, t => t.Namespace?.StartsWith("WeihanLi") == false // 要忽略的类型断言
)
.Build()
.Run();

More

事件发布示例,定义了一个发布事件的中间件:

// pageView middleware
app.Use((context, next) =>
{
var eventPublisher = context.RequestServices
.GetRequiredService<IEventPublisher>();
eventPublisher.Publish(new PageViewEvent()
{
Path = context.Request.Path.Value,
}); return next();
});

事件处理示例是用一个消息队列的模式来处理的,示例和前面的事件的文章类似,EventConsumer 是一个后台任务,完整代码示例如下:

public class EventConsumer : BackgroundService
{
private readonly IEventQueue _eventQueue;
private readonly IEventHandlerFactory _eventHandlerFactory; public EventConsumer(IEventQueue eventQueue, IEventHandlerFactory eventHandlerFactory)
{
_eventQueue = eventQueue;
_eventHandlerFactory = eventHandlerFactory;
} protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var queues = await _eventQueue.GetQueuesAsync();
if (queues.Count > 0)
{
await queues.Select(async q =>
{
var @event = await _eventQueue.DequeueAsync(q);
if (null != @event)
{
var handlers = _eventHandlerFactory.GetHandlers(@event.GetType());
if (handlers.Count > 0)
{
await handlers
.Select(h => h.Handle(@event))
.WhenAll()
;
}
}
})
.WhenAll()
;
} await Task.Delay(1000, stoppingToken);
}
}
}

完整的示例代码可以从https://github.com/WeihanLi/WeihanLi.Common/blob/dev/samples/AspNetCoreSample 获取

Reference

结合 AOP 轻松处理事件发布处理日志的更多相关文章

  1. Springboot中使用AOP统一处理Web请求日志

    title: Springboot中使用AOP统一处理Web请求日志 date: 2017-04-26 16:30:48 tags: ['Spring Boot','AOP'] categories: ...

  2. Spring Boot 2.0 教程 | AOP 切面统一打印请求日志

    欢迎关注微信公众号: 小哈学Java 文章首发于个人网站 https://www.exception.site/springboot/spring-boot-aop-web-request 本节中,您 ...

  3. spring AOP自定义注解方式实现日志管理

    今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在appli ...

  4. 46. Spring Boot中使用AOP统一处理Web请求日志

    在之前一系列的文章中都是提供了全部的代码,在之后的文章中就提供核心的代码进行讲解.有什么问题大家可以给我留言或者加我QQ,进行咨询. AOP为Aspect Oriented Programming的缩 ...

  5. 关于spring 事务 和 AOP 管理事务和打印日志问题

    关于spring 事务 和 AOP 管理事务和打印日志问题 1. 就是支持事务注解的(@Transactional) . ​ 可以在server层总使用@Transactional,进行方法内的事务管 ...

  6. SpringBoot2.0 使用AOP统一处理Web请求日志(完整版)

    一,加入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  7. SpringSecurity权限管理系统实战—八、AOP 记录用户、异常日志

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  8. 十:SpringBoot-配置AOP切面编程,解决日志记录业务

    SpringBoot-配置AOP切面编程,解决日志记录业务 1.AOP切面编程 1.1 AOP编程特点 1.2 AOP中术语和图解 2.SpringBoot整合AOP 2.1 核心依赖 2.2 编写日 ...

  9. 运用Spring Aop,一个注解实现日志记录

    运用Spring Aop,一个注解实现日志记录 1. 介绍 我们都知道Spring框架的两大特性分别是 IOC (控制反转)和 AOP (面向切面),这个是每一个Spring学习视频里面一开始都会提到 ...

随机推荐

  1. 【Spark】SparkStreaming的容错机制

    文章目录 检查点机制 驱动器程序容错 工作节点容错 接收器容错 处理保证 检查点机制 Metadata checkpointing -- 将定义流计算的信息存入容错的系统如HDFS. Data che ...

  2. 【Hadoop离线基础总结】Hive的基本操作

    Hive的基本操作 创建数据库与创建数据库表 创建数据库的相关操作 创建数据库:CREATE TABLE IF NOT EXISTS myhive hive创建表成功后的存放位置由hive-site. ...

  3. 初探Redis-基础类型Hash

    Redis存在五种基础类型:字符串(String).队列(List).哈希(Hash).集合(Set).有序集合(Sorted Set).本次列举出Hash的常用操作. Redis官网:https:/ ...

  4. 生产者消费者问题中的同步机制JAVA设计和实现

    目录 问题描述 问题分析 利用记录型信号量解决 运行环境 实现思路 代码实现 运行截图 过程中出现的问题和注意点 利用AND信号集解决 运行环境 实现思路 代码实现 运行截图 问题描述 若干进程通过有 ...

  5. [hdu1242]优先队列

    题意:给一个地图,'x'走一步代价为2,'.'走一步代价为1,求从s到t的最小代价.裸优先队列. #pragma comment(linker, "/STACK:10240000,10240 ...

  6. vim(vi)的常用快捷键

    Vim 简介 vim是什么?听说它是编辑器之神!神一样的存在,而我却经常用上下左右箭头来移动光标,实在是有点对不起它,所以想着稍微学学一点神的技能吧! 一. vim的三种模式 Nomal mode:默 ...

  7. POI 导入excel数据自动封装成model对象--介绍

    1.项目开发中,导入输入应该是常用的基本功能.我们经常会使用excel将数据导入到数据库,在导入之前必须得将excel数据转换成javaBean对象 2.由于此功能经常使用,所以开发此工具类方便日后轻 ...

  8. 站在CSS3的肩上定义选择器

    按上下文选择元素 按祖先元素选择要格式化的元素 输入ancestor,这里的ancestor是希望格式化的元素的祖先元素的选择器. 输入一个空格(必不可少). 如果需要,对后续的每个祖先元素重复第(1 ...

  9. quartz 定时任务调度管理器

    本项目使用的是spring-quartz 以下配置可以开启多个已知定时任务 <?xml version="1.0" encoding="UTF-8"?&g ...

  10. @vue/cli 4.0.5 学习记录

    1. Vue CLI (@vue/cli) 是一个全局安装的 npm 包,提供了终端里的 vue 命令.Vue CLI 插件的名字以 @vue/cli-plugin- (内建插件) 或 vue-cli ...