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.
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 订单履约完成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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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!
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.
