优化
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
package com.baiyue.common.web;
|
||||
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** Immutable standard headers for one synchronous outbound child span. */
|
||||
public record OutboundTraceHeaders(String requestId, String traceId, String traceParent) {
|
||||
private static final Pattern SAFE_REQUEST_ID = Pattern.compile("[A-Za-z0-9._:-]{1,128}");
|
||||
|
||||
/** Builds a child span from the current MDC, safely starting a new trace when absent/invalid. */
|
||||
public static OutboundTraceHeaders fromCurrentOrNew() {
|
||||
String requestId = safeRequestId(MDC.get(CorrelationContext.REQUEST_ID));
|
||||
String traceId = TraceParent.normalizeTrustedTraceId(MDC.get(CorrelationContext.TRACE_ID))
|
||||
.orElseGet(() -> TraceParent.newRoot(null).traceId());
|
||||
return child(requestId, traceId);
|
||||
}
|
||||
|
||||
/** Builds a local child from a trusted persisted trace value, or safely starts a new trace. */
|
||||
public static OutboundTraceHeaders fromTrustedTraceOrNew(String trustedTraceId) {
|
||||
String traceId = TraceParent.normalizeTrustedTraceId(trustedTraceId)
|
||||
.orElseGet(() -> TraceParent.newRoot(null).traceId());
|
||||
return child(UUID.randomUUID().toString(), traceId);
|
||||
}
|
||||
|
||||
public String spanId() {
|
||||
return TraceParent.parse(traceParent).orElseThrow().parentId();
|
||||
}
|
||||
|
||||
private static OutboundTraceHeaders child(String requestId, String traceId) {
|
||||
TraceParent child = TraceParent.outboundChild(traceId);
|
||||
return new OutboundTraceHeaders(requestId, child.traceId(), child.value());
|
||||
}
|
||||
|
||||
private static String safeRequestId(String value) {
|
||||
return value != null && SAFE_REQUEST_ID.matcher(value).matches()
|
||||
? value : UUID.randomUUID().toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.baiyue.common.web;
|
||||
|
||||
import org.springframework.boot.web.client.RestClientCustomizer;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
/** Installs the trace interceptor after ordinary RestClient customizers. */
|
||||
public final class RestClientTraceCustomizer implements RestClientCustomizer, Ordered {
|
||||
private final RestClientTraceInterceptor interceptor;
|
||||
|
||||
public RestClientTraceCustomizer(RestClientTraceInterceptor interceptor) {
|
||||
this.interceptor = interceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(RestClient.Builder builder) {
|
||||
builder.requestInterceptor(interceptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.baiyue.common.web;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** Applies the trusted synchronous trace context to Spring RestClient requests. */
|
||||
public final class RestClientTraceInterceptor implements ClientHttpRequestInterceptor {
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
|
||||
ClientHttpRequestExecution execution) throws IOException {
|
||||
OutboundTraceHeaders outbound = OutboundTraceHeaders.fromCurrentOrNew();
|
||||
request.getHeaders().set(CorrelationContext.REQUEST_ID_HEADER, outbound.requestId());
|
||||
request.getHeaders().set(CorrelationContext.TRACE_ID_HEADER, outbound.traceId());
|
||||
request.getHeaders().set(CorrelationContext.TRACE_PARENT_HEADER, outbound.traceParent());
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.baiyue.common.web;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.web.client.RestClientCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
/** Adds trace propagation to Spring-managed RestClient builders. */
|
||||
@AutoConfiguration(afterName = "org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration")
|
||||
@ConditionalOnClass({RestClient.class, RestClientCustomizer.class})
|
||||
public class RestClientTracePropagationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(RestClientTraceInterceptor.class)
|
||||
RestClientTraceInterceptor restClientTraceInterceptor() {
|
||||
return new RestClientTraceInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestClientTraceCustomizer baiyueTraceRestClientCustomizer(RestClientTraceInterceptor interceptor) {
|
||||
return new RestClientTraceCustomizer(interceptor);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.baiyue.common.web;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HexFormat;
|
||||
import java.util.Optional;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -63,6 +64,24 @@ public record TraceParent(String traceId, String parentId, String traceFlags) {
|
||||
return new TraceParent(currentTraceId.toLowerCase(java.util.Locale.ROOT), randomNonZero(8), "01");
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a trace ID from a trusted persisted/internal context.
|
||||
*
|
||||
* <p>Unlike an untrusted HTTP header, a canonical UUID is accepted and compacted because
|
||||
* existing event envelopes historically persisted UUID trace IDs.</p>
|
||||
*/
|
||||
public static Optional<String> normalizeTrustedTraceId(String value) {
|
||||
if (validLegacyTraceId(value)) {
|
||||
return Optional.of(value.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
if (value != null && value.matches(
|
||||
"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}")) {
|
||||
String compact = value.replace("-", "").toLowerCase(Locale.ROOT);
|
||||
return validLegacyTraceId(compact) ? Optional.of(compact) : Optional.empty();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static boolean validLegacyTraceId(String value) {
|
||||
return value != null && LEGACY_TRACE_ID.matcher(value).matches()
|
||||
&& !ZERO_TRACE_ID.equalsIgnoreCase(value);
|
||||
|
||||
Reference in New Issue
Block a user