com.undefinedlabs.scope.rules.RequestContextScopeAgentRule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scope-rule-akka-http Show documentation
Show all versions of scope-rule-akka-http Show documentation
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.
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 super TypeDescription> typeMatcher() {
return hasSuperType(named("akka.http.scaladsl.server.RequestContext"));
}
@Override
protected ElementMatcher classLoaderMatcher() {
return hasClassesNamed("akka.http.scaladsl.server.RequestContext");
}
@Override
protected Map extends ElementMatcher super MethodDescription>, 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