从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(十二)日志篇(1):rocketmq+ aop +自定义注解 实现入参出参日志收集记录 完整源码

本文涉及的产品
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
简介: 从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(十二)日志篇(1):rocketmq+ aop +自定义注解 实现入参出参日志收集记录 完整源码

edc5c4e10b854e7eb0dbe5fc9214322a.png

入参出参日志

我们日常开发中日志是不可缺少的一部分,

如mini-cloud架构图所示,大型系统一般可用elk 等进行日志收集

中小型系统也可以用spring-boot-admin 等进行收集,但我们业务场景经常

会有一种需求,就是一些重要入参出参接口希望按照url 进行收集并便于以后排查分析

比较典型的就是金融产品或者银行产品扣款,出账,转账,扣款等

期望效果

我们可能会希望通过一个url 或者关联参数定位查询某接口入参出参,比如转账例子,我们希望单独看转账接口得入出参日志

url: /transfer

args: {"transfer":1,"amount":200,"to":2}

response: {"status":200,"msg":"转账成功"}

keyword:{"转账"}

description:"转账接口记录"

架构图eb58220dbfd7425b987f201f98399c06.png

源码

共通部分aop+自定义注解

我们在共同中添加common-log模块,并添加spring.factories 自动注入,目录结构如下:

d4824fcd18c14a5da4061cab6f1d5d55.png

pom.xml

1.    <dependencies>
        <!--web 模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>
        <!--rocketmq依赖-->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
    </dependencies>


IOLogRecordDTO.java

@Builder
@Getter
@Setter
public class IOLogRecordDTO implements Serializable {
    private Long timestamp ;
    private String method;
    private String url ;
    private String contentType;
    private String args ;
    private Object response ;
    private String dateTime;
    private String keyword ;
    private String description;
}

IOLogRecorder.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface IOLogRecorder {
    String keyword() default "";
    String descrition() default "";
}

IOLogAspect.java

@Aspect
public class IOLogAspect {
    @Autowired
    RocketMQTemplate rocketMQTemplate;
    @Pointcut("@annotation(com.minicloud.common.log.annotation.IOLogRecorder)")
    public void pointCut() {
    }
    @Around("pointCut()")
    public Object record(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String url = request.getRequestURI();
        String contentType= request.getHeader("content-type");
        String method = request.getMethod();
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        IOLogRecorder ioLogRecorder = methodSignature.getMethod().getAnnotation(IOLogRecorder.class);
        System.out.println(methodSignature.getMethod().getName());
        Object[] args = joinPoint.getArgs();
        String inArgs = JSONUtil.toJsonStr(args);
        Object response =  joinPoint.proceed();
        long timestamp = System.nanoTime();
        IOLogRecordDTO ioLogRecordDTO = IOLogRecordDTO.builder().keyword(ioLogRecorder.keyword()).description(ioLogRecorder.descrition()).url(url).contentType(contentType).method(method).args(inArgs).response(response).dateTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))).timestamp(timestamp).build();
        rocketMQTemplate.send("iolog", MessageBuilder.withPayload(ioLogRecordDTO).build());
        return response;
    }
}

IOLogConfigration.java

@Configuration
public class IOLogConfigration {
    @Bean
    public IOLogAspect ioLogAspect(){
        return new IOLogAspect();
    }
}

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.minicloud.common.log.config.IOLogConfigration

rockemq搭建

本篇暂时不提及rocketmq搭建,因为涉及内容太多,会有单独篇幅介绍

日志数据库创建

日志存储可以是数据库,文件,缓存等,本篇使用的是mysql数据库


a87075e28cdc4f6d818f7b89bcb96618.png

CREATE TABLE `iolog` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '住建',
  `timestamp` bigint(20) DEFAULT NULL COMMENT '时间戳',
  `method` varchar(10) DEFAULT '' COMMENT '请求方式',
  `url` varchar(100) DEFAULT NULL COMMENT '请求url',
  `content_type` varchar(30) DEFAULT '' COMMENT '数据类型',
  `args` varchar(500) DEFAULT '' COMMENT '请求参数',
  `response` varchar(1000) DEFAULT '' COMMENT '响应',
  `data_time` varchar(30) DEFAULT '' COMMENT '日志时间',
  `keyword` varchar(50) DEFAULT '' COMMENT '关键字',
  `description` varchar(100) DEFAULT NULL COMMENT '描述',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4;

mq 消费端搭建

mq消费端是一个独立的mq服务,因为以后还需要集成别的消费业务,所以独立消费端便于扩展,不与具体某业务服务耦合,具体代码如下:


3b424494773340f8a79a6108beb3b76f.png

pom.xml

    <dependencies>
        <!--注册中心客户端-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--配置中心客户端-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--日志拦截依赖-->
        <dependency>
            <groupId>org.mini-cloud</groupId>
            <artifactId>mini-cloud-common-log</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.mini-cloud</groupId>
            <artifactId>mini-cloud-common-fegin</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <!--common data 依赖 -->
        <dependency>
            <groupId>org.mini-cloud</groupId>
            <artifactId>mini-cloud-common-data</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.mini-cloud</groupId>
            <artifactId>mini-cloud-common-auth</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

IOLogConsumer.java

@Component
@RocketMQMessageListener(consumerGroup = "group-a", topic = "iolog",consumeMode = ConsumeMode.ORDERLY)
public class IOLogConsumer implements RocketMQListener<String> {
    @Autowired
    private IOLogRecordDao ioLogRecordDao;
    @Override
    public void onMessage(String message) {
        IOLogRecordDTO ioLogRecordDTO = JSONUtil.toBean(message,IOLogRecordDTO.class);
        ioLogRecordDao.insert(ioLogRecordDTO);
    }
}

MiniCloudLogConsumerApplication.java

@SpringCloudApplication
@EnableCaching
@EnableMiniCloudFeignClients
@EnableMiniCloudResourceServer
public class MiniCloudLogConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(MiniCloudLogConsumerApplication.class, args);
    }
}

nacos 中 mini-cloud-log-consumer-dev.yml

server:
  port: 6600
  tomcat:
    uri-encoding: UTF-8
    max-threads: 500
    max-connections: 10000
    accept-count: 500
spring:
  shardingsphere:
    props:
      sql:
        show: true
    datasource:
      names: master,slave0,slave1
      master:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://${MYSQL_HOST:192.168.1.2}:${MYSQL_PORT:3306}/${MYSQL_DB:mini_cloud_log}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true
        username: root
        password: root
      slave0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://${MYSQL_HOST:192.168.1.2}:${MYSQL_PORT:3306}/${MYSQL_DB:mini_cloud_log}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true
        username: root
        password: root
      slave1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://${MYSQL_HOST:192.168.1.2}:${MYSQL_PORT:3306}/${MYSQL_DB:mini_cloud_log}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true
        username: root
        password: root
    sharding:
      master-slave-rules:
        master:
          master-data-source-name: master
          slave-data-source-names: slave0,slave1

服务日志发送方集成

日志发送方其实就是实际的各个业务端服务,本文在upms 服务的一个角色分页接口加上日志注解,只要添加 @IOLogRecorder 变可自动收集入参出参发送到mq,如下:

8339929eb8aa4bdfb3c4ce6ab392c275.png

重启服务以及查看结果

我们在业务端请求一个角色分页列表接口,来看看效


191d45a75b6b43ffa405ca3558af2d45.png

业务消费端接收到消息


来看看response

{
    "headers": { }, 
    "statusCodeValue": 200, 
    "body": {
        "data": [
            {
                "roleId": 33, 
                "roleDesc": "最大权限", 
                "roleCode": "SUPER_ADMIN", 
                "roleName": "超级管理员", 
                "tenantId": 1, 
                "upmsPermDTOS": [ ]
            }, 
            {
                "roleId": 34, 
                "roleDesc": "普通用户1", 
                "roleCode": "USER1", 
                "roleName": "普通用户1", 
                "tenantId": 1, 
                "upmsPermDTOS": [ ]
            }, 
            {
                "roleId": 35, 
                "roleDesc": "普通用户2", 
                "roleCode": "USER2", 
                "roleName": "普通用户2", 
                "tenantId": 1, 
                "upmsPermDTOS": [ ]
            }
        ], 
        "total": 3, 
        "size": 10, 
        "page": 1
    }, 
    "statusCode": "OK"
}

看结果已经通过mq消费端保存到数据库里了,以后可以通过各个字段进行查询操作



相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
149 2
|
3月前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
360 69
从单体到微服务:如何借助 Spring Cloud 实现架构转型
|
10天前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
|
2月前
|
搜索推荐 NoSQL Java
微服务架构设计与实践:用Spring Cloud实现抖音的推荐系统
本文基于Spring Cloud实现了一个简化的抖音推荐系统,涵盖用户行为管理、视频资源管理、个性化推荐和实时数据处理四大核心功能。通过Eureka进行服务注册与发现,使用Feign实现服务间调用,并借助Redis缓存用户画像,Kafka传递用户行为数据。文章详细介绍了项目搭建、服务创建及配置过程,包括用户服务、视频服务、推荐服务和数据处理服务的开发步骤。最后,通过业务测试验证了系统的功能,并引入Resilience4j实现服务降级,确保系统在部分服务故障时仍能正常运行。此示例旨在帮助读者理解微服务架构的设计思路与实践方法。
132 17
|
1月前
|
传感器 监控 安全
智慧工地云平台的技术架构解析:微服务+Spring Cloud如何支撑海量数据?
慧工地解决方案依托AI、物联网和BIM技术,实现对施工现场的全方位、立体化管理。通过规范施工、减少安全隐患、节省人力、降低运营成本,提升工地管理的安全性、效率和精益度。该方案适用于大型建筑、基础设施、房地产开发等场景,具备微服务架构、大数据与AI分析、物联网设备联网、多端协同等创新点,推动建筑行业向数字化、智能化转型。未来将融合5G、区块链等技术,助力智慧城市建设。
|
2月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
115 7
|
3月前
|
运维 监控 Java
为何内存不够用?微服务改造启动多个Spring Boot的陷阱与解决方案
本文记录并复盘了生产环境中Spring Boot应用内存占用过高的问题及解决过程。系统上线初期运行正常,但随着业务量上升,多个Spring Boot应用共占用了64G内存中的大部分,导致应用假死。通过jps和jmap工具排查发现,原因是运维人员未设置JVM参数,导致默认配置下每个应用占用近12G内存。最终通过调整JVM参数、优化堆内存大小等措施解决了问题。建议在生产环境中合理设置JVM参数,避免资源浪费和性能问题。
164 3
|
3月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
88 2
|
4月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
103 9
|
18天前
|
人工智能 SpringCloudAlibaba 自然语言处理
SpringCloud Alibaba AI整合DeepSeek落地AI项目实战
在现代软件开发领域,微服务架构因其灵活性、可扩展性和模块化特性而受到广泛欢迎。微服务架构通过将大型应用程序拆分为多个小型、独立的服务,每个服务运行在其独立的进程中,服务与服务间通过轻量级通信机制(通常是HTTP API)进行通信。这种架构模式有助于提升系统的可维护性、可扩展性和开发效率。
172 1