Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ import io.opentelemetry.instrumentation.testing.junit.http.{
HttpServerTestOptions,
ServerEndpoint
}
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.{
StringAssertConsumer,
equalTo,
satisfies
}
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert
import io.opentelemetry.sdk.trace.data.StatusData
import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes
import org.assertj.core.api.AbstractStringAssert
import org.junit.jupiter.api.extension.RegisterExtension

import java.util.concurrent.Executors
Expand Down Expand Up @@ -69,9 +74,18 @@ class FinatraServerLatestTest extends AbstractHttpServerTest[HttpServer] {
)
.hasKind(SpanKind.INTERNAL)
.hasAttributesSatisfyingExactly(
equalTo(
satisfies(
CodeIncubatingAttributes.CODE_NAMESPACE,
"io.opentelemetry.javaagent.instrumentation.finatra.FinatraController"
new StringAssertConsumer {
override def accept(t: AbstractStringAssert[_]): Unit =
t.startsWith(
"io.opentelemetry.javaagent.instrumentation.finatra.FinatraController"
)
}
),
equalTo(
CodeIncubatingAttributes.CODE_FUNCTION,
"apply"
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
import javax.annotation.Nullable;

public class FinatraCodeAttributesGetter implements CodeAttributesGetter<Class<?>> {
public class FinatraCodeAttributesGetter implements CodeAttributesGetter<FinatraRequest> {
@Nullable
@Override
public Class<?> getCodeClass(Class<?> request) {
return request;
public Class<?> getCodeClass(FinatraRequest request) {
return request.declaringClass();
}

@Nullable
@Override
public String getMethodName(Class<?> request) {
return null;
public String getMethodName(FinatraRequest request) {
return request.methodName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public FinatraInstrumentationModule() {

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(new FinatraRouteInstrumentation(), new FinatraExceptionManagerInstrumentation());
return asList(
new FinatraRouteInstrumentation(),
new FinatraRouteBuilderInstrumentation(),
new FinatraExceptionManagerInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.finatra;

import javax.annotation.Nullable;

public final class FinatraRequest {
private final Class<?> controllerClass;
private final Class<?> declaringClass;
private final String methodName;

private FinatraRequest(Class<?> controllerClass, Class<?> declaringClass, String methodName) {
this.controllerClass = controllerClass;
this.declaringClass = declaringClass;
this.methodName = methodName;
}

public static FinatraRequest create(Class<?> controllerClass) {
return new FinatraRequest(controllerClass, null, null);
}

public static FinatraRequest create(
Class<?> controllerClass, @Nullable Class<?> declaringClass, @Nullable String methodName) {
return new FinatraRequest(controllerClass, declaringClass, methodName);
}

public Class<?> controllerClass() {
return controllerClass;
}

@Nullable
public Class<?> declaringClass() {
return declaringClass;
}

@Nullable
public String methodName() {
return methodName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ public final class FinatraResponseListener implements FutureEventListener<Respon
VirtualField.find(Response.class, Throwable.class);

private final Context context;
private final Class<?> request;
private final FinatraRequest request;

public FinatraResponseListener(Context context, Class<?> request) {
public FinatraResponseListener(Context context, FinatraRequest request) {
this.context = context;
this.request = request;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.finatra;

import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.setCallbackClass;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;

import com.twitter.finatra.http.internal.routing.Route;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import scala.Function1;

public class FinatraRouteBuilderInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.twitter.finatra.http.RouteBuilder");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("build").and(returns(named("com.twitter.finatra.http.internal.routing.Route"))),
this.getClass().getName() + "$BuildAdvice");
}

@SuppressWarnings("unused")
public static class BuildAdvice {

@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(
@Advice.Return Route route, @Advice.FieldValue("callback") Function1<?, ?> callback) {
setCallbackClass(route, callback.getClass());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@

package io.opentelemetry.javaagent.instrumentation.finatra;

import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.getCallbackClass;
import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.instrumenter;
import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.setCallbackClass;
import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.updateServerSpanName;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import com.twitter.finagle.http.Response;
import com.twitter.finatra.http.contexts.RouteInfo;
import com.twitter.finatra.http.internal.routing.Route;
import com.twitter.util.Future;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
Expand All @@ -35,26 +38,38 @@ public ElementMatcher<TypeDescription> typeMatcher() {
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(named("handleMatch"))
named("handleMatch")
.and(takesArguments(2))
.and(takesArgument(0, named("com.twitter.finagle.http.Request"))),
this.getClass().getName() + "$HandleMatchAdvice");
transformer.applyAdviceToMethod(
named("copy").and(returns(named("com.twitter.finatra.http.internal.routing.Route"))),
this.getClass().getName() + "$CopyAdvice");
}

@SuppressWarnings("unused")
public static class HandleMatchAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static void nameSpan(
@Advice.This Route route,
@Advice.FieldValue("routeInfo") RouteInfo routeInfo,
@Advice.FieldValue("clazz") Class<?> request,
@Advice.FieldValue("clazz") Class<?> controllerClass,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {

@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelRequest") FinatraRequest request) {
Context parentContext = Java8BytecodeBridge.currentContext();
updateServerSpanName(parentContext, routeInfo);

Class<?> callbackClass = getCallbackClass(route);
// We expect callback to be an inner class of the controller class. If it is not we are not
// going to record it at all.
if (callbackClass != null) {
request = FinatraRequest.create(controllerClass, callbackClass, "apply");
} else {
request = FinatraRequest.create(controllerClass);
}

if (!instrumenter().shouldStart(parentContext, request)) {
return;
}
Expand All @@ -67,10 +82,9 @@ public static void nameSpan(
public static void setupCallback(
@Advice.Thrown Throwable throwable,
@Advice.Return Some<Future<Response>> responseOption,
@Advice.FieldValue("clazz") Class<?> request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {

@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelRequest") FinatraRequest request) {
if (scope == null) {
return;
}
Expand All @@ -83,4 +97,13 @@ public static void setupCallback(
}
}
}

@SuppressWarnings("unused")
public static class CopyAdvice {

@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(@Advice.This Route route, @Advice.Return Route result) {
setCallbackClass(result, getCallbackClass(route));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,52 @@
package io.opentelemetry.javaagent.instrumentation.finatra;

import com.twitter.finatra.http.contexts.RouteInfo;
import com.twitter.finatra.http.internal.routing.Route;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.internal.ClassNames;
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource;
import io.opentelemetry.instrumentation.api.util.VirtualField;

public final class FinatraSingletons {

private static final Instrumenter<Class<?>, Void> INSTRUMENTER;
private static final Instrumenter<FinatraRequest, Void> INSTRUMENTER;

static {
FinatraCodeAttributesGetter codeAttributesGetter = new FinatraCodeAttributesGetter();
INSTRUMENTER =
Instrumenter.<Class<?>, Void>builder(
Instrumenter.<FinatraRequest, Void>builder(
GlobalOpenTelemetry.get(),
"io.opentelemetry.finatra-2.9",
CodeSpanNameExtractor.create(codeAttributesGetter))
request ->
request.controllerClass() != null
? ClassNames.simpleName(request.controllerClass())
: "<unknown>")
.addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter))
.buildInstrumenter();
}

public static Instrumenter<Class<?>, Void> instrumenter() {
public static Instrumenter<FinatraRequest, Void> instrumenter() {
return INSTRUMENTER;
}

public static void updateServerSpanName(Context context, RouteInfo routeInfo) {
HttpServerRoute.update(context, HttpServerRouteSource.CONTROLLER, routeInfo.path());
}

private static final VirtualField<Route, Class<?>> callbackClassFiled =
VirtualField.find(Route.class, Class.class);

public static void setCallbackClass(Route route, Class<?> clazz) {
callbackClassFiled.set(route, clazz);
}

public static Class<?> getCallbackClass(Route route) {
return callbackClassFiled.get(route);
}

private FinatraSingletons() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,19 @@ import io.opentelemetry.instrumentation.testing.junit.http.{
HttpServerTestOptions,
ServerEndpoint
}
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.{
StringAssertConsumer,
equalTo,
satisfies
}
import io.opentelemetry.sdk.testing.assertj.{SpanDataAssert, TraceAssert}
import io.opentelemetry.sdk.trace.data.StatusData
import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes
import org.assertj.core.api.AbstractStringAssert
import org.junit.jupiter.api.extension.RegisterExtension

import java.util.concurrent.Executors
import java.util.function.Predicate
import java.util.function.{Consumer, Predicate}
import scala.concurrent.{ExecutionContext, Future}
import scala.language.postfixOps

Expand Down Expand Up @@ -70,9 +75,18 @@ class FinatraServerTest extends AbstractHttpServerTest[HttpServer] {
)
.hasKind(SpanKind.INTERNAL)
.hasAttributesSatisfyingExactly(
equalTo(
satisfies(
CodeIncubatingAttributes.CODE_NAMESPACE,
"io.opentelemetry.javaagent.instrumentation.finatra.FinatraController"
new StringAssertConsumer {
override def accept(t: AbstractStringAssert[_]): Unit =
t.startsWith(
"io.opentelemetry.javaagent.instrumentation.finatra.FinatraController"
)
}
),
equalTo(
CodeIncubatingAttributes.CODE_FUNCTION,
"apply"
)
)

Expand Down
Loading