编程 一种高效的日志打印工具,通过使用SpringAOP和MDC,将用户ID和订单ID自动填充到日志中,简化了日志记录的过程

2024-11-19 03:30:24 +0800 CST views 684

一种高效的日志打印工具,通过使用SpringAOP和MDC,将用户ID和订单ID自动填充到日志中,简化了日志记录的过程

1. 目标

在电商交易系统的日志中,为了更方便地记录信息,每次都需要手动设置 userIdorderId,这显得非常繁琐。因此,目标是通过优化,实现日志打印时自动填充这些常用字段,而无需每次手动指定。

2. 实现思路

  • 在日志模板中声明占位符 userIdorderId
  • 在业务入口处将 userId 放入到线程的 ThreadLocal 本地变量中。
  • 通过 Spring AOP + 注解,自动将用户信息放入到线程上下文中,进而填充日志。

3. 配置日志变量,读取上下文变量

使用 %X{} 占位符来自定义日志格式。例如:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info">
    <Appenders>
        <Console name="consoleAppender" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{DEFAULT} [%t] %-5p - userId:%X{userId} orderId:%X{orderId} %m%n%ex" charset="UTF-8"/>
        </Console>
    </Appenders>
    <Loggers>
        <AsyncRoot level="info" includeLocation="true">
            <appender-ref ref="consoleAppender"/>
        </AsyncRoot>
    </Loggers>
</Configuration>

4. 基于 MDC 将订单和用户信息放到线程的上下文 Map

MDC(Mapped Diagnostic Context)允许将信息放入线程上下文中,日志框架会自动将这些信息应用到日志格式中。

使用 MDC 类的 put() 方法可以将 userIdorderId 注入到日志中,例如:

MDC.put("userId", userId);
MDC.put("orderId", orderId);
log.warn("订单履约完成");

效果如下:

2024-08-17 21:35:38,284 [main] WARN  - userId:32894934895 orderId:8497587947594859232 订单履约完成

5. 注解 + SpringAop,自动将 UserId 放到 MDC

通过注解和切面的方式,自动将 UserId 注入到 MDC 中。在方法执行前,注解会自动获取入参中的 userIdorderId

5.1 定义注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLog {
    String userId() default "";
    String orderId() default "";
}

使用方法:

@UserLog(userId = "userId", orderId = "orderId")
public void orderPerform(UserOrder order) {
   log.warn("订单履约完成");
}

5.2 定义切面

@Aspect
@Component
public class UserLogAspect {
   @Pointcut("@annotation(UserLog) && execution(public * *(..))")
   public void pointcut() {}

   @Around("pointcut()")
   public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
      Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
      Object[] args = joinPoint.getArgs();
      
      UserLog userLogAnnotation = method.getAnnotation(UserLog.class);
      if (userLogAnnotation != null && args != null && args.length > 0) {
         String userId = String.valueOf(PropertyUtils.getProperty(args[0], userLogAnnotation.userId()));
         String orderId = String.valueOf(PropertyUtils.getProperty(args[0], userLogAnnotation.orderId()));
         
         MDC.put("userId", userId);
         MDC.put("orderId", orderId);
      }

      try {
         return joinPoint.proceed();
      } finally {
         MDC.clear();
      }
   }
}

5.3 关键代码解读

  • 获取 UserLog 注解

    UserLog userLogAnnotation = method.getAnnotation(UserLog.class);
    
  • 使用 PropertyUtils.getProperty 获取 userId

    PropertyUtils.getProperty(args[0], userLogAnnotation.userId());
    
  • 使用 MDC.put() 设置和清除变量

    MDC.put("userId", userId);
    MDC.clear();
    

6. 验证使用效果

6.1 声明业务 Service

@Service
public class OrderService {
   public static final Logger log = LoggerFactory.getLogger(OrderService.class);
   
   @UserLog(userId = "userId", orderId = "orderId")
   public void orderPerform(UserOrder order) {
      log.warn("订单履约完成");
   }

   @Data
   public static class UserOrder {
      String userId;
      String orderId;
   }
}

6.2 测试日志打印

@Test
public void testUserLog() {
   OrderService.UserOrder order = new OrderService.UserOrder();
   order.setUserId("32894934895");
   order.setOrderId("8497587947594859232");
   orderService.orderPerform(order);
}

6.3 日志效果

打印的日志会自动包含 userIdorderId

2024-08-17 21:35:38,284 [main] WARN  - userId:32894934895 orderId:8497587947594859232 订单履约完成

7. 总结

使用 UserLog 注解和 AOP,可以自动将 userIdorderId 等默认参数放入日志中,简化了业务日志的打印,大幅提升了生产力。通过进一步扩展,可以自动打印出入参日志或自动上报监控打点等功能。

复制全文 生成海报 日志 电商 开发工具 Spring框架 AOP

推荐文章

PHP中获取某个月份的天数
2024-11-18 11:28:47 +0800 CST
nginx反向代理
2024-11-18 20:44:14 +0800 CST
Vue3中如何处理异步操作?
2024-11-19 04:06:07 +0800 CST
回到上次阅读位置技术实践
2025-04-19 09:47:31 +0800 CST
MySQL用命令行复制表的方法
2024-11-17 05:03:46 +0800 CST
Vue中的表单处理有哪几种方式?
2024-11-18 01:32:42 +0800 CST
Python 获取网络时间和本地时间
2024-11-18 21:53:35 +0800 CST
Elasticsearch 文档操作
2024-11-18 12:36:01 +0800 CST
Python 基于 SSE 实现流式模式
2025-02-16 17:21:01 +0800 CST
Go语言中的mysql数据库操作指南
2024-11-19 03:00:22 +0800 CST
用 Rust 构建一个 WebSocket 服务器
2024-11-19 10:08:22 +0800 CST
php指定版本安装php扩展
2024-11-19 04:10:55 +0800 CST
Golang在整洁架构中优雅使用事务
2024-11-18 19:26:04 +0800 CST
PHP如何进行MySQL数据备份?
2024-11-18 20:40:25 +0800 CST
Go 1.23 中的新包:unique
2024-11-18 12:32:57 +0800 CST
底部导航栏
2024-11-19 01:12:32 +0800 CST
OpenCV 检测与跟踪移动物体
2024-11-18 15:27:01 +0800 CST
Go 中的单例模式
2024-11-17 21:23:29 +0800 CST
程序员茄子在线接单