Backend Development 8 min read

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.

Architect's Guide
Architect's Guide
Architect's Guide
Automatic Insertion of UserId and OrderId into Logs Using MDC, Annotations, and Spring AOP

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.

BackendJavaAOPSpringloggingAnnotationsMDC
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

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.