关于Spring Cloud实现日志管理模块

目录
  • 简介
  • 思路
  • 1、日志表结构
  • 2、注解
  • 3、AOP切面
  • 4、RabbitMQ消息队列
  • 5、应用
  • 6、总结

简介

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

思路

  • 注解:标记需要记录日志的方法
  • AOP:通过AOP增强代码,利用后置/异常通知的方式获取相关日志信息,最后使用MQ将日志信息发送到专门处理日志的系统
  • RabbitMQ:利用解耦、异步的特性,协调完成各个微服务系统之间的通信

1、日志表结构

表结构(sys_log):

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

实体类(SysLog):

@Data
public class SysLog  {

    private static final long serialVersionUID = 1L;

    /**
     * 唯一ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 操作用户id
     */
    private Integer optId;
    /**
     * 操作用户名
     */
    private String optName;
    /**
     * 日志类型
     */
    private String logType;
    /**
     * 日志信息(具体方法名)
     */
    private String logMessage;
    /**
     * 创建时间
     */
    private Date createTime;

}

2、注解

注解(SystemLog):

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

  • @Target:定义注解作用的范围,这里是方法
  • @Retention:定义注解生命周期,这里是运行时
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {

    SystemLogEnum type();

}

枚举(SystemLogEnum):

限定日志类型范围

public enum SystemLogEnum {

    SAVE_LOG("保存"),
    DELETE_LOG("删除"),
    REGISTER_LOG("注册"),
    LOGIN_LOG("登录"),
    LAUD_LOG("点赞"),
    COLLECT_LOG("收藏"),
    THROW_LOG("异常"),
    ;
    private String type;

    SystemLogEnum(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}

3、AOP切面

AOP(SysLogAspect):

实现代码的增强,主要通过动态代理方式实现的代码增强。

拦截注解,并获取拦截到的相关信息,封装成日志对象发送到MQ队列(生产端

Component
@Aspect
@Slf4j
public class SysLogAspect {

    @Autowired
    MqStream stream;

    //切点
    @Pointcut("@annotation(cn.zdxh.commons.utils.SystemLog)")
    public void logPointcut(){}

    //后置通知
    @After("logPointcut()")
    public void afterLog(JoinPoint joinPoint) {
        //一般日志
        SysLog sysLog = wrapSysLog(joinPoint);

        log.info("Log值:"+sysLog);

        //发送mq消息
        stream.logOutput().send(MessageBuilder.withPayload(sysLog).build());

    }

    //异常通知
    @AfterThrowing(value = "logPointcut()", throwing = "e")
    public void throwingLog(JoinPoint joinPoint, Exception e) {
        //异常日志
        SysLog sysLog = wrapSysLog(joinPoint);
        sysLog.setLogType(SystemLogEnum.THROW_LOG.getType());
        sysLog.setLogMessage(sysLog.getLogMessage()+"==="+e);

        log.info("异常Log值:"+sysLog);

        //发送mq消息
        stream.logOutput().send(MessageBuilder.withPayload(sysLog).build());
    }

    /**
     * 封装SysLog对象
     * @param joinPoint
     * @return
     */
    public SysLog wrapSysLog(JoinPoint joinPoint){
        //获取请求响应对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        SysLog sysLog = new SysLog();

        //获取方法全路径
        String methodName = signature.getDeclaringTypeName()+"."+signature.getName();
        //获取注解参数值
        SystemLog systemLog = signature.getMethod().getAnnotation(SystemLog.class);
        //从header取出token
        String token = request.getHeader("token");
        if (!StringUtils.isEmpty(token)) {
            //操作人信息
            Integer userId = JwtUtils.getUserId(token);
            String username = JwtUtils.getUsername(token);
            sysLog.setOptId(userId);
            sysLog.setOptName(username);
        }
        if (!StringUtils.isEmpty(systemLog.type())){
            sysLog.setLogType(systemLog.type().getType());
        }
        sysLog.setLogMessage(methodName);
        sysLog.setCreateTime(new Date());
        return sysLog;
    }

}

4、RabbitMQ消息队列

MQ:

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

Spring Cloud Stream:

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

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

@Component
public interface MqStream {

    String LOG_INPUT = "log_input";

    String LOG_OUTPUT = "log_output";

    @Input(LOG_INPUT)
    SubscribableChannel logInput();

    @Output(LOG_OUTPUT)
    MessageChannel logOutput();

}

(2)MQ生产者

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

引入依赖:

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

<!--Spring Cloud Stream-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

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

@EnableBinding(MqStream.class)

@SpringBootApplication(scanBasePackages = {"cn.zdxh.user","cn.zdxh.commons"})
@EnableEurekaClient
@MapperScan("cn.zdxh.user.mapper")
@EnableBinding(MqStream.class) //开启绑定
@EnableFeignClients
public class YouquServiceProviderUserApplication {

    public static void main(String[] args) {
        SpringApplication.run(YouquServiceProviderUserApplication.class, args);
    }

}

yml配置:

在生产者端设置output

  • destination:相当于rabbitmq的exchange
  • group:相当于rabbitmq的queue,不过是和destination一起组合成的queue名
  • binder:需要绑定的MQ
#Spring Cloud Stream相关配置
spring:
  cloud:
    stream:
      bindings: # exchange与queue绑定
        log_output: # 日志生产者设置output
          destination: log.exchange
          content-type: application/json
          group: log.queue
          binder: youqu_rabbit #自定义名称
      binders:
        youqu_rabbit:  #自定义名称
          type: rabbit
          environment:
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: 25802580

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

(3)MQ消费者

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

yml配置:

在生产者端设置input

spring:
  cloud:  # Spring Cloud Stream 相关配置
    stream:
      bindings: # exchange与queue绑定
        log_input: # 日志消费者设置input
          destination: log.exchange
          content-type: application/json
          group: log.queue
          binder: youqu_rabbit
      binders:
        youqu_rabbit:
          type: rabbit
          environment:
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: 25802580

消费者监听(LogMqListener):

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

@Service
@Slf4j
public class LogMqListener {

    @Autowired
    SysLogService sysLogService;

    @StreamListener(MqStream.LOG_INPUT)
    public void input(SysLog sysLog)  {
        log.info("开始记录日志========================");

        sysLogService.save(sysLog);

        log.info("结束记录日志========================");

    }
}

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

5、应用

简述:

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

6、总结

流程:

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • springboot 配置日志 打印不出来sql的解决方法

    今天整合springboot2 + mybatis + logback 遇到了在日志中sql打印不出来的坑,在网上找了好久,都不是我遇到的问题,这里吐槽一下下现在的博客质量,好多都是抄袭的,也没有标注转载. 先说下要将sql打印到日志的配置 1.在mybatis.xml配置中增加以下配置 <!--指定 MyBatis 增加到日志名称的前缀.--> <setting name="logPrefix" value="m-shop-mybatis-sql.&quo

  • 详解spring cloud分布式日志链路跟踪

    首先要明白一点,为什么要使用链路跟踪? 当我们微服务之间调用的时候可能会出错,但是我们不知道是哪个服务的问题,这时候就可以通过日志链路跟踪发现哪个服务出错. 它还有一个好处:当我们在企业中,可能每个人都负责一个服务,我们可以通过日志来检查自己所负责的服务不会出错,当调用其它服务时,这时候出现错误,那么就可以判定出不是自己的服务出错,从而也可以发现责任不是自己的. 基于微服务之间的调用开始,如果看不懂的小伙伴,请先参考我上篇博客:spring cloud中微服务之间的调用以及eureka的自我保护

  • springcloud LogBack日志使用详解

    <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> 引入lombok即可,lombok包含了Slf4j 下面只需在resources目录下引入此xml配置即可 <?xml version="1.0" encoding="UTF-8"?> <

  • 关于Spring Cloud实现日志管理模块

    目录 简介 思路 1.日志表结构 2.注解 3.AOP切面 4.RabbitMQ消息队列 5.应用 6.总结 简介 无论在什么系统中,日志管理模块都属于十分重要的部分,接下来会通过注解+AOP+MQ的方式实现一个简易的日志管理系统 思路 注解:标记需要记录日志的方法 AOP:通过AOP增强代码,利用后置/异常通知的方式获取相关日志信息,最后使用MQ将日志信息发送到专门处理日志的系统 RabbitMQ:利用解耦.异步的特性,协调完成各个微服务系统之间的通信 1.日志表结构 表结构(sys_log)

  • 如何使用Spring Cloud Feign日志查看请求响应

    这篇文章主要介绍了如何使用Spring Cloud Feign日志查看请求响应,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在使用微服务时,常常会用feign做客户端去调用别的微服务,但是在日志中很难查看到具体的请求和响应.因此,需要把feign默认的日志打开. 日志设置创建feign配置类 @Configuration public class FeignLogConfiguration { @Bean Logger.Level feign

  • python+pytest接口自动化之日志管理模块loguru简介

    目录 安装 简单示例 add()常用参数说明 使用 python自带日志管理模块logging,使用时可进行模块化配置,详细可参考博文Python日志采集(详细). 但logging配置起来比较繁琐,且在多进行多线程等场景下使用时,如果不经过特殊处理,则容易出现日志丢失或记录错乱的情况. python中有一个用起来非常简便的第三方日志管理模块--loguru,不仅可以避免logging的繁琐配置,而且可以很简单地避免在logging中多进程多线程记录日志时出现的问题,甚至还可以自定义控制台输出的

  • 深入理解Spring Boot的日志管理

    前言 Spring Boot在所有内部日志中使用Commons Logging,但是默认配置也提供了对常用日志的支持, 如:Java Util Logging,Log4J, Log4J2和Logback.每种Logger都可以通过配置使用控制台或者文件输出日志内容. 日志输出格式 2016-08-19 10:22:04.233 INFO 7368 --- [ main] com.juzi.AsyncTest : Started AsyncTest in 10.084 seconds (JVM r

  • 详解使用spring cloud config来统一管理配置文件

    当一个系统中的配置文件发生改变的时候,我们需要重新启动该服务,才能使得新的配置文件生效,spring cloud config可以实现微服务中的所有系统的配置文件的统一管理,而且还可以实现当配置文件发生变化的时候,系统会自动更新获取新的配置. 其架构原理图大致如下: 我们将配置文件放入git或者svn等服务中,通过一个Config Server服务来获取git中的配置数据,而我们需要使用的到配置文件的Config Client系统可以通过Config Server来获取对应的配置. 下面我们通过

  • 利用Spring Cloud Config结合Bus实现分布式配置中心的步骤

    概述 假设现在有个需求: 我们的应用部署在10台机器上,当我们调整完某个配置参数时,无需重启机器,10台机器自动能获取到最新的配置. 如何来实现呢?有很多种,比如: 1.将配置放置到一个数据库里面,应用每次读取配置都是直接从DB读取.这样的话,我们只需要做一个DB变更,把最新的配置信息更新到数据库即可.这样无论多少台应用,由于都从同一个DB获取配置信息,自然都能拿到最新的配置. 2.每台机器提供一个更新配置信息的updateConfig接口,当需要修改配置时,挨个调用服务器的updateConf

  • Spring Cloud Alibaba使用Sentinel实现接口限流

    最近管点闲事浪费了不少时间,感谢网友 libinwalan 的留言提醒.及时纠正路线,继续跟大家一起学习Spring Cloud Alibaba. Nacos作为注册中心和配置中心的基础教程,到这里先告一段落,后续与其他结合的内容等讲到的时候再一起拿出来说,不然内容会有点跳跃.接下来我们就来一起学习一下Spring Cloud Alibaba下的另外一个重要组件:Sentinel. Sentinel是什么 Sentinel的官方标题是:分布式系统的流量防卫兵.从名字上来看,很容易就能猜到它是用来

  • Spring Cloud Stream如何实现服务之间的通讯

    Spring Cloud Stream Srping cloud Bus的底层实现就是Spring Cloud Stream,Spring Cloud Stream的目的是用于构建基于消息驱动(或事件驱动)的微服务架构.Spring Cloud Stream本身对Spring Messaging.Spring Integration.Spring Boot Actuator.Spring Boot Externalized Configuration等模块进行封装(整合)和扩展,下面我们实现两个

  • 详解spring cloud分布式整合zipkin的链路跟踪

    为什么使用zipkin? 上篇主要写了:spring cloud分布式日志链路跟踪 从上篇中可以看出服务之间的调用,假设现在有十几台服务,那么在查找日志的时候比较繁琐.复杂,而且在查看调用的时候也会像蜘蛛网一样,量太大. 这时候zipkin可以把链路调用整个过程给升级起来,只需要到一个地方去查找,就可以知道哪一步出错. zipkin也分为服务器和客户端,服务器就是zipkin,微服务就是客户端. 首先,建立服务器zipkin 在此服务build.gradle加上zipkin的依赖: compil

随机推荐