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

com.undefinedlabs.scope.rules.RequestContextScopeAgentRule Maven / Gradle / Ivy

Go to download

Scope is a APM for tests to give engineering teams unprecedented visibility into their CI process to quickly identify, troubleshoot and fix failed builds. This artifact contains the classes to instrument the Akka HTTP.

There is a newer version: 0.15.1-beta.2
Show newest version
package com.undefinedlabs.scope.rules;

import static com.undefinedlabs.scope.agent.ScopeClassLoaderMatcher.hasClassesNamed;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.named;

import akka.http.javadsl.model.HttpResponse;
import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.server.RequestContext;
import akka.http.scaladsl.server.RouteResult;
import com.undefinedlabs.scope.ScopeGlobalTracer;
import com.undefinedlabs.scope.ScopeSpanContainer;
import com.undefinedlabs.scope.events.EventFieldsFactory;
import com.undefinedlabs.scope.events.exception.ThrowableEvent;
import com.undefinedlabs.scope.rules.headers.AkkaHttpHeadersAdapter;
import com.undefinedlabs.scope.rules.http.HttpHeadersExtractorForSpan;
import com.undefinedlabs.scope.rules.propagation.carriers.HttpAkkaRequestExtractAdapter;
import com.undefinedlabs.scope.settings.ScopeSettings;
import com.undefinedlabs.scope.settings.ScopeSettingsResolver;
import com.undefinedlabs.scope.utils.SpanUtils;
import com.undefinedlabs.scope.utils.event.EventValues;
import com.undefinedlabs.scope.utils.sourcecode.ExceptionSourceCodeFactory;
import com.undefinedlabs.scope.utils.sourcecode.ExceptionSourceCodeFrame;
import com.undefinedlabs.scope.utils.tag.TagKeys;
import com.undefinedlabs.scope.utils.tag.TagValues;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.Tracer;
import io.opentracing.propagation.Format;
import io.opentracing.tag.Tags;
import java.util.HashMap;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import scala.util.Try;

public class RequestContextScopeAgentRule extends AbstractScopeAgentRule {

  @Override
  protected ElementMatcher typeMatcher() {
    return hasSuperType(named("akka.http.scaladsl.server.RequestContext"));
  }

  @Override
  protected ElementMatcher classLoaderMatcher() {
    return hasClassesNamed("akka.http.scaladsl.server.RequestContext");
  }

  @Override
  protected Map, String> transformers() {
    final Map, String> transformers = new HashMap<>();
    transformers.put(
        named("complete"), RequestContextScopeAgentRule.class.getName() + "$CompleteAdvice");
    return transformers;
  }

  public static class CompleteAdvice {

    @Advice.OnMethodExit
    public static void exit(
        @Advice.This final RequestContext thiz,
        @Advice.Return final scala.concurrent.Future futureRouteResult) {

      final HttpRequest request = thiz.request();
      final Tracer tracer = ScopeGlobalTracer.get();
      final SpanContext parentSpanCtx =
          tracer.extract(Format.Builtin.HTTP_HEADERS, new HttpAkkaRequestExtractAdapter(request));

      final String operationName = String.format("HTTP %s", request.method().name());
      final Tracer.SpanBuilder spanBuilder =
          (parentSpanCtx == null)
              ? tracer.buildSpan(operationName)
              : tracer.buildSpan(operationName).asChildOf(parentSpanCtx);

      final Span span =
          spanBuilder
              .withTag(TagKeys.SPAN_KIND, Tags.SPAN_KIND_SERVER)
              .withTag(TagKeys.COMPONENT, TagValues.Component.AKKA_HTTP)
              .withTag(TagKeys.Network.HTTP_URL, request.uri().toString())
              .withTag(TagKeys.Network.HTTP_METHOD, request.method().name())
              .withTag(TagKeys.Network.PEER_SERVICE, TagValues.Network.Service.HTTP)
              .withTag(TagKeys.LANGUAGE, TagValues.Language.SCALA)
              .start();

      // **
      // Request cannot be unmarshalled multiple times if it is not complete loaded on memory.
      // So, it is not possible to provide http.request_payload.
      // **

      final Map httpReqHeaders =
          HttpHeadersExtractorForSpan.DEFAULT.extract(
              new AkkaHttpHeadersAdapter(thiz.request().getHeaders()));
      SpanUtils.INSTANCE.setTagObject(span, TagKeys.Network.HTTP_REQUEST_HEADERS, httpReqHeaders);

      final boolean instrumentPayloads =
          (boolean)
              ScopeSettingsResolver.INSTANCE
                  .get()
                  .getSetting(ScopeSettings.SCOPE_INSTRUMENTATION_HTTP_PAYLOADS);
      span.setTag(
          TagKeys.Network.HTTP_REQUEST_PAYLOAD_UNAVAILABLE,
          (instrumentPayloads)
              ? TagValues.Network.HTTP_PAYLOAD_NOT_ACCESSIBLE
              : TagValues.Network.HTTP_PAYLOAD_DISABLED);

      final ScopeSpanContainer scopeSpanContainer =
          new ScopeSpanContainer(span, tracer.activateSpan(span));
      futureRouteResult.andThen(
          ScopeJavaPartialFunctionCreator.create(scopeSpanContainer), thiz.executionContext());
    }
  }

  public static class ScopeJavaPartialFunctionCreator {

    public static ScopeAkkaJavaPartialFunction create(final ScopeSpanContainer scopeSpanContainer) {
      return new ScopeAkkaJavaPartialFunction(scopeSpanContainer) {

        @Override
        public Object apply(final Object result, final boolean isCheck) throws Exception {
          if (!(result instanceof Try) && !(((Try) result).get() instanceof RouteResult)) {
            return result;
          }

          final Try routeResultTry = (Try) result;
          final ScopeSpanContainer scopeSpanContainer = scopeSpanContainer();
          final Span span = scopeSpanContainer.getSpan();
          final Scope scope = scopeSpanContainer.getScope();

          try {
            if (routeResultTry.isSuccess()) {
              final RouteResult routeResult = routeResultTry.get();
              final RouteResult.Complete complete = (RouteResult.Complete) routeResult;
              final HttpResponse response = complete.response();

              final int statusCode = response.status().intValue();

              final Map httpResHeaders =
                  HttpHeadersExtractorForSpan.DEFAULT.extract(
                      new AkkaHttpHeadersAdapter(response.getHeaders()));
              SpanUtils.INSTANCE.setTagObject(
                  span, TagKeys.Network.HTTP_RESPONSE_HEADERS, httpResHeaders);

              // **
              // Response cannot be unmarshalled multiple times if it is not complete loaded on
              // memory.
              // So, it is not possible to provide http.response_payload.

              final boolean instrumentPayloads =
                  (boolean)
                      ScopeSettingsResolver.INSTANCE
                          .get()
                          .getSetting(ScopeSettings.SCOPE_INSTRUMENTATION_HTTP_PAYLOADS);
              span.setTag(
                  TagKeys.Network.HTTP_RESPONSE_PAYLOAD_UNAVAILABLE,
                  (instrumentPayloads)
                      ? TagValues.Network.HTTP_PAYLOAD_NOT_ACCESSIBLE
                      : TagValues.Network.HTTP_PAYLOAD_DISABLED);

              span.setTag(TagKeys.Network.HTTP_STATUS_CODE, statusCode);
              span.setTag(TagKeys.ERROR, statusCode >= 400);

            } else {
              final Try failed = routeResultTry.failed();

              if (failed.isSuccess()) {
                span.setTag(TagKeys.ERROR, true);

                final Throwable throwable = failed.get();
                final ExceptionSourceCodeFrame exceptionSourceCodeFrame =
                    ExceptionSourceCodeFactory.INSTANCE.createFrame(throwable);
                final ThrowableEvent.Builder throwableEventBuilder = ThrowableEvent.newBuilder();
                throwableEventBuilder
                    .withEventType(EventValues.General.ERROR)
                    .withThrowable(exceptionSourceCodeFrame.getUserThrowable())
                    .withSource(
                        exceptionSourceCodeFrame.getSourceCodeFrame().getLinkPathWithMethodLine());

                span.log(EventFieldsFactory.INSTANCE.createFields(throwableEventBuilder.build()));
              }
            }
          } finally {
            span.finish();
            scope.close();
          }

          return result;
        }
      };
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy