How to Auto‑Inject UserId and OrderId into Logs with MDC, Annotations, and Spring AOP

This guide shows how to automatically include userId and orderId in Java log statements by defining MDC placeholders, storing context data in ThreadLocal, creating a custom @UserLog annotation, and using Spring AOP to inject these values without manual code changes.

dbaplus Community
dbaplus Community
dbaplus Community
How to Auto‑Inject UserId and OrderId into Logs with MDC, Annotations, and Spring AOP

In e‑commerce transaction systems, developers often need to log common fields such as userId and orderId for troubleshooting, but manually adding them to each log statement is tedious.

Goal

Automatically fill log messages with userId and orderId so that developers no longer need to specify them manually.

Implementation Idea

Declare placeholders userId and orderId in the log pattern.

Store the current request's userId in a ThreadLocal variable at the entry point.

Use Spring AOP together with a custom annotation to automatically copy these values into the thread’s MDC (Mapped Diagnostic Context).

Configure Log Pattern

Use Log4j2 (or Logback) pattern with custom %X{userId} and %X{orderId} placeholders:

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

Put Context Data into MDC

Using SLF4J’s MDC (which works with Log4j2 and Logback), store values in the thread context:

MDC.put("userId", userId);
MDC.put("orderId", orderId);
log.warn("订单履约完成");

The resulting log line automatically contains the IDs:

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

Define @UserLog Annotation

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

Apply it to a method, specifying the property paths for the IDs:

@UserLog(userId = "userId", orderId = "orderId")
public void orderPerform(UserOrder order) {
    log.warn("订单履约完成");
}

@Data
public static class UserOrder {
    String userId;
    String orderId;
}

Aspect to Populate 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 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();
        }
    }
}

Key steps:

Retrieve the UserLog annotation from the method.

Use PropertyUtils.getProperty (commons‑beanutils) to extract the fields based on the paths provided.

Put the extracted values into MDC and clear them after method execution.

Testing the Setup

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

The test prints a log line that already contains the IDs, confirming the automatic injection works.

Result Screenshot

log output
log output

Conclusion

By combining MDC placeholders, a custom @UserLog annotation, and Spring AOP, developers can automatically enrich log statements with essential business identifiers, reducing boilerplate and improving traceability across services.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javaloggingannotationspring-aopThreadLocalmdc
dbaplus Community
Written by

dbaplus Community

Enterprise-level professional community for Database, BigData, and AIOps. Daily original articles, weekly online tech talks, monthly offline salons, and quarterly XCOPS&DAMS conferences—delivered by industry experts.

0 followers
Reader feedback

How this landed with the community

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.