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

Intro

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

事件发布日志

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

事件发布者接口定义:

  1. public interface IEventPublisher
  2. {
  3. /// <summary>
  4. /// publish an event
  5. /// </summary>
  6. /// <typeparam name="TEvent">event type</typeparam>
  7. /// <param name="event">event data</param>
  8. /// <returns>whether the operation succeed</returns>
  9. bool Publish<TEvent>(TEvent @event) where TEvent : class, IEventBase;
  10. /// <summary>
  11. /// publish an event async
  12. /// </summary>
  13. /// <typeparam name="TEvent">event type</typeparam>
  14. /// <param name="event">event data</param>
  15. /// <returns>whether the operation succeed</returns>
  16. Task<bool> PublishAsync<TEvent>(TEvent @event) where TEvent : class, IEventBase;
  17. }

事件发布日志拦截器:

  1. public class EventPublishLogInterceptor : AbstractInterceptor
  2. {
  3. public override async Task Invoke(IInvocation invocation, Func<Task> next)
  4. {
  5. Console.WriteLine("-------------------------------");
  6. Console.WriteLine($"Event publish begin, eventData:{invocation.Arguments.ToJson()}");
  7. var watch = Stopwatch.StartNew();
  8. try
  9. {
  10. await next();
  11. }
  12. catch (Exception ex)
  13. {
  14. Console.WriteLine($"Event publish exception({ex})");
  15. }
  16. finally
  17. {
  18. watch.Stop();
  19. Console.WriteLine($"Event publish complete, elasped:{watch.ElapsedMilliseconds} ms");
  20. }
  21. Console.WriteLine("-------------------------------");
  22. }
  23. }

事件处理日志

事件处理器接口定义:

  1. public interface IEventHandler
  2. {
  3. Task Handle(object eventData);
  4. }

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

  1. public class EventHandleLogInterceptor : IInterceptor
  2. {
  3. public async Task Invoke(IInvocation invocation, Func<Task> next)
  4. {
  5. Console.WriteLine("-------------------------------");
  6. Console.WriteLine($"Event handle begin, eventData:{invocation.Arguments.ToJson()}");
  7. var watch = Stopwatch.StartNew();
  8. try
  9. {
  10. await next();
  11. }
  12. catch (Exception ex)
  13. {
  14. Console.WriteLine($"Event handle exception({ex})");
  15. }
  16. finally
  17. {
  18. watch.Stop();
  19. Console.WriteLine($"Event handle complete, elasped:{watch.ElapsedMilliseconds} ms");
  20. }
  21. Console.WriteLine("-------------------------------");
  22. }
  23. }

AOP 配置

  1. Host.CreateDefaultBuilder(args)
  2. .ConfigureWebHostDefaults(builder =>
  3. {
  4. builder.UseStartup<Startup>();
  5. })
  6. .UseFluentAspectServiceProviderFactory(options =>
  7. {
  8. // 拦截器配置
  9. // 拦截 `IEventPublisher` 日志,注册事件发布日志拦截器
  10. options
  11. .InterceptType<IEventPublisher>()
  12. .With<EventPublishLogInterceptor>();
  13. // 拦截 `IEventHandler`,注册事件处理日志拦截器
  14. options.InterceptType<IEventHandler>()
  15. .With<EventHandleLogInterceptor>();
  16. }, builder =>
  17. {
  18. // 默认使用默认实现来生成代理,现在提供了 Castle 和 AspectCore 的扩展,也可以自己扩展实现自定义代理生成方式
  19. // 取消注释使用 Castle 来生成代理
  20. //builder.UseCastleProxy();
  21. }, t => t.Namespace?.StartsWith("WeihanLi") == false // 要忽略的类型断言
  22. )
  23. .Build()
  24. .Run();

More

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

  1. // pageView middleware
  2. app.Use((context, next) =>
  3. {
  4. var eventPublisher = context.RequestServices
  5. .GetRequiredService<IEventPublisher>();
  6. eventPublisher.Publish(new PageViewEvent()
  7. {
  8. Path = context.Request.Path.Value,
  9. });
  10. return next();
  11. });

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

  1. public class EventConsumer : BackgroundService
  2. {
  3. private readonly IEventQueue _eventQueue;
  4. private readonly IEventHandlerFactory _eventHandlerFactory;
  5. public EventConsumer(IEventQueue eventQueue, IEventHandlerFactory eventHandlerFactory)
  6. {
  7. _eventQueue = eventQueue;
  8. _eventHandlerFactory = eventHandlerFactory;
  9. }
  10. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  11. {
  12. while (!stoppingToken.IsCancellationRequested)
  13. {
  14. var queues = await _eventQueue.GetQueuesAsync();
  15. if (queues.Count > 0)
  16. {
  17. await queues.Select(async q =>
  18. {
  19. var @event = await _eventQueue.DequeueAsync(q);
  20. if (null != @event)
  21. {
  22. var handlers = _eventHandlerFactory.GetHandlers(@event.GetType());
  23. if (handlers.Count > 0)
  24. {
  25. await handlers
  26. .Select(h => h.Handle(@event))
  27. .WhenAll()
  28. ;
  29. }
  30. }
  31. })
  32. .WhenAll()
  33. ;
  34. }
  35. await Task.Delay(1000, stoppingToken);
  36. }
  37. }
  38. }

完整的示例代码可以从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. python学习之break和continue在for循环中的使用(案例:打印出10以内的偶数,并且只要前三个偶数)

    运行程序,break是整个程序都跳出 continue则表示跳过当前一次循环,然后继续执行循环

  2. python 读取矢量文件

    #导入包 from osgeo import ogr #打开文件(False - read only, True - read/write) filename = "文件名.shp" ...

  3. Qt 视频播放器

    #include <phonon/VideoPlayer> #include <phonon/SeekSlider> #include <phonon/MediaObje ...

  4. CF#214 C. Dima and Salad 01背包变形

    C. Dima and Salad 题意 有n种水果,第i个水果有一个美味度ai和能量值bi,现在要选择部分水果做沙拉,假如此时选择了m个水果,要保证\(\frac{\sum_{i=1}^ma_i}{ ...

  5. JDBC02 加载JDBC驱动 建立连接

    JDBC(Java Database Connection)为Java开发者使用数据库提供了统一的编程接口 sun公司由于不知道各个主流商用数据库的程序代码,因此无法自己写代码连接各个数据库,因此su ...

  6. 【hdu1006】解方程

    http://acm.hdu.edu.cn/showproblem.php?pid=1006 这题坑了我好久,发现居然是一个除法变成了整除,TAT,所以建议在写较长的运算表达式的时候出现了除法尽量加个 ...

  7. iview tree 绑定数据

    官方文档 :https://www.iviewui.com/components/tree 效果图 1 主体分析 <Tree ref="tree" :data="t ...

  8. .NET Core接入ElasticSearch 7.5

    写在前面 最近一段时间,团队在升级ElasticSearch(以下简称ES),从ES 2.2升级到ES 7.5.也是这段时间,我从零开始,逐步的了解了ES,中间也踩了不少坑,所以特地梳理和总结一下相关 ...

  9. [Abp vNext 入坑分享] - 6.完整接入swagger

    前言 由于最近一直在修改一下排版,同时找了非技术的朋友帮忙看一下排版的问题,现在已经基本上确定了排版和样式了.更新可以恢复正常了. 作为一个写前端代码基本只写js不写css的开发,搞排版真的头疼..各 ...

  10. mysqldump 参数--single-transaction

    其实很简单,single-transaction可以让mysqldump 的时候不锁表.但是他有3个前提 innodb的引擎 不能在执行的同时,有其他alter table ,drop table,r ...