Skip to content

Commit 5253316

Browse files
committed
feat(pekko): add tapir path matching instrumentation
1 parent 67b014f commit 5253316

File tree

4 files changed

+133
-3
lines changed

4 files changed

+133
-3
lines changed

instrumentation/pekko/pekko-http-1.0/javaagent/build.gradle.kts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,57 @@ muzzle {
1010
versions.set("[1.0,)")
1111
assertInverse.set(true)
1212
extraDependency("org.apache.pekko:pekko-stream_2.12:1.0.1")
13+
extraDependency("com.softwaremill.sttp.tapir:tapir-server_2.12:1.7.0")
14+
extraDependency("com.softwaremill.sttp.tapir:tapir-core_2.12:1.7.0")
1315
}
1416
pass {
1517
group.set("org.apache.pekko")
1618
module.set("pekko-http_2.13")
1719
versions.set("[1.0,)")
1820
assertInverse.set(true)
1921
extraDependency("org.apache.pekko:pekko-stream_2.13:1.0.1")
22+
extraDependency("com.softwaremill.sttp.tapir:tapir-server_2.12:1.7.0")
23+
extraDependency("com.softwaremill.sttp.tapir:tapir-core_2.12:1.7.0")
2024
}
2125
pass {
2226
group.set("org.apache.pekko")
2327
module.set("pekko-http_3")
2428
versions.set("[1.0,)")
2529
assertInverse.set(true)
2630
extraDependency("org.apache.pekko:pekko-stream_3:1.0.1")
31+
extraDependency("com.softwaremill.sttp.tapir:tapir-server_2.12:1.7.0")
32+
extraDependency("com.softwaremill.sttp.tapir:tapir-core_2.12:1.7.0")
33+
}
34+
pass {
35+
group.set("sttp.tapir_2.12")
36+
module.set("tapir-pekko-http-server")
37+
versions.set("[1.7,)")
38+
assertInverse.set(true)
39+
extraDependency("com.softwaremill.sttp.tapir:tapir-server_2.12:1.7.0")
40+
extraDependency("com.softwaremill.sttp.tapir:tapir-core_2.12:1.7.0")
41+
}
42+
pass {
43+
group.set("sttp.tapir_2.13")
44+
module.set("tapir-pekko-http-server")
45+
versions.set("[1.7,)")
46+
assertInverse.set(true)
47+
extraDependency("com.softwaremill.sttp.tapir:tapir-server_2.13:1.7.0")
48+
extraDependency("com.softwaremill.sttp.tapir:tapir-core_2.13:1.7.0")
49+
}
50+
pass {
51+
group.set("sttp.tapir_3")
52+
module.set("tapir-pekko-http-server")
53+
versions.set("[1.7,)")
54+
assertInverse.set(true)
55+
extraDependency("com.softwaremill.sttp.tapir:tapir-server_3:1.7.0")
56+
extraDependency("com.softwaremill.sttp.tapir:tapir-core_3:1.7.0")
2757
}
2858
}
2959

3060
dependencies {
3161
library("org.apache.pekko:pekko-http_2.12:1.0.0")
3262
library("org.apache.pekko:pekko-stream_2.12:1.0.1")
33-
34-
testImplementation("com.softwaremill.sttp.tapir:tapir-pekko-http-server_2.12:1.7.0")
63+
library("com.softwaremill.sttp.tapir:tapir-pekko-http-server_2.12:1.7.0")
3564

3665
testInstrumentation(project(":instrumentation:pekko:pekko-actor-1.0:javaagent"))
3766
testInstrumentation(project(":instrumentation:executors:javaagent"))

instrumentation/pekko/pekko-http-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/server/route/PekkoHttpServerRouteInstrumentationModule.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public String getModuleGroup() {
3232
@Override
3333
public List<TypeInstrumentation> typeInstrumentations() {
3434
return asList(
35+
new TapirPathInstrumentation(),
3536
new PathMatcherInstrumentation(),
3637
new PathMatcherStaticInstrumentation(),
3738
new RouteConcatenationInstrumentation(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.pekkohttp.v1_0.server.route;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.named;
9+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
10+
11+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
13+
import net.bytebuddy.asm.Advice;
14+
import net.bytebuddy.description.type.TypeDescription;
15+
import net.bytebuddy.matcher.ElementMatcher;
16+
import org.apache.pekko.http.scaladsl.server.RequestContext;
17+
import org.apache.pekko.http.scaladsl.server.RouteResult;
18+
import scala.Function1;
19+
import scala.Function2;
20+
import scala.Option;
21+
import scala.PartialFunction;
22+
import scala.Unit;
23+
import scala.concurrent.Future;
24+
import scala.util.Try;
25+
import sttp.tapir.EndpointInput;
26+
import sttp.tapir.server.ServerEndpoint;
27+
28+
public class TapirPathInstrumentation implements TypeInstrumentation {
29+
30+
@Override
31+
public ElementMatcher<TypeDescription> typeMatcher() {
32+
return named("sttp.tapir.server.pekkohttp.PekkoHttpServerInterpreter");
33+
}
34+
35+
@Override
36+
public void transform(TypeTransformer transformer) {
37+
transformer.applyAdviceToMethod(
38+
named("toRoute").and(takesArgument(0, named("sttp.tapir.server.ServerEndpoint"))),
39+
this.getClass().getName() + "$ApplyAdvice");
40+
}
41+
42+
public static class RouteWrapper implements Function1<RequestContext, Future<RouteResult>> {
43+
private final Function1<RequestContext, Future<RouteResult>> route;
44+
private final ServerEndpoint<?, ?> serverEndpoint;
45+
46+
public RouteWrapper(
47+
ServerEndpoint<?, ?> serverEndpoint, Function1<RequestContext, Future<RouteResult>> route) {
48+
this.route = route;
49+
this.serverEndpoint = serverEndpoint;
50+
}
51+
52+
public class Finalizer implements PartialFunction<Try<RouteResult>, Unit> {
53+
@Override
54+
public boolean isDefinedAt(Try<RouteResult> x) {
55+
return true;
56+
}
57+
58+
@Override
59+
public Unit apply(Try<RouteResult> tryResult) {
60+
if (tryResult.isSuccess()) {
61+
RouteResult result = tryResult.get();
62+
if (result.getClass() == RouteResult.Complete.class) {
63+
String path =
64+
serverEndpoint.showPathTemplate(
65+
(index, pc) ->
66+
pc.name().isDefined()
67+
? "{" + pc.name().get() + "}"
68+
: "{param" + index + "}",
69+
Option.apply(
70+
(Function2<Object, EndpointInput.Query<?>, String>)
71+
(index, q) -> q.name() + "={" + q.name() + "}"),
72+
false,
73+
"*",
74+
Option.apply("*"),
75+
Option.apply("*"));
76+
77+
PekkoRouteHolder.push(path);
78+
PekkoRouteHolder.endMatched();
79+
}
80+
}
81+
return null;
82+
}
83+
}
84+
85+
@Override
86+
public Future<RouteResult> apply(RequestContext ctx) {
87+
return route.apply(ctx).andThen(new Finalizer(), ctx.executionContext());
88+
}
89+
}
90+
91+
@SuppressWarnings("unused")
92+
public static class ApplyAdvice {
93+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
94+
public static void onExit(
95+
@Advice.Argument(0) ServerEndpoint<?, ?> endpoint,
96+
@Advice.Return(readOnly = false) Function1<RequestContext, Future<RouteResult>> route) {
97+
route = new RouteWrapper(endpoint, route);
98+
}
99+
}
100+
}

instrumentation/pekko/pekko-http-1.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/PekkoHttpServerRouteTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class PekkoHttpServerRouteTest {
9696
concat(makeRoute("test" / "3"), makeRoute("test" / "4"))
9797
)
9898

99-
test(routes, "/test/4", "GET")
99+
test(routes, "/test/4", "GET /test/4")
100100
}
101101

102102
def test(route: Route, path: String, spanName: String): Unit = {

0 commit comments

Comments
 (0)