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

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

一种高效的日志打印工具,通过使用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

推荐文章

2025,重新认识 HTML!
2025-02-07 14:40:00 +0800 CST
XSS攻击是什么?
2024-11-19 02:10:07 +0800 CST
pycm:一个强大的混淆矩阵库
2024-11-18 16:17:54 +0800 CST
Git 常用命令详解
2024-11-18 16:57:24 +0800 CST
Golang 中你应该知道的 Range 知识
2024-11-19 04:01:21 +0800 CST
Vue3中如何处理状态管理?
2024-11-17 07:13:45 +0800 CST
ElasticSearch集群搭建指南
2024-11-19 02:31:21 +0800 CST
Go中使用依赖注入的实用技巧
2024-11-19 00:24:20 +0800 CST
JavaScript设计模式:适配器模式
2024-11-18 17:51:43 +0800 CST
一个数字时钟的HTML
2024-11-19 07:46:53 +0800 CST
向满屏的 Import 语句说再见!
2024-11-18 12:20:51 +0800 CST
联系我们
2024-11-19 02:17:12 +0800 CST
Vue3中如何处理异步操作?
2024-11-19 04:06:07 +0800 CST
JavaScript中设置器和获取器
2024-11-17 19:54:27 +0800 CST
程序员茄子在线接单