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

2023-10-27

简介:

无论在什么系统中,日志管理模块都属于十分重要的部分,接下来会通过注解+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;
    }

}

3、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消费端的所有工作

4、应用

简述:

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

5、总结

流程:

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于Spring Cloud实现日志管理模块 的相关文章

  • 如何查看Pocketsphinx词典中是否存在该单词?

    我只是想看看字典文件中是否存在字符串 字典文件位于问题底部 我想检查语音识别器是否可以识别单词 例如 识别器将无法识别字符串ahdfojakdlfafiop 因为字典中没有定义 所以 我可以检查某个单词是否在 pocktsphinx 词典中
  • HTTP 状态 404 - 请求的资源不可用

    在使用 MyEclipse IDE 中的 Tomcat 服务器和 Struts 2 框架时 我遇到了反复出现的问题 我将我的程序作为服务器应用程序运行 当它运行时 默认的index jsp 文件将成功打开 但应用程序的其他过去都不起作用 当
  • 检查双精度值的等于和不等于条件

    我在比较两者时遇到困难double values using and 我创建了 6 个双变量并尝试进行比较If健康 状况 double a b c d e f if a b c d e f My code here in case of t
  • Java 泛型/类型调度问题

    考虑以下程序 import java util List import java util ArrayList public class TypeTest public static class TypeTestA extends Type
  • 方法断点可能会大大减慢调试速度

    每当向方法声明行添加断点 在 Intellij IDEA 或 Android Studio 中 时 都会出现一个弹出窗口 方法断点可能会大大减慢调试速度 为什么会这样戏剧性地减慢调试速度 是我的问题吗 将断点放在函数的第一行有什么不同 Th
  • Java 变量的作用域

    我不明白为什么这段代码的输出是10 package uno public class A int x 10 A int x 12 new B public static void main String args int x 11 new
  • 场景生成器删除 fxml 文件中的导入

    我使用场景构建器 Gluon Scene Builder JavaFX Scene Builder 8 1 1 来创建应用程序的 UI 并使用 Eclipse 开发 JavaFX 现在 每次我在场景生成器中保存某些内容时 它都会从 fxml
  • Java替换特定字符

    这是我在这个网站上的第一个问题 所以我会尽量不要成为一个十足的菜鸟 我目前正在用java 创建刽子手游戏 所以我问你的问题是我们是否被赋予了 幽灵 这个词 并将 Ghost 替换为 hiddenWord ghost length for i
  • 想要开发像 Facebook 这样的网站 - 处理数百万个请求 - 高性能 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我想用 Java 开发一个像 Fac
  • 使用 Guava 联合两个 ImmutableEnumSets

    我想联合两个ImmutableEnumSets来自番石榴 这是我的尝试 public final class OurColors public enum Colors RED GREEN BLUE YELLOW PINK BLACK pub
  • 参数动态时如何构建 JPQL 查询?

    我想知道是否有一个好的解决方案来构建基于过滤器的 JPQL 查询 我的查询太 富有表现力 我无法使用 Criteria 就像是 query Select from Ent if parameter null query WHERE fiel
  • 从 html 页面和 javascript 调用 java webservice

    我正在尝试从 javascript 调用 java 实现的 Web 服务 使用 NetBeans IDE 我读过很多关于 jQuery 和 AJAX 的内容 但我似乎无法掌握它 假设我的 Web 服务 WSDL 位于 http localh
  • 从 Java 日历迁移到 Joda 日期时间

    以前 当我第一次设计股票应用相关软件时 我决定使用java util Date表示股票的日期 时间信息 后来我体会到了大部分方法java util Date已弃用 因此 很快 我重构了所有代码以利用java util Calendar 然而
  • 让JScrollPane控制多个组件

    对于我的应用程序 我正在设计一个脚本编辑器 目前我有一个JPanel其中包含另一个JPanel保存行号 位于左侧 以及JTextArea用于允许用户输入代码 位于右侧 目前 我已经实施了JScrollPane on the JTextAre
  • Apache Commons CLI:替代已弃用的 OptionBuilder?

    IntelliJ 显示此示例代码中不推荐使用 OptionBuilderhttp commons apache org proper commons cli usage html http commons apache org proper
  • OpenJDK 版本控制

    上下文 我想确保我们系统上安装的 Java 不受 CVE 2022 21449 的影响 java version 给出 openjdk version 11 0 7 2020 04 14 LTS OpenJDK Runtime Enviro
  • Spock模拟inputStream导致无限循环

    我有一个代码 gridFSFile inputStream bytes 当我尝试这样测试时 given def inputStream Mock InputStream def gridFSDBFile Mock GridFSDBFile
  • 从一个文本文件中获取数据并将其移动到新的文本文件

    我有一个文件 里面有数据 在我的主要方法中 我读入文件并关闭文件 我调用另一种方法 在原始文件的同一文件夹内创建一个新文件 所以现在我有两个文件 原始文件和通过我调用的方法生成的文件 我需要另一种方法 从原始文件中获取数据并将其写入创建的新
  • Java 编码风格、局部变量与重复方法调用

    我更喜欢使用局部变量而不是多次调用同一方法 I prefer this Vehicle vehicle person getVehicle if vehicle instanceof Car Car car Car vehicle car
  • Java中有类似分支/跳转表的东西吗?

    Java有类似分支表或跳转表的东西吗 分支表或跳转表是 根据维基百科 http en wikipedia org wiki Branch table 用于描述使用分支指令表将程序控制 分支 转移到程序的另一部分 或可能已动态加载的不同程序

随机推荐

  • 如何通过C语言自动生成MAC地址

    如何通过C语言自动生成MAC地址 最近在做虚拟机项目时 需要给创建的每一个虚拟机自动生成一个MAC地址 由于MAC地址为48位 而且格式是以 隔开的 所以下面我写了一个c程序 来自动生成MAC地址 MAC c include
  • solidity实现智能合约教程(5)-NFT拍卖合约

    文章目录 1 介绍 2 主要功能 3 代码示例 4 部署测试 猛戳订阅学习专栏 solidity系列合约源码 解析 1 介绍 拍卖作为历史悠久的交易方式 具有规范化 市场化的特点 在经济活动中扮演着重要角色 以其公开 公平 公正的价格发现功
  • unity动态加载(1)Resources加载方法

    在开发过程中我们很可能需要使用到动态加载 这样一方面可以节省性能 另一方面使我们的开发过程更加便捷 我之前写过一篇游戏中音效控制器 可以很方便的播放音效 就是用Resources 传送门 大家如果有兴趣可以参考 然后这篇博客实现以下使用Re
  • [推荐] (SqlServer)批量清理指定数据库中所有数据

    在实际应用中 当我们准备把一个项目移交至客户手中使用时 我们需要把库中所有表先前的测试数据清空 以给客户一个干净的数据库 如果涉及的表很多 要一一的清空 不仅花费时间 还容易出错以及漏删 在这儿我提供了一个方法 可快捷有效的清空指定数据库所
  • 杭电1005.找规律就好

    本题连接 点击打开链接 Number Sequence Time Limit 2000 1000 MS Java Others Memory Limit 65536 32768 K Java Others Total Submission
  • 【人工智能】Fisher 线性分类器的设计与实现(QDU)

    人工智能 Astar算法求解8数码问题 QDU 人工智能 利用 搜索的博弈树算法编写一字棋游戏 QDU 人工智能 Fisher 线性分类器的设计与实现 QDU 人工智能 感知器算法的设计实现 QDU 人工智能 SVM 分类器的设计与应用 Q
  • Invalid option 'latest' for /langversion; must be ISO-1, ISO-2, Default or an integer in range 1 to

    Easily Add PDF Word Excel Function to Your NET Apps You may be thinking that C 7 features are already supported with Vis
  • 【C语言】浮点型数据为什么不能直接比较

    由于浮点型的精度是有限的 经过运算就可能存在舍入误差 比如 x y y x 所以如果要比较浮点型数值 最好要先定义一个极小值MIN作为允许误差 1 浮点型与0比较 define MIN 0 0000000001 double temp if
  • 1个星期,教你快速上手Unity ASE-【UI流动】

    目录 前言回顾 效果图 节点预览 步骤 前言回顾 不熟悉节点属性的可以点击传送门预览 传送门 1个星期 教你快速上手Unity ASE 预览 传送门 1个星期 教你快速上手Unity ASE 遮罩 传送门 1个星期 教你快速上手Unity
  • Qt中ui文件的使用

    用designer设计的 ui文件可以通过uic工具转换为 h文件 在编译时也会自动生成这样一个ui h文件 有了这个 h文件就可以直接按照纯C 的方式对其中的类进行调用 ui文件的使用就是利用默认工具uic自动产生一个类 然后用该类的se
  • 【数据结构与算法】<==>二叉树下

    目录 堆的应用 1 堆排序 1 建堆 2 向下调整的时间复杂度 3 向上调整建堆的时间复杂度 二叉树链式结构的实现 遍历操作 其他操作 堆的应用 1 堆排序 堆排序即利用堆的思想来进行排序 总共分为两个步骤 1 建堆 升序 建大堆 序 建大
  • 小程序隐私保护的常见问题汇总 小程序隐私配置解决

    涉及调用用户个人信息相关接口的 每一个小程序均需补充相应用户隐私保护指引 1 设置隐私保护的时候 clientip is not registered 返回的错误信息 errCode 61004 errMsg access clientip
  • UE4,UE5虚幻引擎,怎么在蓝图中获取FPS帧速率,显示在UMG(UI)上

    前言 在UE中可以使用命令行比如stat fps显示帧率 但只是显示在界面 假设我们要在蓝图中获取FPS帧率 并且显示在我们创建的UMG控件蓝图 这种stat fps命令行的形式就不行了 因为它只会固定显示在右上角的位置 1 在Tick中获
  • js data 日期初始化

    创建一个日期对象 var objDate new Date arguments list 参数形式有 以下 种 1 new Date month dd yyyy hh mm ss 2 new Date month dd yyyy 3 new
  • 小白数学建模模型入门(一)

    数学建模模型入门 一 1 层次分析法 AHP 针对于难以完全定量的复杂系统做出决策的模型和方法 如图 判断好目标层 准测层 可理解为考虑元素 方案层 不把所有因素放在一起比较 而是两两互相比较 两因素比较的标度由参考论文或其他途径得出 得到
  • 【C++】类的默认成员函数(上)

    文章目录 类的默认成员函数 1构造函数 1 1构造函数特征 1 2编译器自动生成的构造函数 1 3编译器默认的构造函数 1 4C 11特征 2析构函数 2 1特征 2 2编译器默认的析构函数 3拷贝构造函数 3 1特征 3 2编译器默认生成
  • ssh渗透与hydra爆破(简明不啰嗦)

    适合新手上路 MSF与hydra两种方式渗透22端口 后进行远程连接 如有不足请各位见谅 此次实验仅供参考 切勿做违法犯罪 出事一切与本人无关后果自负 希望大家早日成为白帽子 渗透机 kali 靶机 192 168 75 128 1 扫描局
  • vim配置全攻略(2)——vim的简单配置

    这篇文章主要讲的是vim的简单配置 相对于vim内置的一些快捷键和功能 vim的客制化才是vim的灵魂 也是vim存活31年仍被热衷的原因 如果你还不了解vim的基本操作和体系 我建议你看一下我上一篇文章 vim配置全攻略 1 vim的基本
  • 【ARM】rk3399挂载nfs报错

    挂载nfs报错mount mnt bad option for several filesystems e g nfs cifs you might need a sbi 实验1 无效 sudo apt get install cifs u
  • 基于Spring Cloud实现日志管理模块

    简介 无论在什么系统中 日志管理模块都属于十分重要的部分 接下来会通过注解 AOP MQ的方式实现一个简易的日志管理系统 思路 注解 标记需要记录日志的方法 AOP 通过AOP增强代码 利用后置 异常通知的方式获取相关日志信息 最后使用MQ