All Downloads are FREE. Search and download functionalities are using the official Maven repository.

brave.jaxrs2.TracingContainerFilter Maven / Gradle / Ivy

package brave.jaxrs2;

import brave.Span;
import brave.Tracer;
import brave.Tracer.SpanInScope;
import brave.http.HttpServerHandler;
import brave.http.HttpTracing;
import brave.propagation.Propagation.Getter;
import brave.propagation.TraceContext;
import java.lang.annotation.Annotation;
import javax.annotation.Priority;
import javax.inject.Inject;
import javax.ws.rs.ConstrainedTo;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;

import static javax.ws.rs.RuntimeType.SERVER;

// Currently not using PreMatching because we are attempting to detect if the method is async or not
@Provider
@Priority(0) // to make the span in scope visible to other filters
@ConstrainedTo(SERVER)
final class TracingContainerFilter implements ContainerRequestFilter, ContainerResponseFilter {
  static final Getter GETTER =
      new Getter() {

        @Override public String get(ContainerRequestContext carrier, String key) {
          return carrier.getHeaderString(key);
        }

        @Override public String toString() {
          return "ContainerRequestContext::getHeaderString";
        }
      };

  final Tracer tracer;
  final HttpServerHandler handler;
  final TraceContext.Extractor extractor;

  @Inject TracingContainerFilter(HttpTracing httpTracing) {
    tracer = httpTracing.tracing().tracer();
    handler = HttpServerHandler.create(httpTracing, new ContainerAdapter());
    extractor = httpTracing.tracing().propagation().extractor(GETTER);
  }

  /**
   * This implementation peeks to see if the request is async or not, which means {@link
   * PreMatching} cannot be used: pre-matching doesn't inject the resource info!
   */
  @Context ResourceInfo resourceInfo;

  @Override public void filter(ContainerRequestContext request) {
    if (resourceInfo != null) request.setProperty(ResourceInfo.class.getName(), resourceInfo);
    Span span = handler.handleReceive(extractor, request);
    request.removeProperty(ResourceInfo.class.getName());
    if (shouldPutSpanInScope(resourceInfo)) {
      request.setProperty(SpanInScope.class.getName(), tracer.withSpanInScope(span));
    } else {
      request.setProperty(Span.class.getName(), span);
    }
  }

  @Override
  public void filter(ContainerRequestContext request, ContainerResponseContext response) {
    Span span = (Span) request.getProperty(Span.class.getName());
    SpanInScope spanInScope = (SpanInScope) request.getProperty(SpanInScope.class.getName());
    if (span != null) { // asynchronous response or we couldn't figure it out
    } else if (spanInScope != null) { // synchronous response
      span = tracer.currentSpan();
      spanInScope.close();
    } else if (response.getStatus() == 404) {
      span = handler.handleReceive(extractor, request);
    } else {
      return; // unknown state
    }
    handler.handleSend(response, null, span);
  }

  /**
   * We shouldn't put a span in scope unless we know for sure the request is not async. That's
   * because we cannot detach if from the calling thread when async is used.
   */
  // TODO: add benchmark and cache if slow
  static boolean shouldPutSpanInScope(ResourceInfo resourceInfo) {
    if (resourceInfo == null) return false;
    for (Annotation[] annotations : resourceInfo.getResourceMethod().getParameterAnnotations()) {
      for (Annotation annotation : annotations) {
        if (annotation.annotationType().equals(Suspended.class)) {
          return false;
        }
      }
    }
    return true;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy