Skip to content

Commit b802d15

Browse files
committed
add code attributes to play mvc
1 parent b1eb1fd commit b802d15

File tree

6 files changed

+140
-26
lines changed

6 files changed

+140
-26
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.play.v2_4;
7+
8+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
9+
import javax.annotation.Nullable;
10+
11+
public class ActionCodeAttributesGetter implements CodeAttributesGetter<ActionData> {
12+
@Nullable
13+
@Override
14+
public Class<?> getCodeClass(ActionData actionData) {
15+
return actionData.codeClass();
16+
}
17+
18+
@Nullable
19+
@Override
20+
public String getMethodName(ActionData actionData) {
21+
return actionData.methodName();
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.play.v2_4;
7+
8+
import java.lang.reflect.Method;
9+
10+
public class ActionData {
11+
private final Class<?> target;
12+
private final Method method;
13+
14+
public ActionData(Class<?> target, Method method) {
15+
this.target = target;
16+
this.method = method;
17+
}
18+
19+
public Class<?> codeClass() {
20+
return target;
21+
}
22+
23+
public String methodName() {
24+
return method.getName();
25+
}
26+
}

instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_4/ActionInstrumentation.java

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,15 @@
88
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
99
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
1010
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
11-
import static io.opentelemetry.javaagent.instrumentation.play.v2_4.Play24Singletons.instrumenter;
1211
import static io.opentelemetry.javaagent.instrumentation.play.v2_4.Play24Singletons.updateSpan;
1312
import static net.bytebuddy.matcher.ElementMatchers.named;
1413
import static net.bytebuddy.matcher.ElementMatchers.returns;
1514
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
1615

1716
import io.opentelemetry.context.Context;
18-
import io.opentelemetry.context.Scope;
1917
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
2018
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
19+
import java.lang.reflect.Method;
2120
import net.bytebuddy.asm.Advice;
2221
import net.bytebuddy.description.type.TypeDescription;
2322
import net.bytebuddy.matcher.ElementMatcher;
@@ -50,17 +49,14 @@ public void transform(TypeTransformer transformer) {
5049
public static class ApplyAdvice {
5150

5251
@Advice.OnMethodEnter(suppress = Throwable.class)
53-
public static void onEnter(
54-
@Advice.Argument(0) Request<?> req,
55-
@Advice.Local("otelContext") Context context,
56-
@Advice.Local("otelScope") Scope scope) {
52+
public static ActionScope onEnter(
53+
@Advice.This Object target,
54+
@Advice.Origin Method method,
55+
@Advice.Argument(0) Request<?> req) {
5756
Context parentContext = currentContext();
58-
if (!instrumenter().shouldStart(parentContext, null)) {
59-
return;
60-
}
6157

62-
context = instrumenter().start(parentContext, null);
63-
scope = context.makeCurrent();
58+
ActionData actionData = new ActionData(target.getClass(), method);
59+
return ActionScope.start(parentContext, actionData);
6460
}
6561

6662
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
@@ -69,20 +65,21 @@ public static void stopTraceOnResponse(
6965
@Advice.Thrown Throwable throwable,
7066
@Advice.Argument(0) Request<?> req,
7167
@Advice.Return(readOnly = false) Future<Result> responseFuture,
72-
@Advice.Local("otelContext") Context context,
73-
@Advice.Local("otelScope") Scope scope) {
74-
if (scope == null) {
68+
@Advice.Enter ActionScope actionScope) {
69+
if (actionScope == null || actionScope.getScope() == null) {
7570
return;
7671
}
77-
scope.close();
72+
actionScope.closeScope();
73+
74+
updateSpan(actionScope.getContext(), req);
7875

79-
updateSpan(context, req);
8076
// span finished in RequestCompleteCallback
8177
if (throwable == null) {
8278
responseFuture.onComplete(
83-
new RequestCompleteCallback(context), ((Action<?>) thisAction).executionContext());
79+
new RequestCompleteCallback(actionScope.getContext()),
80+
((Action<?>) thisAction).executionContext());
8481
} else {
85-
instrumenter().end(context, null, null, throwable);
82+
actionScope.end(throwable);
8683
}
8784
}
8885
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.play.v2_4;
7+
8+
import static io.opentelemetry.javaagent.instrumentation.play.v2_4.Play24Singletons.instrumenter;
9+
10+
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.context.Scope;
12+
13+
/** Container used to carry state between enter and exit advices */
14+
public final class ActionScope {
15+
16+
private final ActionData actionData;
17+
private final Context context;
18+
private final Scope scope;
19+
20+
public ActionScope(Context context, Scope scope, ActionData actionData) {
21+
this.actionData = actionData;
22+
this.context = context;
23+
this.scope = scope;
24+
}
25+
26+
public Context getContext() {
27+
return context;
28+
}
29+
30+
public Scope getScope() {
31+
return scope;
32+
}
33+
34+
public static ActionScope start(Context parentContext, ActionData actionData) {
35+
if (!instrumenter().shouldStart(parentContext, actionData)) {
36+
return null;
37+
}
38+
39+
Context context = instrumenter().start(parentContext, actionData);
40+
return new ActionScope(context, context.makeCurrent(), actionData);
41+
}
42+
43+
public void closeScope() {
44+
if (scope != null) {
45+
scope.close();
46+
}
47+
}
48+
49+
public void end(Throwable throwable) {
50+
closeScope();
51+
instrumenter().end(context, actionData, null, throwable);
52+
}
53+
}

instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_4/Play24Singletons.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.opentelemetry.api.GlobalOpenTelemetry;
99
import io.opentelemetry.api.trace.Span;
1010
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
1112
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
1213
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
1314
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource;
@@ -16,15 +17,20 @@
1617
import scala.Option;
1718

1819
public final class Play24Singletons {
19-
2020
private static final String SPAN_NAME = "play.request";
21-
private static final Instrumenter<Void, Void> INSTRUMENTER =
22-
Instrumenter.<Void, Void>builder(
23-
GlobalOpenTelemetry.get(), "io.opentelemetry.play-mvc-2.4", s -> SPAN_NAME)
24-
.setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled())
25-
.buildInstrumenter();
21+
private static final Instrumenter<ActionData, Void> INSTRUMENTER;
22+
23+
static {
24+
INSTRUMENTER =
25+
Instrumenter.<ActionData, Void>builder(
26+
GlobalOpenTelemetry.get(), "io.opentelemetry.play-mvc-2.4", s -> SPAN_NAME)
27+
.addAttributesExtractor(
28+
CodeAttributesExtractor.create(new ActionCodeAttributesGetter()))
29+
.setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled())
30+
.buildInstrumenter();
31+
}
2632

27-
public static Instrumenter<Void, Void> instrumenter() {
33+
public static Instrumenter<ActionData, Void> instrumenter() {
2834
return INSTRUMENTER;
2935
}
3036

instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/play/v2_4/server/PlayServerTest.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM;
1414
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT;
1515
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS;
16+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
1617
import static io.opentelemetry.semconv.HttpAttributes.HTTP_ROUTE;
18+
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION;
19+
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE;
1720

1821
import io.opentelemetry.api.common.AttributeKey;
1922
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
@@ -112,9 +115,15 @@ protected void configure(HttpServerTestOptions options) {
112115
}
113116

114117
@Override
118+
@SuppressWarnings("deprecation") // uses deprecated semconv
115119
public SpanDataAssert assertHandlerSpan(
116120
SpanDataAssert span, String method, ServerEndpoint endpoint) {
117-
span.hasName("play.request").hasKind(INTERNAL);
121+
span.hasName("play.request")
122+
.hasKind(INTERNAL)
123+
.hasAttributesSatisfyingExactly(
124+
equalTo(CODE_NAMESPACE, "play.api.mvc.ActionBuilder$$anon$2"),
125+
equalTo(CODE_FUNCTION, "apply"));
126+
118127
if (endpoint == EXCEPTION) {
119128
span.hasStatus(StatusData.error());
120129
span.hasException(new IllegalArgumentException(EXCEPTION.getBody()));

0 commit comments

Comments
 (0)