Automate User and Order IDs in Spring Boot Logs with MDC and AOP

This guide shows how to automatically inject userId and orderId into Spring Boot log messages by defining log placeholders, using ThreadLocal, creating a custom @UserLog annotation, and applying a Spring AOP aspect that populates MDC, dramatically simplifying logging in e‑commerce systems.

macrozheng
macrozheng
macrozheng
Automate User and Order IDs in Spring Boot Logs with MDC and AOP

1. Goal

Automatically fill userId and orderId in log statements without manually passing them each time.

2. Implementation Idea

Declare placeholders userId:%X{userId} orderId:%X{orderId} in the log pattern.

Store userId and orderId in a ThreadLocal variable at the entry point of each request.

Use a custom annotation together with Spring AOP to copy the ThreadLocal values into MDC before the method executes.

3. Configure Log Variables

Use %X{} to define custom placeholders in the logging configuration, for example:

<?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>
    <!-- Root Logger -->
    <AsyncRoot level="info" includeLocation="true">
      <appender-ref ref="consoleAppender"/>
    </AsyncRoot>
  </Loggers>
</Configuration>

4. Use MDC to Store Context

Put values into MDC so the logging framework can substitute them automatically:

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

Resulting log line:

2024-08-17 21:35:38,284 [main] WARN  - userId:32894934895 orderId:8497587947594859232 订单履约完成
Next, declare an annotation and an aspect to automate the MDC population.

5. Annotation + Spring AOP

5.1 Define Annotation

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

5.2 Define Aspect

@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 Key Points

Retrieve the @UserLog annotation at runtime.

Use PropertyUtils.getProperty (from commons‑beanutils) to extract nested properties, e.g., info.userId.

Store extracted values in MDC and clear them after method execution.

6. Verify the Effect

6.1 Service Declaration

@Service
public class OrderService {
    private 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 Case

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

6.3 Log Output

7. Summary

Different business scenarios require different log details; for troubleshooting it is often useful to attach a unique identifier to each request. Using a custom @UserLog annotation together with AOP automatically injects these common parameters into the log, greatly improving productivity.

You can further extend this approach to automatically log method arguments, report metrics, and more.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaaoploggingSpring Bootmdc
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.