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.
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
ThreadLocalvariable 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><?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></code>Using MDC to Store Context Variables
Slf4j’s
MDCclass 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>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
@UserLogannotation together with Spring AOP and MDC reduces boilerplate logging code and boosts developer productivity.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.