
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 extends AgentBuilder> 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