Backend Development 9 min read

Automate User and Order ID Logging in Spring Boot with MDC and AOP

This guide explains how to automatically inject userId and orderId into log statements in a Spring Boot microservice by configuring Log4j2 patterns, using MDC to store context variables, and creating a custom @UserLog annotation with an AOP aspect that populates the MDC before method execution.

macrozheng
macrozheng
macrozheng
Automate User and Order ID Logging in Spring Boot with MDC and AOP

In e‑commerce systems, logging userId and orderId for each request is essential for troubleshooting, but manually adding these fields to every log statement is tedious.

Goal

Automatically fill common parameters such as userId and orderId in log messages without manual specification.

Implementation Idea

Declare placeholders

userId:%X{userId} orderId:%X{orderId}

in the log pattern.

Store userId in a

ThreadLocal

variable at the entry point of the business logic.

Use a custom annotation together with Spring AOP to automatically transfer the stored values into the MDC context.

Configure Log4j2 Pattern

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

Using MDC to Store Context Variables

Slf4j’s

MDC

class allows you to put variables into the thread’s diagnostic context; the logging framework will automatically replace the placeholders defined in the pattern.

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

When the log statement is executed, the output includes the injected IDs:

<code>2024-08-17 21:35:38,284 [main] WARN  - userId:32894934895 orderId:8497587947594859232 订单履约完成</code>
Log output example
Log output example

Annotation + Spring AOP

Create an annotation that specifies the property paths for userId and orderId, then write an aspect that extracts these values before method execution and puts them into MDC.

Define the Annotation

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

Define the Aspect

<code>@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();
        }
    }
}</code>

Usage Example

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

@Data
public static class UserOrder {
    String userId;
    String orderId;
}</code>

Verification

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

The test prints a log line that already contains the userId and orderId, confirming that the annotation and aspect work as intended.

Conclusion

Different business scenarios require different log details, but for most debugging needs, automatically attaching a unique identifier to each request greatly simplifies tracing. Using a custom

@UserLog

annotation together with Spring AOP and MDC reduces boilerplate logging code and boosts developer productivity.

JavamicroservicesAOPloggingSpring BootAnnotationMDC
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

login 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.