SpringCloud使用注解+AOP+MQ来实现日志管理模块

简介

无论在什么系统中,日志管理模块都属于十分重要的部分,接下来会通过注解+AOP+MQ的方式实现一个简易的日志管理系统

思路

  • 注解: 标记需要记录日志的方法

  • AOP: 通过AOP增强代码,利用后置/异常通知的方式获取相关日志信息,最后使用MQ将日志信息发送到专门处理日志的系统

  • RabbitMQ: 利用解耦、异步的特性,协调完成各个微服务系统之间的通信

1、日志表结构

表结构(sys_log):

  1. CREATE TABLE `sys_log` (
  2.   `id` int(11NOT NULL AUTO_INCREMENT COMMENT '唯一ID',
  3.   `opt_id` int(11DEFAULT NULL COMMENT '操作用户id',
  4.   `opt_name` varchar(50DEFAULT NULL COMMENT '操作用户名',
  5.   `log_type` varchar(20DEFAULT NULL COMMENT '日志类型',
  6.   `log_message` varchar(255DEFAULT NULL COMMENT '日志信息(具体方法名)',
  7.   `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  8.   PRIMARY KEY (`id`)
  9. ) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COMMENT='系统日志表';

实体类(SysLog):

  1. @Data
  2. public class SysLog  {
  3.  
  4.     private static final long serialVersionUID = 1L;
  5.  
  6.     /**
  7.      * 唯一ID
  8.      */
  9.     @TableId(value = "id", type = IdType.AUTO)
  10.     private Integer id;
  11.     /**
  12.      * 操作用户id
  13.      */
  14.     private Integer optId;
  15.     /**
  16.      * 操作用户名
  17.      */
  18.     private String optName;
  19.     /**
  20.      * 日志类型
  21.      */
  22.     private String logType;
  23.     /**
  24.      * 日志信息(具体方法名)
  25.      */
  26.     private String logMessage;
  27.     /**
  28.      * 创建时间
  29.      */
  30.     private Date createTime;
  31.  
  32. }

2、注解

注解(SystemLog):

仅作为标记的作用,目的让JVM可以识别,然后可以从中获取相关信息

  • @Target: 定义注解作用的范围,这里是方法

  • @Retention: 定义注解生命周期,这里是运行时

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface SystemLog {
  4.  
  5.     SystemLogEnum type();
  6.  
  7. }

枚举(SystemLogEnum):

限定日志类型范围

  1. public enum SystemLogEnum {
  2.  
  3.     SAVE_LOG("保存"),
  4.     DELETE_LOG("删除"),
  5.     REGISTER_LOG("注册"),
  6.     LOGIN_LOG("登录"),
  7.     LAUD_LOG("点赞"),
  8.     COLLECT_LOG("收藏"),
  9.     THROW_LOG("异常"),
  10.     ;
  11.     private String type;
  12.  
  13.     SystemLogEnum(String type) {
  14.         this.type = type;
  15.     }
  16.  
  17.     public String getType() {
  18.         return type;
  19.     }
  20. }

3、AOP切面

AOP(SysLogAspect):

实现代码的增强,主要通过动态代理方式实现的代码增强。拦截注解,并获取拦截到的相关信息,封装成日志对象发送到MQ队列(生产端)

  1. Component
  2. @Aspect
  3. @Slf4j
  4. public class SysLogAspect {
  5.  
  6.     @Autowired
  7.     MqStream stream;
  8.  
  9.     //切点
  10.     @Pointcut("@annotation(cn.zdxh.commons.utils.SystemLog)")
  11.     public void logPointcut(){}
  12.  
  13.     //后置通知
  14.     @After("logPointcut()")
  15.     public void afterLog(JoinPoint joinPoint) {
  16.         //一般日志
  17.         SysLog sysLog = wrapSysLog(joinPoint);
  18.  
  19.         log.info("Log值:"+sysLog);
  20.  
  21.         //发送mq消息
  22.         stream.logOutput().send(MessageBuilder.withPayload(sysLog).build());
  23.  
  24.     }
  25.  
  26.     //异常通知
  27.     @AfterThrowing(value = "logPointcut()", throwing = "e")
  28.     public void throwingLog(JoinPoint joinPoint, Exception e) {
  29.         //异常日志
  30.         SysLog sysLog = wrapSysLog(joinPoint);
  31.         sysLog.setLogType(SystemLogEnum.THROW_LOG.getType());
  32.         sysLog.setLogMessage(sysLog.getLogMessage()+"==="+e);
  33.  
  34.         log.info("异常Log值:"+sysLog);
  35.  
  36.         //发送mq消息
  37.         stream.logOutput().send(MessageBuilder.withPayload(sysLog).build());
  38.     }
  39.  
  40.     /**
  41.      * 封装SysLog对象
  42.      * @param joinPoint
  43.      * @return
  44.      */
  45.     public SysLog wrapSysLog(JoinPoint joinPoint){
  46.         //获取请求响应对象
  47.         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  48.         HttpServletRequest request = attributes.getRequest();
  49.         MethodSignature signature = (MethodSignature)joinPoint.getSignature();
  50.         SysLog sysLog = new SysLog();
  51.  
  52.         //获取方法全路径
  53.         String methodName = signature.getDeclaringTypeName()+"."+signature.getName();
  54.         //获取注解参数值
  55.         SystemLog systemLog = signature.getMethod().getAnnotation(SystemLog.class);
  56.         //从header取出token
  57.         String token = request.getHeader("token");
  58.         if (!StringUtils.isEmpty(token)) {
  59.             //操作人信息
  60.             Integer userId = JwtUtils.getUserId(token);
  61.             String username = JwtUtils.getUsername(token);
  62.             sysLog.setOptId(userId);
  63.             sysLog.setOptName(username);
  64.         }
  65.         if (!StringUtils.isEmpty(systemLog.type())){
  66.             sysLog.setLogType(systemLog.type().getType());
  67.         }
  68.         sysLog.setLogMessage(methodName);
  69.         sysLog.setCreateTime(new Date());
  70.         return sysLog;
  71.     }
  72.  
  73. }

3、RabbitMQ消息队列

MQ:

这里主要是通过Spring Cloud Stream集成的RabbitMQ

Spring Cloud Stream:

作为MQ的抽象层,已屏蔽各种MQ的各自名词,统称为input、output两大块。可以更方便灵活地切换各种MQ,如 kafka、RocketMQ等

 

(1)定义Input/Ouput接口(MqStream)

  1. @Component
  2. public interface MqStream {
  3.  
  4.     String LOG_INPUT = "log_input";
  5.  
  6.     String LOG_OUTPUT = "log_output";
  7.   
  8.     @Input(LOG_INPUT)
  9.     SubscribableChannel logInput();
  10.  
  11.     @Output(LOG_OUTPUT)
  12.     MessageChannel logOutput();
  13.  
  14. }

(2)MQ生产者

注:这里使用到AOP切面的微服务,都属于MQ生产者服务

引入依赖:

这里没有版本号的原因是spring cloud已经帮我们管理好各个版本号,已无需手动定义版本号

  1. <!--Spring Cloud Stream-->
  2. <dependency>
  3.     <groupId>org.springframework.cloud</groupId>
  4.      <artifactId>spring-cloud-stream</artifactId>
  5. </dependency>
  6. <dependency>
  7.     <groupId>org.springframework.cloud</groupId>
  8.     <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
  9. </dependency>

在程序入口开启MQ的Input/Output绑定:

  1. @SpringBootApplication(scanBasePackages = {"cn.zdxh.user","cn.zdxh.commons"})
  2. @EnableEurekaClient
  3. @MapperScan("cn.zdxh.user.mapper")
  4. @EnableBinding(MqStream.class) //开启绑定
  5. @EnableFeignClients 
  6. public class YouquServiceProviderUserApplication {
  7.  
  8.     public static void main(String[] args) {
  9.         SpringApplication.run(YouquServiceProviderUserApplication.class, args);
  10.     }
  11.  
  12. }

yml配置:

在生产者端设置output

  • destination: 相当于rabbitmqexchange

  • group: 相当于rabbitmq的queue,不过是和destination一起组合成的queue名。另外,搜索公众号Linux中文社区后台回复“私房菜”,获取一份惊喜礼包。

  • binder: 需要绑定的MQ

  1. #Spring Cloud Stream相关配置
  2. spring:
  3.   cloud:
  4.     stream:
  5.       bindings: # exchange与queue绑定
  6.         log_output: # 日志生产者设置output
  7.           destination: log.exchange
  8.           content-type: application/json
  9.           group: log.queue
  10.           binder: youqu_rabbit #自定义名称
  11.       binders:
  12.         youqu_rabbit:  #自定义名称
  13.           type: rabbit
  14.           environment:
  15.             spring:
  16.               rabbitmq:
  17.                 host: localhost
  18.                 port: 5672
  19.                 username: guest
  20.                 password: 25802580

注:完成以上操作,即完成MQ生产端的所有工作

(3)MQ消费者

引入依赖、开启Input/Output绑定:均和生产者的设置一致

yml配置:

在生产者端设置input

  1. spring:
  2.   cloud:  # Spring Cloud Stream 相关配置
  3.     stream:
  4.       bindings: # exchange与queue绑定
  5.         log_input: # 日志消费者设置input
  6.           destination: log.exchange
  7.           content-type: application/json
  8.           group: log.queue
  9.           binder: youqu_rabbit
  10.       binders:
  11.         youqu_rabbit:
  12.           type: rabbit
  13.           environment:
  14.             spring:
  15.               rabbitmq:
  16.                 host: localhost
  17.                 port5672
  18.                 username: guest
  19.                 password25802580

消费者监听(LogMqListener):

监听生产者发过来的日志信息,将信息添加到数据库即可

  1. @Service
  2. @Slf4j
  3. public class LogMqListener {
  4.  
  5.     @Autowired
  6.     SysLogService sysLogService;
  7.  
  8.     @StreamListener(MqStream.LOG_INPUT)
  9.     public void input(SysLog sysLog)  {
  10.         log.info("开始记录日志========================");
  11.  
  12.         sysLogService.save(sysLog);
  13.  
  14.         log.info("结束记录日志========================");
  15.  
  16.     }
  17. }

注:完成以上操作,即完成MQ消费端的所有工作

4、应用

简述:

只需将@SystemLog(type = SystemLogEnum.REGISTER_LOG),标记在需要记录的方法上,当有客户端访问该方法时,就可以自动完成日志的记录

5、总结

流程:

注解标记--->AOP拦截--->日志发送到MQ--->专门处理日志的系统监听MQ消息 --->日志插入到数据库

来源:https://blog.csdn.net/m0_71777195/article/details/126539664
posted @ 2022-09-24 10:39  程序员小明1024  阅读(271)  评论(0编辑  收藏  举报