How to Auto‑Inject User and Order IDs into Logs with MDC & Spring AOP

This article explains how to automatically include userId and orderId in log messages by defining log placeholders, storing these values in a thread‑local MDC map, and using a custom @UserLog annotation together with Spring AOP to inject the data at runtime, simplifying debugging and improving productivity.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
How to Auto‑Inject User and Order IDs into Logs with MDC & Spring AOP

1. Goal

Automatically fill common log parameters such as userId and orderId when printing logs, without manually specifying them each time.

2. Implementation Idea

Declare placeholders userId and orderId in the log pattern.

Store userId in a ThreadLocal variable at the business entry point.

Use Spring AOP + custom annotation to automatically put the user information into the thread context.

3. Configure Log Variables and Read Context Variables

Use %X{} to define custom placeholders, e.g. userId:%X{userId} orderId:%X{orderId}.

<?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. Use MDC to Put Order and User Info into Thread Context Map

MDC (Mapped Diagnostic Context) stores variables in a thread‑local map; the logging framework automatically substitutes these variables into the defined placeholders.

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 订单履约完成
Log output example
Log output example

5. Annotation + Spring AOP to Automatically Put UserId into MDC

5.1 Define Annotation

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

Usage example:

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

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

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 annotation = method.getAnnotation(UserLog.class);
        if (annotation != null && args != null && args.length > 0) {
            String userId = String.valueOf(PropertyUtils.getProperty(args[0], annotation.userId()));
            String orderId = String.valueOf(PropertyUtils.getProperty(args[0], annotation.orderId()));
            MDC.put("userId", userId);
            MDC.put("orderId", orderId);
        }
        try {
            return joinPoint.proceed();
        } finally {
            MDC.clear();
        }
    }
}

5.3 Key Code Explanation

Retrieve the @UserLog annotation via method.getAnnotation(UserLog.class).

Use PropertyUtils.getProperty (from commons‑beanutils) to extract the specified fields, supporting nested paths like info.userId.

Put the extracted values into MDC with MDC.put and clear them after method execution.

6. Verify Effect

6.1 Declare Business Service

@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 Log Printing

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

6.3 Log Result

See the image above for the formatted log line containing the automatically injected IDs.

7. Summary

Different business scenarios require different log information. By using a custom @UserLog annotation combined with Spring AOP and MDC, common parameters such as userId and orderId can be automatically injected into log messages, greatly simplifying logging code and boosting productivity. The core implementation is under 30 lines of code.

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.

Javaaopspringannotationmdc
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

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.