Backend Development 8 min read

Automatic Insertion of UserId and OrderId into Logs Using Spring AOP and MDC

This article explains how to automatically embed userId and orderId into log statements by defining log placeholders, storing these values in MDC via ThreadLocal, and using a custom @UserLog annotation together with Spring AOP to inject the data before method execution, complete with configuration and test examples.

Architecture Digest
Architecture Digest
Architecture Digest
Automatic Insertion of UserId and OrderId into Logs Using Spring AOP and MDC

Goal : Automatically fill common parameters such as userId and orderId into log messages without manually specifying them each time.

Implementation idea : Declare placeholders in the log pattern (e.g., %X{userId} and %X{orderId} ), store the values in a thread‑local map (MDC), and use Spring AOP together with a custom annotation to inject the values into MDC before the target method runs.

Log configuration (log4j2 XML):

<?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>

1. Define the annotation that marks methods needing automatic logging:

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

2. Implement the AOP aspect that extracts the annotation values, reads the corresponding properties from the method arguments (using PropertyUtils ), puts them into MDC, proceeds with the method, and finally clears MDC:

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

3. Apply the annotation to a business service method. The method itself only logs a simple message; the userId and orderId are automatically added by the aspect:

@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;
    }
}

4. Test the behavior with a JUnit test that creates a UserOrder , sets the fields, and calls the service method. The console output shows the placeholders filled:

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

Resulting log line (example):

2024-08-17 21:35:38,284 [main] WARN - userId:32894934895 orderId:8497587947594859232 订单履约完成

Conclusion : By combining a custom annotation with Spring AOP and MDC, common contextual information can be injected into log statements automatically, reducing boilerplate code and greatly improving debugging efficiency in backend services.

backendJavaAOPSpringloggingAnnotationMDC
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

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.