Skip to content

Commit fcebab2

Browse files
lauritjaydeluca
andauthored
Capture finatra code function name (#13939)
Co-authored-by: Jay DeLuca <[email protected]>
1 parent 4774069 commit fcebab2

File tree

9 files changed

+185
-30
lines changed

9 files changed

+185
-30
lines changed

instrumentation/finatra-2.9/javaagent/src/latestDepTest/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerLatestTest.scala

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@ import io.opentelemetry.instrumentation.testing.junit.http.{
1414
HttpServerTestOptions,
1515
ServerEndpoint
1616
}
17-
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo
17+
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.{
18+
StringAssertConsumer,
19+
equalTo,
20+
satisfies
21+
}
1822
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert
1923
import io.opentelemetry.sdk.trace.data.StatusData
2024
import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes
25+
import org.assertj.core.api.AbstractStringAssert
2126
import org.junit.jupiter.api.extension.RegisterExtension
2227

2328
import java.util.concurrent.Executors
@@ -69,9 +74,18 @@ class FinatraServerLatestTest extends AbstractHttpServerTest[HttpServer] {
6974
)
7075
.hasKind(SpanKind.INTERNAL)
7176
.hasAttributesSatisfyingExactly(
72-
equalTo(
77+
satisfies(
7378
CodeIncubatingAttributes.CODE_NAMESPACE,
74-
"io.opentelemetry.javaagent.instrumentation.finatra.FinatraController"
79+
new StringAssertConsumer {
80+
override def accept(t: AbstractStringAssert[_]): Unit =
81+
t.startsWith(
82+
"io.opentelemetry.javaagent.instrumentation.finatra.FinatraController"
83+
)
84+
}
85+
),
86+
equalTo(
87+
CodeIncubatingAttributes.CODE_FUNCTION,
88+
"apply"
7589
)
7690
)
7791

instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraCodeAttributesGetter.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@
88
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
99
import javax.annotation.Nullable;
1010

11-
public class FinatraCodeAttributesGetter implements CodeAttributesGetter<Class<?>> {
11+
public class FinatraCodeAttributesGetter implements CodeAttributesGetter<FinatraRequest> {
1212
@Nullable
1313
@Override
14-
public Class<?> getCodeClass(Class<?> request) {
15-
return request;
14+
public Class<?> getCodeClass(FinatraRequest request) {
15+
return request.declaringClass();
1616
}
1717

1818
@Nullable
1919
@Override
20-
public String getMethodName(Class<?> request) {
21-
return null;
20+
public String getMethodName(FinatraRequest request) {
21+
return request.methodName();
2222
}
2323
}

instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraInstrumentationModule.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ public FinatraInstrumentationModule() {
2020

2121
@Override
2222
public List<TypeInstrumentation> typeInstrumentations() {
23-
return asList(new FinatraRouteInstrumentation(), new FinatraExceptionManagerInstrumentation());
23+
return asList(
24+
new FinatraRouteInstrumentation(),
25+
new FinatraRouteBuilderInstrumentation(),
26+
new FinatraExceptionManagerInstrumentation());
2427
}
2528
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.finatra;
7+
8+
import javax.annotation.Nullable;
9+
10+
public final class FinatraRequest {
11+
private final Class<?> controllerClass;
12+
private final Class<?> declaringClass;
13+
private final String methodName;
14+
15+
private FinatraRequest(Class<?> controllerClass, Class<?> declaringClass, String methodName) {
16+
this.controllerClass = controllerClass;
17+
this.declaringClass = declaringClass;
18+
this.methodName = methodName;
19+
}
20+
21+
public static FinatraRequest create(Class<?> controllerClass) {
22+
return new FinatraRequest(controllerClass, null, null);
23+
}
24+
25+
public static FinatraRequest create(
26+
Class<?> controllerClass, @Nullable Class<?> declaringClass, @Nullable String methodName) {
27+
return new FinatraRequest(controllerClass, declaringClass, methodName);
28+
}
29+
30+
public Class<?> controllerClass() {
31+
return controllerClass;
32+
}
33+
34+
@Nullable
35+
public Class<?> declaringClass() {
36+
return declaringClass;
37+
}
38+
39+
@Nullable
40+
public String methodName() {
41+
return methodName;
42+
}
43+
}

instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraResponseListener.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ public final class FinatraResponseListener implements FutureEventListener<Respon
1818
VirtualField.find(Response.class, Throwable.class);
1919

2020
private final Context context;
21-
private final Class<?> request;
21+
private final FinatraRequest request;
2222

23-
public FinatraResponseListener(Context context, Class<?> request) {
23+
public FinatraResponseListener(Context context, FinatraRequest request) {
2424
this.context = context;
2525
this.request = request;
2626
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.finatra;
7+
8+
import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.setCallbackClass;
9+
import static net.bytebuddy.matcher.ElementMatchers.named;
10+
import static net.bytebuddy.matcher.ElementMatchers.returns;
11+
12+
import com.twitter.finatra.http.internal.routing.Route;
13+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
14+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
15+
import net.bytebuddy.asm.Advice;
16+
import net.bytebuddy.description.type.TypeDescription;
17+
import net.bytebuddy.matcher.ElementMatcher;
18+
import scala.Function1;
19+
20+
public class FinatraRouteBuilderInstrumentation implements TypeInstrumentation {
21+
@Override
22+
public ElementMatcher<TypeDescription> typeMatcher() {
23+
return named("com.twitter.finatra.http.RouteBuilder");
24+
}
25+
26+
@Override
27+
public void transform(TypeTransformer transformer) {
28+
transformer.applyAdviceToMethod(
29+
named("build").and(returns(named("com.twitter.finatra.http.internal.routing.Route"))),
30+
this.getClass().getName() + "$BuildAdvice");
31+
}
32+
33+
@SuppressWarnings("unused")
34+
public static class BuildAdvice {
35+
36+
@Advice.OnMethodExit(suppress = Throwable.class)
37+
public static void onExit(
38+
@Advice.Return Route route, @Advice.FieldValue("callback") Function1<?, ?> callback) {
39+
setCallbackClass(route, callback.getClass());
40+
}
41+
}
42+
}

instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraRouteInstrumentation.java

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@
55

66
package io.opentelemetry.javaagent.instrumentation.finatra;
77

8+
import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.getCallbackClass;
89
import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.instrumenter;
10+
import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.setCallbackClass;
911
import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.updateServerSpanName;
10-
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
1112
import static net.bytebuddy.matcher.ElementMatchers.named;
13+
import static net.bytebuddy.matcher.ElementMatchers.returns;
1214
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
1315
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
1416

1517
import com.twitter.finagle.http.Response;
1618
import com.twitter.finatra.http.contexts.RouteInfo;
19+
import com.twitter.finatra.http.internal.routing.Route;
1720
import com.twitter.util.Future;
1821
import io.opentelemetry.context.Context;
1922
import io.opentelemetry.context.Scope;
@@ -35,26 +38,38 @@ public ElementMatcher<TypeDescription> typeMatcher() {
3538
@Override
3639
public void transform(TypeTransformer transformer) {
3740
transformer.applyAdviceToMethod(
38-
isMethod()
39-
.and(named("handleMatch"))
41+
named("handleMatch")
4042
.and(takesArguments(2))
4143
.and(takesArgument(0, named("com.twitter.finagle.http.Request"))),
4244
this.getClass().getName() + "$HandleMatchAdvice");
45+
transformer.applyAdviceToMethod(
46+
named("copy").and(returns(named("com.twitter.finatra.http.internal.routing.Route"))),
47+
this.getClass().getName() + "$CopyAdvice");
4348
}
4449

4550
@SuppressWarnings("unused")
4651
public static class HandleMatchAdvice {
4752

4853
@Advice.OnMethodEnter(suppress = Throwable.class)
4954
public static void nameSpan(
55+
@Advice.This Route route,
5056
@Advice.FieldValue("routeInfo") RouteInfo routeInfo,
51-
@Advice.FieldValue("clazz") Class<?> request,
57+
@Advice.FieldValue("clazz") Class<?> controllerClass,
5258
@Advice.Local("otelContext") Context context,
53-
@Advice.Local("otelScope") Scope scope) {
54-
59+
@Advice.Local("otelScope") Scope scope,
60+
@Advice.Local("otelRequest") FinatraRequest request) {
5561
Context parentContext = Java8BytecodeBridge.currentContext();
5662
updateServerSpanName(parentContext, routeInfo);
5763

64+
Class<?> callbackClass = getCallbackClass(route);
65+
// We expect callback to be an inner class of the controller class. If it is not we are not
66+
// going to record it at all.
67+
if (callbackClass != null) {
68+
request = FinatraRequest.create(controllerClass, callbackClass, "apply");
69+
} else {
70+
request = FinatraRequest.create(controllerClass);
71+
}
72+
5873
if (!instrumenter().shouldStart(parentContext, request)) {
5974
return;
6075
}
@@ -67,10 +82,9 @@ public static void nameSpan(
6782
public static void setupCallback(
6883
@Advice.Thrown Throwable throwable,
6984
@Advice.Return Some<Future<Response>> responseOption,
70-
@Advice.FieldValue("clazz") Class<?> request,
7185
@Advice.Local("otelContext") Context context,
72-
@Advice.Local("otelScope") Scope scope) {
73-
86+
@Advice.Local("otelScope") Scope scope,
87+
@Advice.Local("otelRequest") FinatraRequest request) {
7488
if (scope == null) {
7589
return;
7690
}
@@ -83,4 +97,13 @@ public static void setupCallback(
8397
}
8498
}
8599
}
100+
101+
@SuppressWarnings("unused")
102+
public static class CopyAdvice {
103+
104+
@Advice.OnMethodExit(suppress = Throwable.class)
105+
public static void onExit(@Advice.This Route route, @Advice.Return Route result) {
106+
setCallbackClass(result, getCallbackClass(route));
107+
}
108+
}
86109
}

instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraSingletons.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,52 @@
66
package io.opentelemetry.javaagent.instrumentation.finatra;
77

88
import com.twitter.finatra.http.contexts.RouteInfo;
9+
import com.twitter.finatra.http.internal.routing.Route;
910
import io.opentelemetry.api.GlobalOpenTelemetry;
1011
import io.opentelemetry.context.Context;
1112
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
12-
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor;
1313
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
14+
import io.opentelemetry.instrumentation.api.internal.ClassNames;
1415
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
1516
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource;
17+
import io.opentelemetry.instrumentation.api.util.VirtualField;
1618

1719
public final class FinatraSingletons {
1820

19-
private static final Instrumenter<Class<?>, Void> INSTRUMENTER;
21+
private static final Instrumenter<FinatraRequest, Void> INSTRUMENTER;
2022

2123
static {
2224
FinatraCodeAttributesGetter codeAttributesGetter = new FinatraCodeAttributesGetter();
2325
INSTRUMENTER =
24-
Instrumenter.<Class<?>, Void>builder(
26+
Instrumenter.<FinatraRequest, Void>builder(
2527
GlobalOpenTelemetry.get(),
2628
"io.opentelemetry.finatra-2.9",
27-
CodeSpanNameExtractor.create(codeAttributesGetter))
29+
request ->
30+
request.controllerClass() != null
31+
? ClassNames.simpleName(request.controllerClass())
32+
: "<unknown>")
2833
.addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter))
2934
.buildInstrumenter();
3035
}
3136

32-
public static Instrumenter<Class<?>, Void> instrumenter() {
37+
public static Instrumenter<FinatraRequest, Void> instrumenter() {
3338
return INSTRUMENTER;
3439
}
3540

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

45+
private static final VirtualField<Route, Class<?>> callbackClassField =
46+
VirtualField.find(Route.class, Class.class);
47+
48+
public static void setCallbackClass(Route route, Class<?> clazz) {
49+
callbackClassField.set(route, clazz);
50+
}
51+
52+
public static Class<?> getCallbackClass(Route route) {
53+
return callbackClassField.get(route);
54+
}
55+
4056
private FinatraSingletons() {}
4157
}

instrumentation/finatra-2.9/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerTest.scala

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,19 @@ import io.opentelemetry.instrumentation.testing.junit.http.{
1414
HttpServerTestOptions,
1515
ServerEndpoint
1616
}
17-
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo
18-
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert
17+
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.{
18+
StringAssertConsumer,
19+
equalTo,
20+
satisfies
21+
}
22+
import io.opentelemetry.sdk.testing.assertj.{SpanDataAssert, TraceAssert}
1923
import io.opentelemetry.sdk.trace.data.StatusData
2024
import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes
25+
import org.assertj.core.api.AbstractStringAssert
2126
import org.junit.jupiter.api.extension.RegisterExtension
2227

2328
import java.util.concurrent.Executors
24-
import java.util.function.Predicate
29+
import java.util.function.{Consumer, Predicate}
2530
import scala.concurrent.{ExecutionContext, Future}
2631
import scala.language.postfixOps
2732

@@ -70,9 +75,18 @@ class FinatraServerTest extends AbstractHttpServerTest[HttpServer] {
7075
)
7176
.hasKind(SpanKind.INTERNAL)
7277
.hasAttributesSatisfyingExactly(
73-
equalTo(
78+
satisfies(
7479
CodeIncubatingAttributes.CODE_NAMESPACE,
75-
"io.opentelemetry.javaagent.instrumentation.finatra.FinatraController"
80+
new StringAssertConsumer {
81+
override def accept(t: AbstractStringAssert[_]): Unit =
82+
t.startsWith(
83+
"io.opentelemetry.javaagent.instrumentation.finatra.FinatraController"
84+
)
85+
}
86+
),
87+
equalTo(
88+
CodeIncubatingAttributes.CODE_FUNCTION,
89+
"apply"
7690
)
7791
)
7892

0 commit comments

Comments
 (0)