结合 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学习视频里面一开始都会提到 ...
随机推荐
- ES[7.6.x]学习笔记(八)数据的增删改
在前面几节的内容中,我们学习索引.字段映射.分析器等,这些都是使用ES的基础,就像在数据库中创建表一样,基础工作做好以后,我们就要真正的使用它了,这一节我们要看看怎么向索引里写入数据.修改数据.删除数 ...
- 使用Android studio过程中发现的几个解决R变红的办法
刚开始使用Android studio的时候,好几次碰见新建了一个xml文件,或者new了一个activity之后,Android studio莫名其妙的报错了,而显示红色的地方就是常用的(R.id. ...
- ActiveMQ 持久订阅者,执行结果与初衷相违背,验证离线订阅者无效,问题解决
导读 最新在接触ActiveMQ,里面有个持久订阅者模块,功能是怎么样也演示不出来效果.配置参数比较简单(配置没几个参数),消费者第一次运行时,需要指定ClientID(此时Broker已经记录离线订 ...
- STM32F767ZI NUCLEO144 基于CubeIDE快速开发入门指南
刚入手的NUCLEO-F767ZI:整合官网资源,理清思路,便于快速进行快发: 文章目录 1 NUCLEO 系列 2 NUCLEO-F767ZI 3 环境搭建 3.1 Keil/IAR安装 3.2 C ...
- input在IOS中的聚焦问题
关于input输入框在iPhone手机中的聚焦问题,开发中是会经常遇到的,在一般的浏览器中,我们一般是通过 document.getElementById('opop').focus(); 来获取焦点 ...
- quartus II Warning 好的时序是设计出来的,不是约束出来的
一.Warning (15714): Some pins have incomplete I/O assignments. Refer to the I/O Assignment Warnings r ...
- C++17结构化绑定
动机 std::map<K, V>的insert方法返回std::pair<iterator, bool>,两个元素分别是指向所插入键值对的迭代器与指示是否新插入元素的布尔值, ...
- Redis学习笔记(七) 数据库
Redis 服务器将所有的数据库都保存在服务器状态redisServer结构的db数组中,db数组的每个项都是一个redisDB: struct redisServer{ //一个数组保存着服务器中的 ...
- vue组件中的“:”、“@”、“.”属性
冒号属性 :是指令 v-bind 的缩写,是为了动态绑定数据,用于响应式地更新 HTML 特性. 加了冒号,后面是变量或表达式:不加冒号的是字符串. 如图:将Home组件中的 probe-type 的 ...
- Mysql之Linux中mariadb主从复制
master主机mysql安装配置 1.下载mariadb(Centos7开始mysql的名字) (1)其它方式(不推荐):rpm安装/软件源安装 (2)yum安装(推荐): ①centos官方的yu ...