open-telemetry / open-telemetry/opentelemetry-java-instrumentation

Support for (auto) instrumentation for Spring Cloud Gateway MVC routes

Open
#16,413 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement needs triage
Dominant language
Java
Stars
2.6k
Forks
1.2k
Avg merge
2d 18h
Merged PRs (30d)
228

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by comparing the starter's existing reactive Spring Cloud Gateway instrumentation with the MVC Gateway route handling, then inspect the relevant instrumentation entry points and tests. Done means MVC routes are automatically instrumented with trace context and route details, with coverage for the supported behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend, observability
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.