Implement End-to-End Log Traceability in Spring Cloud Microservices with MDC
This article explains how to build a Spring Cloud‑based microservice framework and add full‑stack log traceability by generating a traceId, propagating it via MDC and interceptors, and visualizing the linked logs through ELK, covering both intra‑process and inter‑service scenarios.
Background
We are constructing a basic project framework on top of Spring Cloud because the team cannot use external solutions such as Ruoyi and needs a custom, second‑generation framework that matches our specific requirements.
Core Features
Multiple microservice modules with a demo module for extension
Core framework module extraction
Eureka service registry
OpenFeign remote calls
Logback logging with traceId support
Swagger API documentation
Shared configuration files
ELK stack log search
Custom starter (planned)
Redis cache with Sentinel high availability
MySQL high‑availability setup
MyBatis‑Plus integration
Link tracing component (planned)
Monitoring (planned)
Utility classes (in development)
Gateway (technology selection pending)
Audit logs to Elasticsearch (planned)
Distributed file system (planned)
Scheduled tasks (planned)
Other features
Pain Points
1. In‑process log correlation
Multiple logs generated by different methods within a single request cannot be linked together.
Example: Method A logs entries 1 and 5, B logs entry 2, C logs entries 3 and 4, but there is no common identifier.
2. Cross‑service log correlation
Logs from different microservices (e.g., Order and Coupon services) are written to separate files and cannot be associated.
3. Cross‑thread log correlation
Logs generated by a parent thread and its child thread lack a common identifier.
4. Third‑party calls
Currently not addressed; will be considered later.
Solution Overview
We choose the MDC‑based approach because it introduces minimal middleware while still providing a workable traceability mechanism.
What is MDC?
MDC (Mapped Diagnostic Context) stores thread‑local contextual data, allowing each thread to attach a traceId that can be accessed by any logging code within that thread.
Implementation Steps
Add %X{traceId} to the Logback pattern:
<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) throws Exception {
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) throws Exception {
MDC.remove("traceId"); // prevent memory leak
}
}Register the interceptor in Spring MVC configuration:
@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, linking all logs belonging to the same request.
Cross‑service propagation
To keep the traceId across services, add an OpenFeign interceptor that copies the value from MDC into the outgoing request header.
@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 the same traceId, which can be searched in Elasticsearch via Kibana to reconstruct the full call chain.
Conclusion
By using an interceptor together with MDC, we inject a traceId into every log entry, enabling end‑to‑end tracing of both intra‑process method calls and inter‑service remote calls. Importing the logs into an ELK stack allows quick retrieval of the entire request flow via the shared traceId.
Future work includes extending the approach to cross‑thread tracing, third‑party calls, and adding dedicated monitoring components.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
