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(); } }