Put `http.route` attribute onto `http.server.duration` on Play framework request processing (#7801)
Basically, `akka-http` instrumenter has the responsibility to instrument
the `http.server.duration` for the Play framework application, but the
current implementation has not marked the `http.route` attribute.
ref:
8e8161cb2e/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerAttributesGetter.java (L59)
Actually, it's hard to record that attribute by only the akka-http layer
because that library's request object doesn't hold the route
information, e.g. placeholder.
So this patch delegates that job to the `play-mvc` instrumenter and when
that has been able to get the route info, the instrumenter puts
`http.route` attribute onto `http.server.duration`.
For example, when the routes configuration of the Play is like the
following:
```
GET /foo/:bar controllers.HomeController.doSomething(bar: String)
```
and when it tries to access that API, then OTEL instruments like so:
```prometheus
http_server_duration_count{otel_scope_name="io.opentelemetry.akka-http-10.0",otel_scope_version="1.23.0-alpha-SNAPSHOT",http_flavor="1.1",http_method="GET",http_route="/foo/$bar<[^/]+>",http_scheme="http",http_status_code="200",net_host_name="localhost",net_host_port="9000"} 1.0 1676078079798
http_server_duration_sum{otel_scope_name="io.opentelemetry.akka-http-10.0",otel_scope_version="1.23.0-alpha-SNAPSHOT",http_flavor="1.1",http_method="GET",http_route="/foo/$bar<[^/]+>",http_scheme="http",http_status_code="200",net_host_name="localhost",net_host_port="9000"} 12183.558843 1676078079798
http_server_duration_bucket{otel_scope_name="io.opentelemetry.akka-http-10.0",otel_scope_version="1.23.0-alpha-SNAPSHOT",http_flavor="1.1",http_method="GET",http_route="/foo/$bar<[^/]+>",http_scheme="http",http_status_code="200",net_host_name="localhost",net_host_port="9000",le="0.0"} 0.0 1676078079798
...
http_server_duration_bucket{otel_scope_name="io.opentelemetry.akka-http-10.0",otel_scope_version="1.23.0-alpha-SNAPSHOT",http_flavor="1.1",http_method="GET",http_route="/foo/$bar<[^/]+>",http_scheme="http",http_status_code="200",net_host_name="localhost",net_host_port="9000",le="+Inf"} 1.0 1676078079798
```
Rel: #1415
---------
Signed-off-by: moznion <moznion@mail.moznion.net>
This commit is contained in:
parent
d847bfcad5
commit
7e8d76a83b
|
@ -91,7 +91,7 @@ These are the supported libraries and frameworks:
|
|||
| [OkHttp](https://github.com/square/okhttp/) | 2.2+ | [opentelemetry-okhttp-3.0](../instrumentation/okhttp/okhttp-3.0/library) | [HTTP Client Spans], [HTTP Client Metrics] |
|
||||
| [Oracle UCP](https://docs.oracle.com/database/121/JJUCP/) | 11.2+ | [opentelemetry-oracle-ucp-11.2](../instrumentation/oracle-ucp-11.2/library) | [Database Pool Metrics] |
|
||||
| [OSHI](https://github.com/oshi/oshi/) | 5.3.1+ | [opentelemetry-oshi](../instrumentation/oshi/library) | [System Metrics] (partial support) |
|
||||
| [Play](https://github.com/playframework/playframework) | 2.4+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
|
||||
| [Play](https://github.com/playframework/playframework) | 2.4+ | N/A | [HTTP Client Spans], [HTTP Client Metrics], Provides `http.route` [2] |
|
||||
| [Play WS](https://github.com/playframework/play-ws) | 1.0+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
|
||||
| [Quartz](https://www.quartz-scheduler.org/) | 2.0+ | [opentelemetry-quartz-2.0](../instrumentation/quartz-2.0/library) | none |
|
||||
| [RabbitMQ Client](https://github.com/rabbitmq/rabbitmq-java-client) | 2.7+ | N/A | [Messaging Spans] |
|
||||
|
|
|
@ -9,7 +9,7 @@ import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentCo
|
|||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
||||
import static io.opentelemetry.javaagent.instrumentation.play.v2_4.Play24Singletons.instrumenter;
|
||||
import static io.opentelemetry.javaagent.instrumentation.play.v2_4.Play24Singletons.updateSpanNames;
|
||||
import static io.opentelemetry.javaagent.instrumentation.play.v2_4.Play24Singletons.updateSpan;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
@ -73,7 +73,7 @@ public class ActionInstrumentation implements TypeInstrumentation {
|
|||
@Advice.Local("otelScope") Scope scope) {
|
||||
scope.close();
|
||||
|
||||
updateSpanNames(context, req);
|
||||
updateSpan(context, req);
|
||||
// span finished in RequestCompleteCallback
|
||||
if (throwable == null) {
|
||||
responseFuture.onComplete(
|
||||
|
|
|
@ -9,7 +9,8 @@ import io.opentelemetry.api.GlobalOpenTelemetry;
|
|||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteHolder;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource;
|
||||
import play.api.mvc.Request;
|
||||
import scala.Option;
|
||||
|
||||
|
@ -25,18 +26,14 @@ public final class Play24Singletons {
|
|||
return INSTRUMENTER;
|
||||
}
|
||||
|
||||
public static void updateSpanNames(Context context, Request<?> request) {
|
||||
public static void updateSpan(Context context, Request<?> request) {
|
||||
String route = getRoute(request);
|
||||
if (route == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Span.fromContext(context).updateName(route);
|
||||
// set the span name on the upstream akka/netty span
|
||||
Span serverSpan = LocalRootSpan.fromContextOrNull(context);
|
||||
if (serverSpan != null) {
|
||||
serverSpan.updateName(route);
|
||||
}
|
||||
HttpRouteHolder.updateHttpRoute(context, HttpRouteSource.CONTROLLER, route);
|
||||
}
|
||||
|
||||
private static String getRoute(Request<?> request) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentCo
|
|||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
||||
import static io.opentelemetry.javaagent.instrumentation.play.v2_6.Play26Singletons.instrumenter;
|
||||
import static io.opentelemetry.javaagent.instrumentation.play.v2_6.Play26Singletons.updateSpanNames;
|
||||
import static io.opentelemetry.javaagent.instrumentation.play.v2_6.Play26Singletons.updateSpan;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
@ -73,7 +73,7 @@ public class ActionInstrumentation implements TypeInstrumentation {
|
|||
@Advice.Local("otelScope") Scope scope) {
|
||||
scope.close();
|
||||
|
||||
updateSpanNames(context, req);
|
||||
updateSpan(context, req);
|
||||
if (throwable == null) {
|
||||
// span is finished when future completes
|
||||
// not using responseFuture.onComplete() because that doesn't guarantee this handler span
|
||||
|
|
|
@ -9,7 +9,8 @@ import io.opentelemetry.api.GlobalOpenTelemetry;
|
|||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteHolder;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -52,18 +53,14 @@ public final class Play26Singletons {
|
|||
return INSTRUMENTER;
|
||||
}
|
||||
|
||||
public static void updateSpanNames(Context context, Request<?> request) {
|
||||
public static void updateSpan(Context context, Request<?> request) {
|
||||
String route = getRoute(request);
|
||||
if (route == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Span.fromContext(context).updateName(route);
|
||||
// set the span name on the upstream akka/netty span
|
||||
Span serverSpan = LocalRootSpan.fromContextOrNull(context);
|
||||
if (serverSpan != null) {
|
||||
serverSpan.updateName(route);
|
||||
}
|
||||
HttpRouteHolder.updateHttpRoute(context, HttpRouteSource.CONTROLLER, route);
|
||||
}
|
||||
|
||||
private static String getRoute(Request<?> request) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package io.opentelemetry.smoketest
|
||||
|
||||
import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||
import spock.lang.IgnoreIf
|
||||
|
||||
import java.time.Duration
|
||||
|
@ -37,6 +38,8 @@ class PlaySmokeTest extends SmokeTest {
|
|||
//One internal, one SERVER
|
||||
countSpansByName(traces, '/welcome') == 2
|
||||
|
||||
new TraceInspector(traces).countFilteredAttributes(SemanticAttributes.HTTP_ROUTE.key, "/welcome") == 1
|
||||
|
||||
cleanup:
|
||||
stopTarget()
|
||||
|
||||
|
|
Loading…
Reference in New Issue