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 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.rules.transformer.ScopeAgentAdvicedTransformer;
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 net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import scala.util.Try;

import java.util.Collections;
import java.util.Map;

import static net.bytebuddy.matcher.ElementMatchers.*;

public class RequestContextScopeAgentRule extends AbstractScopeAgentRule {

    @Override
    protected Iterable transformers() {
        final Class instrumentedClass = lookUp("akka.http.scaladsl.server.RequestContext");

        return (instrumentedClass != null) ?
                Collections.singleton(newAgentBuilder()
                        .type(isSubTypeOf(instrumentedClass), isSystemClassLoader()).transform(new ScopeAgentAdvicedTransformer(CompleteAdvice.class, named("complete"))))
                : emptyCollection();
    }

    public static class CompleteAdvice {

        @Advice.OnMethodExit
        public static void exit(@Advice.This RequestContext thiz, @Advice.Return 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(Object result, 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