Issue
How to trace requests in normal Spring micro service app (non Spring boot) ? I know Spring boot give Sleuth but I wanted to do the similar for non Spring boot app, its a normal Spring app. When request passes through the different micro-service, we want to trace them.
Solution
- You need to configure the Spring app to use a logback.xml file for logging.
- Create an interceptor that would set additional MDC contexts as Sleuth does
- You need to pass the same additional MDC contexts to other services in HTTP headers.
Log back Configuration
<configuration>
<appender class="ch.qos.logback.core.ConsoleAppender" name="STDOUT">
<encoder>
<pattern>[%date{dd-MM-yyyy HH:mm:ss.SSS}] [%thread] %-5level %X{traceId:-} %X{spanId:-} ${PID:-} %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILE">
<encoder>
<pattern>[%date{dd-MM-yyyy HH:mm:ss.SSS}] [%thread] %-5level %X{traceId:-} %X{spanId:-} ${PID:-} %logger{36} - %msg%n</pattern>
</encoder>
<file>log/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>log/app.%d{yyyy-MM-dd-HH}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>200MB</totalSizeCap>
</rollingPolicy>
</appender>
<logger level="INFO" name="root">
<appender-ref ref="STDOUT"/>
</logger>
</configuration>
Interceptor that sets the MDC context for tracing.
@Slf4j
public class LoggerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String traceId = request.getHeader("TRACE_ID");
String spanId = request.getHeader("SPAN_ID");
if (traceId == null) {
traceId = UUID.randomUUID().toString();
}
// You can generate new spanId or use the same one
if (spanId == null) {
spanId = UUID.randomUUID().toString();
}
MDC.put("TRACE_ID", traceId);
MDC.put("traceId", traceId);
MDC.put("SPAN_ID", spanId);
MDC.put("spanId", spanId);
log.info("[preHandle] HTTP: {}, URL: {} ", request.getMethod(), request.getRequestURI());
return true;
}
@Override
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView)
throws Exception {
log.info("[postHandle] HTTP: {}, URL: {} ", request.getMethod(), request.getRequestURI());
MDC.clear();
}
}
If you want to measure execution time then add a profiler for the same. Read more about the same at the example at Profiling Spring Boot, it says specific about Spring boot but you can use the same approach in Spring app as well.
If you're using RestTemplate then you can set these headers.
HttpHeaders headers = new HttpHeaders();
headers.set("TRACE_ID", MDC.get("TRACE_ID"));
headers.set("SPAN_ID", MDC.get("SPAN_ID"));
HttpEntity entity = new HttpEntity(headers);
RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory rf =
(SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
rf.setReadTimeout(2 * Constants.ONE_MILLI_INT);
rf.setConnectTimeout(2 * Constants.ONE_MILLI_INT);
ResponseEntity<String> response =
restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
In this simple example, TRACE_ID
and SPAN_ID
headers have been set.
Answered By - sonus21