结合 AOP 轻松处理事件发布处理日志
结合 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
- https://www.cnblogs.com/weihanli/p/12941919.html
- https://www.cnblogs.com/weihanli/p/implement-event-queue.html
- https://github.com/WeihanLi/WeihanLi.Common
- https://github.com/WeihanLi/WeihanLi.Common/blob/dev/samples/AspNetCoreSample/Startup.cs
结合 AOP 轻松处理事件发布处理日志的更多相关文章
- Springboot中使用AOP统一处理Web请求日志
title: Springboot中使用AOP统一处理Web请求日志 date: 2017-04-26 16:30:48 tags: ['Spring Boot','AOP'] categories: ...
- Spring Boot 2.0 教程 | AOP 切面统一打印请求日志
欢迎关注微信公众号: 小哈学Java 文章首发于个人网站 https://www.exception.site/springboot/spring-boot-aop-web-request 本节中,您 ...
- spring AOP自定义注解方式实现日志管理
今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在appli ...
- 46. Spring Boot中使用AOP统一处理Web请求日志
在之前一系列的文章中都是提供了全部的代码,在之后的文章中就提供核心的代码进行讲解.有什么问题大家可以给我留言或者加我QQ,进行咨询. AOP为Aspect Oriented Programming的缩 ...
- 关于spring 事务 和 AOP 管理事务和打印日志问题
关于spring 事务 和 AOP 管理事务和打印日志问题 1. 就是支持事务注解的(@Transactional) . 可以在server层总使用@Transactional,进行方法内的事务管 ...
- SpringBoot2.0 使用AOP统一处理Web请求日志(完整版)
一,加入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...
- SpringSecurity权限管理系统实战—八、AOP 记录用户、异常日志
目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...
- 十:SpringBoot-配置AOP切面编程,解决日志记录业务
SpringBoot-配置AOP切面编程,解决日志记录业务 1.AOP切面编程 1.1 AOP编程特点 1.2 AOP中术语和图解 2.SpringBoot整合AOP 2.1 核心依赖 2.2 编写日 ...
- 运用Spring Aop,一个注解实现日志记录
运用Spring Aop,一个注解实现日志记录 1. 介绍 我们都知道Spring框架的两大特性分别是 IOC (控制反转)和 AOP (面向切面),这个是每一个Spring学习视频里面一开始都会提到 ...
随机推荐
- 【Spark】SparkStreaming的容错机制
文章目录 检查点机制 驱动器程序容错 工作节点容错 接收器容错 处理保证 检查点机制 Metadata checkpointing -- 将定义流计算的信息存入容错的系统如HDFS. Data che ...
- 【Hadoop离线基础总结】Hive的基本操作
Hive的基本操作 创建数据库与创建数据库表 创建数据库的相关操作 创建数据库:CREATE TABLE IF NOT EXISTS myhive hive创建表成功后的存放位置由hive-site. ...
- 初探Redis-基础类型Hash
Redis存在五种基础类型:字符串(String).队列(List).哈希(Hash).集合(Set).有序集合(Sorted Set).本次列举出Hash的常用操作. Redis官网:https:/ ...
- 生产者消费者问题中的同步机制JAVA设计和实现
目录 问题描述 问题分析 利用记录型信号量解决 运行环境 实现思路 代码实现 运行截图 过程中出现的问题和注意点 利用AND信号集解决 运行环境 实现思路 代码实现 运行截图 问题描述 若干进程通过有 ...
- [hdu1242]优先队列
题意:给一个地图,'x'走一步代价为2,'.'走一步代价为1,求从s到t的最小代价.裸优先队列. #pragma comment(linker, "/STACK:10240000,10240 ...
- vim(vi)的常用快捷键
Vim 简介 vim是什么?听说它是编辑器之神!神一样的存在,而我却经常用上下左右箭头来移动光标,实在是有点对不起它,所以想着稍微学学一点神的技能吧! 一. vim的三种模式 Nomal mode:默 ...
- POI 导入excel数据自动封装成model对象--介绍
1.项目开发中,导入输入应该是常用的基本功能.我们经常会使用excel将数据导入到数据库,在导入之前必须得将excel数据转换成javaBean对象 2.由于此功能经常使用,所以开发此工具类方便日后轻 ...
- 站在CSS3的肩上定义选择器
按上下文选择元素 按祖先元素选择要格式化的元素 输入ancestor,这里的ancestor是希望格式化的元素的祖先元素的选择器. 输入一个空格(必不可少). 如果需要,对后续的每个祖先元素重复第(1 ...
- quartz 定时任务调度管理器
本项目使用的是spring-quartz 以下配置可以开启多个已知定时任务 <?xml version="1.0" encoding="UTF-8"?&g ...
- @vue/cli 4.0.5 学习记录
1. Vue CLI (@vue/cli) 是一个全局安装的 npm 包,提供了终端里的 vue 命令.Vue CLI 插件的名字以 @vue/cli-plugin- (内建插件) 或 vue-cli ...