Skip to content

Commit 18f27dc

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

File tree

5 files changed

+143
-3
lines changed

5 files changed

+143
-3
lines changed

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

+31-2
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.13:1.7.0")
23+
extraDependency("com.softwaremill.sttp.tapir:tapir-core_2.13: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_3:1.7.0")
32+
extraDependency("com.softwaremill.sttp.tapir:tapir-core_3:1.7.0")
33+
}
34+
pass {
35+
group.set("com.softwaremill.sttp.tapir")
36+
module.set("tapir-pekko-http-server_2.12")
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("com.softwaremill.sttp.tapir")
44+
module.set("tapir-pekko-http-server_2.13")
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("com.softwaremill.sttp.tapir")
52+
module.set("tapir-pekko-http-server_3")
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

+1
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,65 @@
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 org.apache.pekko.http.scaladsl.server.RequestContext;
9+
import org.apache.pekko.http.scaladsl.server.RouteResult;
10+
import scala.Function1;
11+
import scala.Function2;
12+
import scala.Option;
13+
import scala.PartialFunction;
14+
import scala.Unit;
15+
import scala.concurrent.Future;
16+
import scala.util.Try;
17+
import sttp.tapir.EndpointInput;
18+
import sttp.tapir.server.ServerEndpoint;
19+
20+
public class RouteWrapper implements Function1<RequestContext, Future<RouteResult>> {
21+
private final Function1<RequestContext, Future<RouteResult>> route;
22+
private final ServerEndpoint<?, ?> serverEndpoint;
23+
24+
public RouteWrapper(
25+
ServerEndpoint<?, ?> serverEndpoint, Function1<RequestContext, Future<RouteResult>> route) {
26+
this.route = route;
27+
this.serverEndpoint = serverEndpoint;
28+
}
29+
30+
public class Finalizer implements PartialFunction<Try<RouteResult>, Unit> {
31+
@Override
32+
public boolean isDefinedAt(Try<RouteResult> tryResult) {
33+
return tryResult.isSuccess();
34+
}
35+
36+
@Override
37+
public Unit apply(Try<RouteResult> tryResult) {
38+
if (tryResult.isSuccess()) {
39+
RouteResult result = tryResult.get();
40+
if (result.getClass() == RouteResult.Complete.class) {
41+
String path =
42+
serverEndpoint.showPathTemplate(
43+
(index, pc) ->
44+
pc.name().isDefined() ? "{" + pc.name().get() + "}" : "{param" + index + "}",
45+
Option.apply(
46+
(Function2<Object, EndpointInput.Query<?>, String>)
47+
(index, q) -> q.name() + "={" + q.name() + "}"),
48+
false,
49+
"*",
50+
Option.apply("*"),
51+
Option.apply("*"));
52+
53+
PekkoRouteHolder.push(path);
54+
PekkoRouteHolder.endMatched();
55+
}
56+
}
57+
return null;
58+
}
59+
}
60+
61+
@Override
62+
public Future<RouteResult> apply(RequestContext ctx) {
63+
return route.apply(ctx).andThen(new Finalizer(), ctx.executionContext());
64+
}
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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.concurrent.Future;
20+
import sttp.tapir.server.ServerEndpoint;
21+
22+
public class TapirPathInstrumentation implements TypeInstrumentation {
23+
24+
@Override
25+
public ElementMatcher<TypeDescription> typeMatcher() {
26+
return named("sttp.tapir.server.pekkohttp.PekkoHttpServerInterpreter");
27+
}
28+
29+
@Override
30+
public void transform(TypeTransformer transformer) {
31+
transformer.applyAdviceToMethod(
32+
named("toRoute").and(takesArgument(0, named("sttp.tapir.server.ServerEndpoint"))),
33+
this.getClass().getName() + "$ApplyAdvice");
34+
}
35+
36+
@SuppressWarnings("unused")
37+
public static class ApplyAdvice {
38+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
39+
public static void onExit(
40+
@Advice.Argument(0) ServerEndpoint<?, ?> endpoint,
41+
@Advice.Return(readOnly = false) Function1<RequestContext, Future<RouteResult>> route) {
42+
route = new RouteWrapper(endpoint, route);
43+
}
44+
}
45+
}

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

+1-1
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)