SpringBoot: generation of project traceId/log printing

created at 07-26-2021 views: 42


When viewing the service log, when the service is adjusted too frequently and the log is refreshed too quickly, it will affect the efficiency of joint debugging, testing, and online troubleshooting. Can a unique identifier be given to each requested log? Later use this representation to match and directly retrieve the log of the request? Introduce the main topic of this article, "traceId".



MDC defines Mapped Diagnostic Context, that is, mapping diagnostic environment.

MDC is a function provided by log4j and logback to facilitate logging under multi-threaded conditions.

MDC can be seen as a hash table bound to the current thread, and key-value pairs can be added to it.

How to use MDC

  • ​ Set value to MDC: MDC.put(key, value);
  • ​ Get the value from MDC: MDC.get(key);
  • ​ Print the contents of the MDC to the log: %X{key};

Initialize TraceId and set value to MDC

Here is mainly to use the aspect, set the MDC before the method is executed, and erase the MDC after the method is executed. There are many specific implementation methods, such as filters, interceptors, AOP, and so on. I personally recommend the implementation of Filter, because Filter is the first encountered by the request and the last encountered before the response to the front end.

Filter implementation

@WebFilter(filterName = "traceIdFilter", urlPatterns = "/*")
public class TraceIdFilter implements Filter {
     * 日志跟踪标识
    public static final String TRACE_ID = "TRACE_ID";

    public void init(FilterConfig filterConfig) {

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        MDC.put(TRACE_ID, UUID.randomUUID().toString());
        filterChain.doFilter(servletRequest, servletResponse);

    public void destroy() {

Interceptor implementation

public class TraceIdInterceptor extends HandlerInterceptorAdapter {
    private static final String TRACE_ID = "TRACE_ID";

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        MDC.put(TRACE_ID, UUID.randomUUID().toString());
        return true;

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) {

public class WebConfig implements WebMvcConfigurer {

    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TraceIdInterceptor());

Configure traceId in the log print configuration pattern

Compared with the previous one, only [%X{TRACE_ID}] is added, [%X{***}] is a template, and the middle attribute name is entered by us using MDC put.

%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - [%X{TRACE_ID}] - %msg%n

Asynchronous method log print traceId

The asynchronous method will start a new thread. We want the asynchronous method and the main thread to share the same traceId. First, create a new task adapter MdcTaskDecorator.

public class MdcTaskDecorator implements TaskDecorator {
    public Runnable decorate(Runnable runnable) {
        Map<String, String> map = MDC.getCopyOfContextMap();
        return () -> {
            try {
                String traceId = MDC.get(TRACE_ID);
                if (StringUtils.isBlank(traceId)) {
                    traceId = UUID.randomUUID().toString();
                    MDC.put(TRACE_ID, traceId);
            } finally {

Add the setting of executor.setTaskDecorator(new MdcTaskDecorator()) in the thread pool configuration.

public class ThreadPoolConfig {
    private int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
    private int maxPoolSize = corePoolSize * 2;
    private static final int queueCapacity = 50;
    private static final int keepAliveSeconds = 30;

    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setTaskDecorator(new MdcTaskDecorator());
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;

Use the specified thread pool to execute business code

public void testThreadPoolTaskExecutor(){
    log.info("Async test");

Return traceId in response DTO

public class RetResult<T> {

    private Integer code;

    private String msg;

    private T data;

    private String traceId;

    public RetResult(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
        this.traceId = MDC.get(TRACE_ID);

    public static <T> RetResult<T> success(T t) {
        return new RetResult<>(RetCode.SUCCESS.getCode(), "ok", t);

    public static <T> RetResult<T> success() {
        return new RetResult<>(RetCode.SUCCESS.getCode(), "ok", null);

    public static <T> RetResult<T> fail() {
        return new RetResult<>(RetCode.FAIL.getCode(), "fail", null);

    public static <T> RetResult<T> fail(String msg) {
        return new RetResult<>(RetCode.FAIL.getCode(), msg, null);

    public static <T> RetResult<T> fail(Integer code, String msg) {
        return new RetResult<>(code, msg, null);

created at:07-26-2021
edited at: 07-26-2021: