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

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

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

推荐文章

ElasticSearch集群搭建指南
2024-11-19 02:31:21 +0800 CST
使用 Go Embed
2024-11-19 02:54:20 +0800 CST
mysql 计算附近的人
2024-11-18 13:51:11 +0800 CST
Redis函数在PHP中的使用方法
2024-11-19 04:42:21 +0800 CST
支付宝批量转账
2024-11-18 20:26:17 +0800 CST
20个超实用的CSS动画库
2024-11-18 07:23:12 +0800 CST
PHP 允许跨域的终极解决办法
2024-11-19 08:12:52 +0800 CST
Go语言SQL操作实战
2024-11-18 19:30:51 +0800 CST
地图标注管理系统
2024-11-19 09:14:52 +0800 CST
基于Flask实现后台权限管理系统
2024-11-19 09:53:09 +0800 CST
GROMACS:一个美轮美奂的C++库
2024-11-18 19:43:29 +0800 CST
Elasticsearch 的索引操作
2024-11-19 03:41:41 +0800 CST
为什么大厂也无法避免写出Bug?
2024-11-19 10:03:23 +0800 CST
PostgreSQL日常运维命令总结分享
2024-11-18 06:58:22 +0800 CST
Python 获取网络时间和本地时间
2024-11-18 21:53:35 +0800 CST
JS 箭头函数
2024-11-17 19:09:58 +0800 CST
vue打包后如何进行调试错误
2024-11-17 18:20:37 +0800 CST
Nginx 性能优化有这篇就够了!
2024-11-19 01:57:41 +0800 CST
程序员茄子在线接单