Implement End-to-End Log Traceability in Spring Cloud Using MDC

This guide explains how to add a traceId to logs in a Spring Cloud microservice architecture by using MDC, configuring Logback, creating interceptors, and propagating the trace across services so that logs can be correlated and visualized via ELK Stack.

ITPUB
ITPUB
ITPUB
Implement End-to-End Log Traceability in Spring Cloud Using MDC

Introduction

We are building a basic project framework on top of Spring Cloud microservices. Existing components such as Swagger and Logback serve as the foundation (level 0.5), and we are assembling additional modules (level 1.5) to complete the framework.

Why Build Our Own Framework?

Our team cannot use third‑party frameworks like Ruoyi, and our specific requirements and technology stack favor a custom solution.

Core Features

Multiple microservice modules with a demo module for extension

Core framework module extraction

Eureka service registry

OpenFeign remote calls

Logback with traceId support

Swagger API documentation

Shared configuration files

ELK log search

Custom starter (planned)

Redis cache with Sentinel high availability

MySQL high availability

MyBatis‑Plus integration

Link tracing component (planned)

Monitoring (planned)

Utility classes (planned)

Gateway (technology to be decided)

Audit logs to ES (planned)

Distributed file system (planned)

Scheduled tasks (planned)

Pain Points

Pain Point 1: In‑process log correlation

A single request may invoke many methods and produce several log entries that cannot be linked together.

Example: Method A logs entries 1 and 5, B logs entry 2, C logs entries 3 and 4. The logs share only timestamps, making correlation impossible under concurrent requests.

Pain Point 2: Cross‑service log correlation

Each microservice writes its own logs; how can logs from different processes be linked?

Example: Order service (method A) calls Coupon service (method D). Both services write separate log files, producing ten unrelated entries.

Pain Point 3: Cross‑thread log correlation

How to associate logs from a main thread and its child threads?

Example: Main thread method A spawns a child thread executing method E. Logs from A and E appear unrelated.

Pain Point 4: Third‑party calls

Currently not addressed; future work will consider tracing external calls.

Solution Overview

1.1 Chosen Approach

We evaluate three options: SkyWalking traceId, Elastic APM traceId, and an MDC‑based custom traceId. To keep the initial setup lightweight, we adopt the MDC solution.

1.2 MDC (Mapped Diagnostic Context)

MDC stores thread‑local context data. When using Log4j or Logback, each thread can have its own MDC, allowing any code in that thread to retrieve values such as a traceId.

Implementation Details

2.1 Correlating Logs Within a Single Request

Steps:

Add %X{traceId} to the Logback pattern in logback.xml.

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %X{traceId} %-5level %logger - %msg%n</pattern>

Create an interceptor that extracts traceId from the incoming HTTP header; if absent, generate a UUID and store it in MDC.

@Service
public class LogInterceptor extends HandlerInterceptorAdapter {
    private static final String TRACE_ID = "traceId";
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String traceId = request.getHeader(TRACE_ID);
        if (StringUtils.isEmpty(traceId)) {
            MDC.put("traceId", UUID.randomUUID().toString());
        } else {
            MDC.put(TRACE_ID, traceId);
        }
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        MDC.remove("traceId"); // prevent memory leak
    }
}

Register the interceptor in a WebMvcConfigurer implementation.

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Resource
    private LogInterceptor logInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logInterceptor).addPathPatterns("/**");
    }
}

After this setup, every log entry automatically includes the same traceId, enabling correlation across multiple log lines.

2.2 Cross‑service Log Correlation

We add a Feign interceptor that injects the traceId into the outgoing request header, ensuring the downstream service receives the same identifier.

@Configuration
public class FeignInterceptor implements RequestInterceptor {
    private static final String TRACE_ID = "traceId";
    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header(TRACE_ID, (String) MDC.get(TRACE_ID));
    }
}

Both services now log with identical traceId values, which can be searched in Elasticsearch via Kibana to reconstruct the full call chain.

Conclusion

By using an interceptor and MDC, we inject a traceId into every log entry, enabling end‑to‑end tracing of method calls within a process and across microservices. Importing the logs into an ELK Stack and searching by traceId provides a complete view of the request flow.

Future work includes handling cross‑thread tracing, third‑party calls, and adding monitoring components.

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.

MicroservicesSpring CloudELKmdclog tracing
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

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.