Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better server span name for Spring MVC, Grails, Wicket and Struts #2814

Merged
merged 23 commits into from
Apr 22, 2021
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.api.servlet;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import java.util.function.Supplier;

/** Helper container for tracking whether instrumentation should update server span name or not. */
public final class ServerSpanNaming {

private static final ContextKey<ServerSpanNaming> CONTEXT_KEY =
ContextKey.named("opentelemetry-servlet-span-naming-key");

public static Context init(Context context, Source initialSource) {
if (context.get(CONTEXT_KEY) != null) {
return context;
}
return context.with(CONTEXT_KEY, new ServerSpanNaming(initialSource));
}

private volatile Source updatedBySource;

private ServerSpanNaming(Source initialSource) {
this.updatedBySource = initialSource;
}

/**
* If there is a server span in the context, and {@link #init(Context, Source)} has been called to
* populate a {@code ServerSpanName} into the context, then this method will update the server
* span name using the provided {@link Supplier} if and only if the last {@link Source} to update
* the span name using this method has strictly lower priority than the provided {@link Source}.
*
* <p>If there is a server span in the context, and {@link #init(Context, Source)} has NOT been
* called to populate a {@code ServerSpanName} into the context, then this method will update the
* server span name using the provided {@link Supplier}.
*/
public static void updateServerSpanName(
Context context, Source source, Supplier<String> serverSpanName) {
Span serverSpan = ServerSpan.fromContextOrNull(context);
if (serverSpan == null) {
return;
}
ServerSpanNaming serverSpanNaming = context.get(CONTEXT_KEY);
if (serverSpanNaming == null) {
serverSpan.updateName(serverSpanName.get());
return;
}
if (source.order > serverSpanNaming.updatedBySource.order) {
serverSpan.updateName(serverSpanName.get());
serverSpanNaming.updatedBySource = source;
}
}

public enum Source {
CONTAINER(1),
SERVLET(2),
CONTROLLER(3);

private final int order;

Source(int order) {
this.order = order;
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,11 @@ public Context startSpan(
onConnectionAndRequest(spanBuilder, connection, request);

Context context = withServerSpan(parentContext, spanBuilder.startSpan());
context = customizeContext(context, request);
attachServerContext(context, storage);

return context;
}

/** Override in subclass to customize context that is returned by {@code startSpan}. */
protected Context customizeContext(Context context, REQUEST request) {
trask marked this conversation as resolved.
Show resolved Hide resolved
return context;
}

/**
* Convenience method. Delegates to {@link #end(Context, Object, long)}, passing {@code timestamp}
* value of {@code -1}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ abstract class AbstractArmeriaHttpServerTest extends HttpServerTest<Server> {
]
}

@Override
boolean testNotFound() {
// currently span name is /notFound which indicates it won't be low-cardinality
false
}

@Override
Server startServer(int port) {
ServerBuilder sb = Server.builder()
Expand All @@ -61,31 +67,31 @@ abstract class AbstractArmeriaHttpServerTest extends HttpServerTest<Server> {
}
}

sb.service(REDIRECT.path) {ctx, req ->
sb.service(REDIRECT.path) { ctx, req ->
controller(REDIRECT) {
HttpResponse.of(ResponseHeaders.of(HttpStatus.valueOf(REDIRECT.status), HttpHeaderNames.LOCATION, REDIRECT.body))
}
}

sb.service(ERROR.path) {ctx, req ->
sb.service(ERROR.path) { ctx, req ->
controller(ERROR) {
HttpResponse.of(HttpStatus.valueOf(ERROR.status), MediaType.PLAIN_TEXT_UTF_8, ERROR.body)
}
}

sb.service(EXCEPTION.path) {ctx, req ->
sb.service(EXCEPTION.path) { ctx, req ->
controller(EXCEPTION) {
throw new Exception(EXCEPTION.body)
}
}

sb.service("/query") {ctx, req ->
sb.service("/query") { ctx, req ->
controller(QUERY_PARAM) {
HttpResponse.of(HttpStatus.valueOf(QUERY_PARAM.status), MediaType.PLAIN_TEXT_UTF_8, "some=${QueryParams.fromQueryString(ctx.query()).get("some")}")
}
}

sb.service("/path/:id/param") {ctx, req ->
sb.service("/path/:id/param") { ctx, req ->
controller(PATH_PARAM) {
HttpResponse.of(HttpStatus.valueOf(PATH_PARAM.status), MediaType.PLAIN_TEXT_UTF_8, ctx.pathParam("id"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

package io.opentelemetry.javaagent.instrumentation.grails;

import io.opentelemetry.api.trace.Span;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTROLLER;

import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import org.grails.web.mapping.mvc.GrailsControllerUrlMappingInfo;
Expand All @@ -23,14 +25,17 @@ public Context startSpan(Object controller, String action) {
return startSpan(spanNameForClass(controller.getClass()) + "." + action);
}

public void nameServerSpan(
Context context, Span serverSpan, GrailsControllerUrlMappingInfo info) {
String action =
info.getActionName() != null
? info.getActionName()
: info.getControllerClass().getDefaultAction();
serverSpan.updateName(
ServletContextPath.prepend(context, "/" + info.getControllerName() + "/" + action));
public void updateServerSpanName(Context context, GrailsControllerUrlMappingInfo info) {
ServerSpanNaming.updateServerSpanName(
context,
CONTROLLER,
() -> {
String action =
info.getActionName() != null
? info.getActionName()
: info.getControllerClass().getDefaultAction();
return ServletContextPath.prepend(context, "/" + info.getControllerName() + "/" + action);
trask marked this conversation as resolved.
Show resolved Hide resolved
});
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map;
Expand Down Expand Up @@ -48,11 +46,7 @@ public static void nameSpan(@Advice.Argument(2) Object handler) {

if (handler instanceof GrailsControllerUrlMappingInfo) {
Context parentContext = Java8BytecodeBridge.currentContext();
Span serverSpan = ServerSpan.fromContextOrNull(parentContext);
if (serverSpan != null) {
tracer()
.nameServerSpan(parentContext, serverSpan, (GrailsControllerUrlMappingInfo) handler);
}
tracer().updateServerSpanName(parentContext, (GrailsControllerUrlMappingInfo) handler);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ class GrailsTest extends HttpServerTest<ConfigurableApplicationContext> implemen
return getContextPath() + "/test/path"
} else if (endpoint == QUERY_PARAM) {
return getContextPath() + "/test/query"
} else if (endpoint == ERROR || endpoint == EXCEPTION) {
return getContextPath() + "/error/index"
} else if (endpoint == ERROR) {
return getContextPath() + "/test/error"
} else if (endpoint == NOT_FOUND) {
return getContextPath() + "/**"
}
Expand Down Expand Up @@ -103,7 +103,7 @@ class GrailsTest extends HttpServerTest<ConfigurableApplicationContext> implemen

@Override
int getErrorPageSpansCount(ServerEndpoint endpoint) {
endpoint == NOT_FOUND ? 1 : 2
2
}

@Override
Expand All @@ -114,12 +114,11 @@ class GrailsTest extends HttpServerTest<ConfigurableApplicationContext> implemen
@Override
void errorPageSpans(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint) {
forwardSpan(trace, index, trace.span(0))
if (endpoint != NOT_FOUND) {
trace.span(index + 1) {
name "ErrorController.index"
kind INTERNAL
attributes {
}
def errorSpanName = endpoint == NOT_FOUND ? "BasicErrorController.error" : "ErrorController.index"
trace.span(index + 1) {
name errorSpanName
kind INTERNAL
attributes {
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ package test

class UrlMappings {

static mappings = {
"/success"(controller: 'test', action: 'success')
"/query"(controller: 'test', action: 'query')
"/redirect"(controller: 'test', action: 'redirect')
"/error-status"(controller: 'test', action: 'error')
"/exception"(controller: 'test', action: 'exception')
"/path/$id/param"(controller: 'test', action: 'path')
static mappings = {
"/success"(controller: 'test', action: 'success')
"/query"(controller: 'test', action: 'query')
"/redirect"(controller: 'test', action: 'redirect')
"/error-status"(controller: 'test', action: 'error')
"/exception"(controller: 'test', action: 'exception')
"/path/$id/param"(controller: 'test', action: 'path')

"500"(controller: 'error')
"404"(controller: 'error', action: 'notFound')
}
"500"(controller: 'error')
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,6 @@ class GrizzlyFilterchainServerTest extends HttpServerTest<HttpServer> implements
break
case "/exception":
throw new Exception(EXCEPTION.body)
case "/notFound":
endpoint = NOT_FOUND
break
case "/query?some=query":
endpoint = QUERY_PARAM
break
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,7 @@ public static Jetty11HttpServerTracer tracer() {
}

public Context startServerSpan(HttpServletRequest request) {
return startSpan(request, "HTTP " + request.getMethod());
}

@Override
protected Context customizeContext(Context context, HttpServletRequest request) {
context = super.customizeContext(context, request);
Context context = startSpan(request, "HTTP " + request.getMethod(), false);
return AppServerBridge.init(context, false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,7 @@ public static Jetty8HttpServerTracer tracer() {
}

public Context startServerSpan(HttpServletRequest request) {
return startSpan(request, "HTTP " + request.getMethod());
}

@Override
protected Context customizeContext(Context context, HttpServletRequest request) {
context = super.customizeContext(context, request);
Context context = startSpan(request, "HTTP " + request.getMethod(), false);
return AppServerBridge.init(context, false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public static LibertyHttpServerTracer tracer() {
}

public Context startSpan(HttpServletRequest request) {
return startSpan(request, "HTTP " + request.getMethod());
return startSpan(request, "HTTP " + request.getMethod(), false);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,4 @@ class GlassFishServerTest extends HttpServerTest<GlassFish> implements AgentTest
break
}
}

@Override
String expectedServerSpanName(ServerEndpoint endpoint) {
if (endpoint == NOT_FOUND) {
return getContextPath() + "/*"
}
return super.expectedServerSpanName(endpoint)
}
}
Loading