-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Open
Labels
enhancementNew feature or requestNew feature or requestneeds triageNew issue that requires triageNew issue that requires triage
Description
Is your feature request related to a problem? Please describe.
Currently, it looks like "only" the reactive variant of Spring Cloud Gateway is natively supported by OpenTelemetry starter. In a recent project, we decided to go with the MVC variant, as in the advent of virtual threads, reactive approach was ruled out due to the complexities it introduces.
I was able to work around this by implementing a custom HandlerInterceptor:
package eu.eliagroup.gras.iam.gatekeeper.service.core.opentelemetry;
import static java.lang.String.format;
import static java.util.Optional.ofNullable;
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapGetter;
import jakarta.annotation.Nonnull;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.val;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
/// The interceptor sets up the trace context for any given route.
///
/// It relies on the W3C Trace Context Propagator to extract the trace context from the request
/// headers.
///
/// After request completion, the trace- and span IDs get removed from the MDC.
@Component
@Order(HIGHEST_PRECEDENCE)
final class TraceContextInterceptor implements HandlerInterceptor {
private static final String TRACE_ID = "trace_id";
private static final String SPAN_ID = "span_id";
@Override
public boolean preHandle(
@Nonnull final HttpServletRequest request,
@Nonnull final HttpServletResponse response,
@Nonnull final Object handler) {
val extractedContext =
W3CTraceContextPropagator.getInstance()
.extract(Context.current(), request, new HeaderExtractor());
try (final var ignored = extractedContext.makeCurrent()) {
val currentSpan = Span.current();
currentSpan.updateName(
format("%s %s", request.getMethod().toUpperCase(), request.getRequestURI()));
final var spanContext = currentSpan.getSpanContext();
if (spanContext.isValid()) {
MDC.put(TRACE_ID, spanContext.getTraceId());
MDC.put(SPAN_ID, spanContext.getSpanId());
}
}
return true;
}
@Override
public void afterCompletion(
@Nonnull final HttpServletRequest request,
@Nonnull final HttpServletResponse response,
@Nonnull final Object handler,
final Exception ex) {
MDC.remove(TRACE_ID);
MDC.remove(SPAN_ID);
}
private static final class HeaderExtractor implements TextMapGetter<HttpServletRequest> {
@Override
public Iterable<String> keys(final HttpServletRequest carrier) {
return () -> carrier.getHeaderNames().asIterator();
}
@Override
public String get(final HttpServletRequest carrier, final String headerName) {
return ofNullable(carrier).map(c -> c.getHeader(headerName)).orElse(null);
}
}
}TBH - the feature is not of high priority, yet I think it would be a nice addition.
Describe the solution you'd like
It would be nice if the starter instruments Gateway MVC routes automagically.
Describe alternatives you've considered
None.
Additional context
None.
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestneeds triageNew issue that requires triageNew issue that requires triage