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

2、注解

注解(SystemLog):

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

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

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


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

枚举(SystemLogEnum):

限定日志类型范围


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

3、AOP切面

AOP(SysLogAspect):

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


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

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.      String  LOG_INPUT  =  "log_input";
  4.      String  LOG_OUTPUT  =  "log_output";
  5.      @Input(LOG_INPUT)
  6.     SubscribableChannel  logInput ();
  7.      @Output(LOG_OUTPUT)
  8.     MessageChannel  logOutput ();
  9. }

(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.      public  static  void  main(String[] args) {
  8.          SpringApplication .run(YouquServiceProviderUserApplication.class, args);
  9.     }
  10. }

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.      @Autowired
  5.     SysLogService sysLogService;
  6.      @StreamListener(MqStream.LOG_INPUT)
  7.     public void  input(SysLog sysLog)  {
  8.          log .info( "开始记录日志========================");
  9.          sysLogService .save(sysLog);
  10.          log .info( "结束记录日志========================");
  11.     }
  12. }

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

4、应用

简述:

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

5、总结

流程:

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

来源:https://blog.youkuaiyun.com/m0_71777195/article/details/126539664
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值