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

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

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