Automatic Insertion of UserId and OrderId into Logs Using MDC, Annotations, and Spring AOP
This article demonstrates how to automatically embed userId and orderId into Java log statements by defining log placeholders, storing these values in ThreadLocal and MDC, and using a custom @UserLog annotation together with Spring AOP to inject the context without manual code changes.
To simplify troubleshooting in e‑commerce systems, the article proposes automatically adding common fields such as userId and orderId to log messages, eliminating the need for manual insertion.
1. Goal
When printing logs, automatically fill userId and orderId without specifying them each time.
2. Implementation Idea
Declare placeholders userId and orderId in the log pattern.
Store the values in a ThreadLocal at the business entry point.
Use Spring AOP + custom annotation to automatically transfer the values from ThreadLocal to the logging context.
3. Configure Log Pattern
Use %X{userId} and %X{orderId} in the pattern, e.g.:
<?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. Put Values into MDC
Use SLF4J's MDC (Mapped Diagnostic Context) to store the variables per thread:
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
Define a custom annotation to specify which fields to extract:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLog {
String userId() default "";
String orderId() default "";
}Apply it on a service method:
@UserLog(userId = "userId", orderId = "orderId")
public void orderPerform(UserOrder order) {
log.warn("订单履约完成");
}
@Data
public static class UserOrder {
String userId;
String orderId;
}Create an AOP aspect that extracts the values via PropertyUtils.getProperty and puts them into MDC before method execution, clearing MDC afterwards:
@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 ann = method.getAnnotation(UserLog.class);
if (ann != null && args != null && args.length > 0) {
String userId = String.valueOf(PropertyUtils.getProperty(args[0], ann.userId()));
String orderId = String.valueOf(PropertyUtils.getProperty(args[0], ann.orderId()));
MDC.put("userId", userId);
MDC.put("orderId", orderId);
}
try {
return joinPoint.proceed();
} finally {
MDC.clear();
}
}
}6. Verification
Test class demonstrates the effect:
@Test
public void testUserLog() {
OrderService.UserOrder order = new OrderService.UserOrder();
order.setUserId("32894934895");
order.setOrderId("8497587947594859232");
orderService.orderPerform(order);
}The console output shows the log line with both identifiers automatically inserted.
7. Summary
Different business scenarios require distinct log information; using a UserLog annotation combined with AOP and MDC provides a concise way to inject common parameters into logs, greatly improving traceability and developer productivity. The core implementation is under 30 lines and can be extended for additional features such as request/response logging or metric reporting.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
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.