Spring Boot结合Zipkin:自定义Trace ID及特定请求日志配置咨询
Great questions! Let's tackle each one with practical examples you can implement right away.
Absolutely. Spring Sleuth gives you full flexibility to override its default Trace ID generation logic. Here's how to roll your own:
Implement the
TraceIdGeneratorinterface
Create a class that generates your preferred unique ID (just make sure it's globally unique to avoid trace collisions):import org.springframework.cloud.sleuth.api.TraceIdGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.UUID; @Configuration public class CustomTraceIdConfig { @Bean public TraceIdGenerator customTraceIdGenerator() { return () -> { // Example: Use UUID without hyphens (matches Sleuth's default 32-char hex format) return UUID.randomUUID().toString().replace("-", ""); // You could also use a custom sequence or business-specific ID here }; } }Key Notes
- Stick to a format compatible with Zipkin (usually 32-character hex strings) to avoid compatibility issues.
- Ensure your ID is unique across your entire distributed system—this is critical for accurate tracing.
You have two main approaches here: customizing Sleuth's sampling logic (to control what gets sent to Zipkin) or filtering logs (to only record these requests in your logs).
Option 1: Custom Sampling for Zipkin Tracing
Sleuth's Sampler decides whether a request gets traced and sent to Zipkin. Build a custom sampler that only triggers for your target requests:
Step 1: Track Request Start Time
First, add an interceptor to capture when the request starts:
import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class RequestTimingInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { request.setAttribute("REQUEST_START_TIME", System.currentTimeMillis()); return true; } }
Step 2: Register the Interceptor
Add it to your Spring MVC config:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private RequestTimingInterceptor timingInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(timingInterceptor); } }
Step 3: Build the Custom Sampler
This sampler checks for 500 errors or slow responses (adjust the threshold to your needs):
import org.springframework.cloud.sleuth.api.Span; import org.springframework.cloud.sleuth.api.Sampler; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @Component public class TargetRequestSampler implements Sampler { // Define your response time threshold (e.g., 500ms) private static final long RESPONSE_TIME_THRESHOLD = 500; @Override public boolean isSampled(Span span) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes == null) return false; HttpServletRequest request = attributes.getRequest(); // Check if the request resulted in a 500 error Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); if (statusCode != null && statusCode == 500) { return true; } // Check if response time exceeds threshold Long startTime = (Long) request.getAttribute("REQUEST_START_TIME"); if (startTime != null) { long responseTime = System.currentTimeMillis() - startTime; if (responseTime > RESPONSE_TIME_THRESHOLD) { return true; } } // Default: Skip tracing for other requests return false; } }
Option 2: Filter Logs for Target Requests
If you want to only log these requests (not just skip Zipkin tracing), use a Logback TurboFilter:
Step 1: Add Filter to logback-spring.xml
<configuration> <!-- ... your existing log config ... --> <turboFilter class="ch.qos.logback.classic.turbo.TurboFilter"> <Name>targetRequestLogFilter</Name> <filter class="com.yourpackage.CustomLogFilter"/> </turboFilter> </configuration>
Step 2: Implement the Filter
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.turbo.TurboFilter; import ch.qos.logback.core.spi.FilterReply; import javax.servlet.http.HttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; public class CustomLogFilter extends TurboFilter { private static final long RESPONSE_TIME_THRESHOLD = 500; @Override public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes == null) return FilterReply.NEUTRAL; HttpServletRequest request = attributes.getRequest(); // Allow logs for 500 errors Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); if (statusCode != null && statusCode == 500) { return FilterReply.ACCEPT; } // Allow logs for slow responses Long startTime = (Long) request.getAttribute("REQUEST_START_TIME"); if (startTime != null) { long responseTime = System.currentTimeMillis() - startTime; if (responseTime > RESPONSE_TIME_THRESHOLD) { return FilterReply.ACCEPT; } } // Reject logs for all other requests return FilterReply.DENY; } }
内容的提问来源于stack exchange,提问作者Ankit Bansal

