diff --git a/.fossa.yml b/.fossa.yml index a3c3a25af675..8c1c7eb10847 100644 --- a/.fossa.yml +++ b/.fossa.yml @@ -925,6 +925,9 @@ targets: - type: gradle path: ./ target: ':instrumentation:servlet:servlet-3.0:javaagent' + - type: gradle + path: ./ + target: ':instrumentation:servlet:servlet-3.0:library' - type: gradle path: ./ target: ':instrumentation:servlet:servlet-5.0:javaagent' diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Advice.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Advice.java index 874bca299cb0..687fdf2726a7 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Advice.java +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Advice.java @@ -6,18 +6,11 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v3_0; import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.getSnippetInjectionHelper; -import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.helper; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.bootstrap.CallDepth; -import io.opentelemetry.javaagent.bootstrap.http.HttpServerResponseCustomizerHolder; import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge; -import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver; -import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext; import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.Servlet3SnippetInjectingResponseWrapper; import javax.annotation.Nullable; -import javax.servlet.Servlet; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; @@ -30,67 +23,6 @@ @SuppressWarnings("unused") public class Servlet3Advice { - public static class AdviceScope { - private final CallDepth callDepth; - private final ServletRequestContext requestContext; - private final Context context; - private final Scope scope; - - public AdviceScope( - CallDepth callDepth, - HttpServletRequest request, - HttpServletResponse response, - Object servletOrFilter) { - this.callDepth = callDepth; - this.callDepth.getAndIncrement(); - - Context currentContext = Context.current(); - Context attachedContext = helper().getServerContext(request); - Context contextToUpdate; - - requestContext = new ServletRequestContext<>(request, servletOrFilter); - if (attachedContext == null && helper().shouldStart(currentContext, requestContext)) { - context = helper().start(currentContext, requestContext); - helper().setAsyncListenerResponse(context, response); - - contextToUpdate = context; - } else if (attachedContext != null - && helper().needsRescoping(currentContext, attachedContext)) { - // Given request already has a context associated with it. - // see the needsRescoping() javadoc for more explanation - contextToUpdate = attachedContext; - context = null; - } else { - // We are inside nested servlet/filter/app-server span, don't create new span - contextToUpdate = currentContext; - context = null; - } - - // Update context with info from current request to ensure that server span gets the best - // possible name. - // In case server span was created by app server instrumentations calling updateContext - // returns a new context that contains servlet context path that is used in other - // instrumentations for naming server span. - MappingResolver mappingResolver = Servlet3Singletons.getMappingResolver(servletOrFilter); - boolean servlet = servletOrFilter instanceof Servlet; - contextToUpdate = helper().updateContext(contextToUpdate, request, mappingResolver, servlet); - scope = contextToUpdate.makeCurrent(); - - if (context != null) { - // Only trigger response customizer once, so only if server span was created here - HttpServerResponseCustomizerHolder.getCustomizer() - .customize(contextToUpdate, response, Servlet3Accessor.INSTANCE); - } - } - - public void exit( - @Nullable Throwable throwable, HttpServletRequest request, HttpServletResponse response) { - - boolean topLevel = callDepth.decrementAndGet() == 0; - helper().end(requestContext, request, response, throwable, topLevel, context, scope); - } - } - @AssignReturned.ToArguments({ @ToArgument(value = 0, index = 1), @ToArgument(value = 1, index = 2) @@ -114,8 +46,8 @@ public static Object[] onEnter( response = new Servlet3SnippetInjectingResponseWrapper((HttpServletResponse) response, snippet); } - AdviceScope adviceScope = - new AdviceScope( + Servlet3RequestAdviceScope adviceScope = + new Servlet3RequestAdviceScope( CallDepth.forClass(AppServerBridge.getCallDepthKey()), (HttpServletRequest) request, (HttpServletResponse) response, @@ -129,7 +61,7 @@ public static void stopSpan( @Advice.Argument(1) ServletResponse response, @Advice.Thrown @Nullable Throwable throwable, @Advice.Enter Object[] enterResult) { - AdviceScope adviceScope = (AdviceScope) enterResult[0]; + Servlet3RequestAdviceScope adviceScope = (Servlet3RequestAdviceScope) enterResult[0]; if (adviceScope == null || !(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3FilterInitAdvice.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3FilterInitAdvice.java index 5bddc729c9e1..3343b4238a5b 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3FilterInitAdvice.java +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3FilterInitAdvice.java @@ -5,7 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v3_0; -import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.FILTER_MAPPING_RESOLVER_FACTORY; +import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.FILTER_MAPPING_RESOLVER; import javax.servlet.Filter; import javax.servlet.FilterConfig; @@ -20,7 +20,6 @@ public static void filterInit( if (filterConfig == null) { return; } - FILTER_MAPPING_RESOLVER_FACTORY.set( - filter, new Servlet3FilterMappingResolverFactory(filterConfig)); + FILTER_MAPPING_RESOLVER.set(filter, new Servlet3FilterMappingResolverFactory(filterConfig)); } } diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3InitAdvice.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3InitAdvice.java index 9c105d27464a..57fa29af91ba 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3InitAdvice.java +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3InitAdvice.java @@ -5,7 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v3_0; -import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.SERVLET_MAPPING_RESOLVER_FACTORY; +import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.SERVLET_MAPPING_RESOLVER; import javax.servlet.Servlet; import javax.servlet.ServletConfig; @@ -20,7 +20,6 @@ public static void servletInit( if (servletConfig == null) { return; } - SERVLET_MAPPING_RESOLVER_FACTORY.set( - servlet, new Servlet3MappingResolverFactory(servletConfig)); + SERVLET_MAPPING_RESOLVER.set(servlet, new Servlet3MappingResolverFactory(servletConfig)); } } diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3RequestAdviceScope.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3RequestAdviceScope.java new file mode 100644 index 000000000000..c50beae8ec63 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3RequestAdviceScope.java @@ -0,0 +1,80 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v3_0; + +import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.helper; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.bootstrap.CallDepth; +import io.opentelemetry.javaagent.bootstrap.http.HttpServerResponseCustomizerHolder; +import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver; +import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext; +import javax.annotation.Nullable; +import javax.servlet.Servlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class Servlet3RequestAdviceScope { + private final CallDepth callDepth; + private final ServletRequestContext requestContext; + private final Context context; + private final Scope scope; + + public Servlet3RequestAdviceScope( + CallDepth callDepth, + HttpServletRequest request, + HttpServletResponse response, + Object servletOrFilter) { + this.callDepth = callDepth; + this.callDepth.getAndIncrement(); + + Context currentContext = Context.current(); + Context attachedContext = helper().getServerContext(request); + Context contextToUpdate; + + requestContext = new ServletRequestContext<>(request, servletOrFilter); + if (attachedContext == null && helper().shouldStart(currentContext, requestContext)) { + context = helper().start(currentContext, requestContext); + helper().setAsyncListenerResponse(context, response); + + contextToUpdate = context; + } else if (attachedContext != null + && helper().needsRescoping(currentContext, attachedContext)) { + // Given request already has a context associated with it. + // see the needsRescoping() javadoc for more explanation + contextToUpdate = attachedContext; + context = null; + } else { + // We are inside nested servlet/filter/app-server span, don't create new span + contextToUpdate = currentContext; + context = null; + } + + // Update context with info from current request to ensure that server span gets the best + // possible name. + // In case server span was created by app server instrumentations calling updateContext + // returns a new context that contains servlet context path that is used in other + // instrumentations for naming server span. + MappingResolver mappingResolver = Servlet3Singletons.getMappingResolver(servletOrFilter); + boolean servlet = servletOrFilter instanceof Servlet; + contextToUpdate = helper().updateContext(contextToUpdate, request, mappingResolver, servlet); + scope = contextToUpdate.makeCurrent(); + + if (context != null) { + // Only trigger response customizer once, so only if server span was created here + HttpServerResponseCustomizerHolder.getCustomizer() + .customize(contextToUpdate, response, Servlet3Accessor.INSTANCE); + } + } + + public void exit( + @Nullable Throwable throwable, HttpServletRequest request, HttpServletResponse response) { + + boolean topLevel = callDepth.decrementAndGet() == 0; + helper().end(requestContext, request, response, throwable, topLevel, context, scope); + } +} diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseAdviceScope.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseAdviceScope.java new file mode 100644 index 000000000000..f5d893e16733 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseAdviceScope.java @@ -0,0 +1,53 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v3_0; + +import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.responseInstrumenter; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod; +import io.opentelemetry.javaagent.bootstrap.CallDepth; +import io.opentelemetry.javaagent.instrumentation.servlet.common.response.HttpServletResponseAdviceHelper; +import javax.annotation.Nullable; + +public class Servlet3ResponseAdviceScope { + private final CallDepth callDepth; + private final ClassAndMethod classAndMethod; + private final Context context; + private final Scope scope; + + public Servlet3ResponseAdviceScope( + CallDepth callDepth, Class declaringClass, String methodName) { + this.callDepth = callDepth; + if (callDepth.getAndIncrement() > 0) { + this.classAndMethod = null; + this.context = null; + this.scope = null; + return; + } + HttpServletResponseAdviceHelper.StartResult result = + HttpServletResponseAdviceHelper.startSpan( + responseInstrumenter(), declaringClass, methodName); + if (result != null) { + classAndMethod = result.getClassAndMethod(); + context = result.getContext(); + scope = result.getScope(); + } else { + classAndMethod = null; + context = null; + scope = null; + } + } + + public void exit(@Nullable Throwable throwable) { + if (callDepth.decrementAndGet() > 0) { + return; + } + HttpServletResponseAdviceHelper.stopSpan( + responseInstrumenter(), throwable, context, scope, classAndMethod); + } +} diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseSendAdvice.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseSendAdvice.java index 4e2441f4b4d0..ebb7921320dc 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseSendAdvice.java +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseSendAdvice.java @@ -5,67 +5,23 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v3_0; -import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.responseInstrumenter; - -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod; import io.opentelemetry.javaagent.bootstrap.CallDepth; -import io.opentelemetry.javaagent.instrumentation.servlet.common.response.HttpServletResponseAdviceHelper; -import javax.annotation.Nullable; import javax.servlet.http.HttpServletResponse; import net.bytebuddy.asm.Advice; @SuppressWarnings("unused") public class Servlet3ResponseSendAdvice { - public static class AdviceScope { - private final CallDepth callDepth; - private final ClassAndMethod classAndMethod; - private final Context context; - private final Scope scope; - - public AdviceScope(CallDepth callDepth, Class declaringClass, String methodName) { - this.callDepth = callDepth; - if (callDepth.getAndIncrement() > 0) { - this.classAndMethod = null; - this.context = null; - this.scope = null; - return; - } - HttpServletResponseAdviceHelper.StartResult result = - HttpServletResponseAdviceHelper.startSpan( - responseInstrumenter(), declaringClass, methodName); - if (result != null) { - classAndMethod = result.getClassAndMethod(); - context = result.getContext(); - scope = result.getScope(); - } else { - classAndMethod = null; - context = null; - scope = null; - } - } - - public void exit(@Nullable Throwable throwable) { - if (callDepth.decrementAndGet() > 0) { - return; - } - HttpServletResponseAdviceHelper.stopSpan( - responseInstrumenter(), throwable, context, scope, classAndMethod); - } - } - @Advice.OnMethodEnter(suppress = Throwable.class) - public static AdviceScope start( + public static Servlet3ResponseAdviceScope start( @Advice.Origin("#t") Class declaringClass, @Advice.Origin("#m") String methodName) { - return new AdviceScope( + return new Servlet3ResponseAdviceScope( CallDepth.forClass(HttpServletResponse.class), declaringClass, methodName); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Thrown Throwable throwable, @Advice.Enter AdviceScope adviceScope) { + @Advice.Thrown Throwable throwable, @Advice.Enter Servlet3ResponseAdviceScope adviceScope) { adviceScope.exit(throwable); } } diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Singletons.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Singletons.java index 17c7082b6f52..73c3228d149d 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Singletons.java +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Singletons.java @@ -24,14 +24,6 @@ public final class Servlet3Singletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.servlet-3.0"; - public static final VirtualField - SERVLET_MAPPING_RESOLVER_FACTORY = - VirtualField.find(Servlet.class, MappingResolver.Factory.class); - - public static final VirtualField - FILTER_MAPPING_RESOLVER_FACTORY = - VirtualField.find(Filter.class, MappingResolver.Factory.class); - private static final Instrumenter< ServletRequestContext, ServletResponseContext> INSTRUMENTER = @@ -41,9 +33,9 @@ public final class Servlet3Singletons { private static final ServletHelper HELPER = new ServletHelper<>(INSTRUMENTER, Servlet3Accessor.INSTANCE); - private static final VirtualField SERVLET_MAPPING_RESOLVER = + public static final VirtualField SERVLET_MAPPING_RESOLVER = VirtualField.find(Servlet.class, MappingResolver.Factory.class); - private static final VirtualField FILTER_MAPPING_RESOLVER = + public static final VirtualField FILTER_MAPPING_RESOLVER = VirtualField.find(Filter.class, MappingResolver.Factory.class); private static final Instrumenter RESPONSE_INSTRUMENTER = diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/build.gradle.kts b/instrumentation/servlet/servlet-3.0/library-unit-tests/build.gradle.kts new file mode 100644 index 000000000000..2f746f99681c --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + id("otel.library-instrumentation") +} + +// These tests are split out so we can unit test against the actual shadowed jar. + +dependencies { + testImplementation(project(":instrumentation:servlet:servlet-3.0:library", configuration = "shadow")) + testImplementation(project(":instrumentation:servlet:servlet-3.0:testing")) + + testLibrary("org.eclipse.jetty:jetty-server:8.0.0.v20110901") + testLibrary("org.eclipse.jetty:jetty-servlet:8.0.0.v20110901") + testLibrary("org.apache.tomcat.embed:tomcat-embed-core:8.0.41") + testLibrary("org.apache.tomcat.embed:tomcat-embed-jasper:8.0.41") + + latestDepTestLibrary("org.eclipse.jetty:jetty-server:10.+") // see servlet-5.0 module + latestDepTestLibrary("org.eclipse.jetty:jetty-servlet:10.+") // see servlet-5.0 module + + latestDepTestLibrary("org.apache.tomcat.embed:tomcat-embed-core:9.+") // see servlet-5.0 module + latestDepTestLibrary("org.apache.tomcat.embed:tomcat-embed-jasper:9.+") // see servlet-5.0 module +} + +tasks { + withType().configureEach { + // required on jdk17+ to allow tomcat to shutdown properly. + jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + } +} + +// Servlet 3.0 in latest Jetty versions requires Java 11 +// However, projects that depend on this module are still be using Java 8 in testLatestDeps mode +// Therefore, we need a separate project for servlet 3.0 tests +val latestDepTest = findProperty("testLatestDeps") as Boolean + +if (latestDepTest) { + otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_11) + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3AsyncLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3AsyncLibraryTest.java new file mode 100644 index 000000000000..bc205d55d127 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3AsyncLibraryTest.java @@ -0,0 +1,63 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.jetty; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; +import io.opentelemetry.testing.internal.armeria.common.HttpMethod; +import javax.servlet.Servlet; +import org.junit.jupiter.api.Test; + +class JettyServlet3AsyncLibraryTest extends JettyServlet3LibraryTest { + + @Override + public Class servlet() { + return TestServlet3.Async.class; + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } + + @Override + public boolean isAsyncTest() { + return true; + } + + @Test + void startAsyncInSpan() { + AggregatedHttpRequest request = + AggregatedHttpRequest.of( + HttpMethod.GET, resolveAddress(SUCCESS, "h1c://") + "?startAsyncInSpan=true"); + AggregatedHttpResponse response = client.execute(request).aggregate().join(); + + assertThat(response.status().code()).isEqualTo(SUCCESS.getStatus()); + assertThat(response.contentUtf8()).isEqualTo(SUCCESS.getBody()); + + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET " + getContextPath() + "/*") + .hasKind(SpanKind.SERVER) + .hasNoParent(), + span -> + span.hasName("startAsync") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("controller") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)))); + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3FakeAsyncLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3FakeAsyncLibraryTest.java new file mode 100644 index 000000000000..027b280f575f --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3FakeAsyncLibraryTest.java @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.jetty; + +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; + +class JettyServlet3FakeAsyncLibraryTest extends JettyServlet3LibraryTest { + @Override + public Class servlet() { + return TestServlet3.FakeAsync.class; + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3LibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3LibraryTest.java new file mode 100644 index 000000000000..5a55ac802858 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3LibraryTest.java @@ -0,0 +1,66 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.jetty; + +import io.opentelemetry.instrumentation.api.internal.HttpConstants; +import io.opentelemetry.instrumentation.servlet.v3_0.OpenTelemetryServletFilter; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.jetty.AbstractJettyServlet3Test; +import java.util.EnumSet; +import javax.servlet.DispatcherType; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.junit.jupiter.api.extension.RegisterExtension; + +public abstract class JettyServlet3LibraryTest extends AbstractJettyServlet3Test { + @RegisterExtension + protected static final InstrumentationExtension testing = + HttpServerInstrumentationExtension.forLibrary(); + + @Override + protected void addFilter(ServletContextHandler servletContext) { + servletContext.addFilter( + OpenTelemetryServletFilter.class, "/*", EnumSet.allOf(DispatcherType.class)); + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestCaptureRequestParameters(false); // Requires AgentConfig. + options.setTestCaptureHttpHeaders(false); // Requires AgentConfig. + options.disableTestNonStandardHttpMethod(); // test doesn't use route mapping correctly. + options.setTestException(false); // filters don't have visibility into exception handling above. + options.setHasResponseCustomizer(e -> false); + } + + @Override + public String expectedHttpRoute(ServerEndpoint endpoint, String method) { + // no need to compute route if we're not expecting it + if (!hasHttpRouteAttribute(endpoint)) { + return null; + } + + if (method.equals(HttpConstants._OTHER)) { + return getContextPath() + endpoint.getPath(); + } + + // NOTE: Primary difference from javaagent servlet instrumentation! + // Since just we're working with a filter, we can't actually get the proper servlet path. + return getContextPath() + "/*"; + } + + @Override + protected void snippetInjectionWithServletOutputStream() { + // override + } + + @Override + protected void snippetInjectionWithPrintWriter() { + // override + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3SyncLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3SyncLibraryTest.java new file mode 100644 index 000000000000..1e6a02074b0c --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/JettyServlet3SyncLibraryTest.java @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.jetty; + +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; + +class JettyServlet3SyncLibraryTest extends JettyServlet3LibraryTest { + @Override + public Class servlet() { + return TestServlet3.Sync.class; + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyDispatchLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyDispatchLibraryTest.java new file mode 100644 index 000000000000..a95a8ebb63a7 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyDispatchLibraryTest.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.jetty.dispatch; + +import io.opentelemetry.instrumentation.servlet.v3_0.jetty.JettyServlet3LibraryTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; + +abstract class JettyDispatchLibraryTest extends JettyServlet3LibraryTest { + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setContextPath(getContextPath() + "/dispatch"); + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3DispatchAsyncLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3DispatchAsyncLibraryTest.java new file mode 100644 index 000000000000..a1b491161069 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3DispatchAsyncLibraryTest.java @@ -0,0 +1,59 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.jetty.dispatch; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_PARAMETERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; +import org.eclipse.jetty.servlet.ServletContextHandler; + +class JettyServlet3DispatchAsyncLibraryTest extends JettyDispatchLibraryTest { + @Override + public Class servlet() { + return TestServlet3.Async.class; + } + + @Override + public boolean isAsyncTest() { + return true; + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), TestServlet3.DispatchAsync.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + SUCCESS.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + QUERY_PARAM.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + ERROR.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + AUTH_REQUIRED.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + CAPTURE_HEADERS.getPath(), TestServlet3.DispatchAsync.class); + addServlet( + context, "/dispatch" + CAPTURE_PARAMETERS.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + INDEXED_CHILD.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch/recursive", TestServlet3.DispatchRecursive.class); + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3DispatchImmediateLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3DispatchImmediateLibraryTest.java new file mode 100644 index 000000000000..913de2849054 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3DispatchImmediateLibraryTest.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.jetty.dispatch; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_PARAMETERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; +import org.eclipse.jetty.servlet.ServletContextHandler; + +class JettyServlet3DispatchImmediateLibraryTest extends JettyDispatchLibraryTest { + @Override + public Class servlet() { + return TestServlet3.Async.class; + } + + @Override + public boolean isAsyncTest() { + return true; + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), TestServlet3.DispatchImmediate.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + TestServlet3.DispatchImmediate.class); + addServlet(context, "/dispatch" + SUCCESS.getPath(), TestServlet3.DispatchImmediate.class); + addServlet(context, "/dispatch" + QUERY_PARAM.getPath(), TestServlet3.DispatchImmediate.class); + addServlet(context, "/dispatch" + ERROR.getPath(), TestServlet3.DispatchImmediate.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), TestServlet3.DispatchImmediate.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), TestServlet3.DispatchImmediate.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), TestServlet3.DispatchImmediate.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), TestServlet3.DispatchImmediate.class); + addServlet( + context, "/dispatch" + CAPTURE_PARAMETERS.getPath(), TestServlet3.DispatchImmediate.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), TestServlet3.DispatchImmediate.class); + addServlet(context, "/dispatch/recursive", TestServlet3.DispatchRecursive.class); + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3ForwardLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3ForwardLibraryTest.java new file mode 100644 index 000000000000..5344501c0541 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3ForwardLibraryTest.java @@ -0,0 +1,56 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.jetty.dispatch; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_PARAMETERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.RequestDispatcherServlet; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; +import org.eclipse.jetty.servlet.ServletContextHandler; + +class JettyServlet3ForwardLibraryTest extends JettyDispatchLibraryTest { + @Override + public Class servlet() { + return TestServlet3.Sync.class; // dispatch to sync servlet + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + QUERY_PARAM.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + ERROR.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, + "/dispatch" + CAPTURE_PARAMETERS.getPath(), + RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), RequestDispatcherServlet.Forward.class); + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3IncludeLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3IncludeLibraryTest.java new file mode 100644 index 000000000000..873f62fd77cc --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/jetty/dispatch/JettyServlet3IncludeLibraryTest.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.jetty.dispatch; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_PARAMETERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.RequestDispatcherServlet; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; +import org.eclipse.jetty.servlet.ServletContextHandler; + +class JettyServlet3IncludeLibraryTest extends JettyDispatchLibraryTest { + @Override + public Class servlet() { + return TestServlet3.Sync.class; // dispatch to sync servlet + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestRedirect(false); + options.setTestCaptureHttpHeaders(false); + options.setTestError(false); + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + QUERY_PARAM.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + ERROR.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, + "/dispatch" + CAPTURE_PARAMETERS.getPath(), + RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), RequestDispatcherServlet.Include.class); + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3AsyncLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3AsyncLibraryTest.java new file mode 100644 index 000000000000..55e51cdecf7f --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3AsyncLibraryTest.java @@ -0,0 +1,58 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.tomcat; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; +import io.opentelemetry.testing.internal.armeria.common.HttpMethod; +import javax.servlet.Servlet; +import org.junit.jupiter.api.Test; + +class TomcatServlet3AsyncLibraryTest extends TomcatServlet3LibraryTest { + + @Override + public Class servlet() { + return TestServlet3.Async.class; + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } + + @Test + void startAsyncInSpan() { + AggregatedHttpRequest request = + AggregatedHttpRequest.of( + HttpMethod.GET, resolveAddress(SUCCESS, "h1c://") + "?startAsyncInSpan=true"); + AggregatedHttpResponse response = client.execute(request).aggregate().join(); + + assertThat(response.status().code()).isEqualTo(SUCCESS.getStatus()); + assertThat(response.contentUtf8()).isEqualTo(SUCCESS.getBody()); + + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET " + getContextPath() + "/*") + .hasKind(SpanKind.SERVER) + .hasNoParent(), + span -> + span.hasName("startAsync") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("controller") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)))); + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3FakeAsyncLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3FakeAsyncLibraryTest.java new file mode 100644 index 000000000000..dd08d1119037 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3FakeAsyncLibraryTest.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.tomcat; + +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; + +class TomcatServlet3FakeAsyncLibraryTest extends TomcatServlet3LibraryTest { + + @Override + public Class servlet() { + return TestServlet3.FakeAsync.class; + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3LibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3LibraryTest.java new file mode 100644 index 000000000000..11a5dcc28bce --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3LibraryTest.java @@ -0,0 +1,83 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.tomcat; + +import io.opentelemetry.instrumentation.api.internal.HttpConstants; +import io.opentelemetry.instrumentation.servlet.v3_0.OpenTelemetryServletFilter; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.tomcat.AbstractTomcatServlet3Test; +import org.apache.catalina.Container; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.core.StandardEngine; +import org.apache.catalina.startup.Tomcat; +import org.apache.tomcat.util.descriptor.web.FilterDef; +import org.apache.tomcat.util.descriptor.web.FilterMap; +import org.junit.jupiter.api.extension.RegisterExtension; + +public abstract class TomcatServlet3LibraryTest extends AbstractTomcatServlet3Test { + + @RegisterExtension + protected static final InstrumentationExtension testing = + HttpServerInstrumentationExtension.forLibrary(); + + @SuppressWarnings("deprecation") // needed API also on Engine. + @Override + protected void beforeStart(Tomcat tomcatServer) { + StandardEngine engine = + (StandardEngine) tomcatServer.getServer().findService("Tomcat").getContainer(); + Container container = engine.findChild(engine.getDefaultHost()); + StandardContext context = (StandardContext) container.findChild(getContextPath()); + + FilterDef filter1definition = new FilterDef(); + filter1definition.setFilterName(OpenTelemetryServletFilter.class.getSimpleName()); + filter1definition.setFilterClass(OpenTelemetryServletFilter.class.getName()); + context.addFilterDef(filter1definition); + + FilterMap filter1mapping = new FilterMap(); + filter1mapping.setFilterName(OpenTelemetryServletFilter.class.getSimpleName()); + filter1mapping.addURLPattern("/*"); + context.addFilterMap(filter1mapping); + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestCaptureRequestParameters(false); // Requires AgentConfig. + options.setTestCaptureHttpHeaders(false); // Requires AgentConfig. + options.disableTestNonStandardHttpMethod(); // test doesn't use route mapping correctly. + options.setTestException(false); // filters don't have visibility into exception handling above. + options.setHasResponseCustomizer(e -> false); + } + + @Override + public String expectedHttpRoute(ServerEndpoint endpoint, String method) { + // no need to compute route if we're not expecting it + if (!hasHttpRouteAttribute(endpoint)) { + return null; + } + + if (method.equals(HttpConstants._OTHER)) { + return getContextPath() + endpoint.getPath(); + } + + // NOTE: Primary difference from javaagent servlet instrumentation! + // Since just we're working with a filter, we can't actually get the proper servlet path. + return getContextPath() + "/*"; + } + + @Override + protected void snippetInjectionWithServletOutputStream() { + // override + } + + @Override + protected void snippetInjectionWithPrintWriter() { + // override + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3SyncLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3SyncLibraryTest.java new file mode 100644 index 000000000000..d986b9f6c521 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/TomcatServlet3SyncLibraryTest.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.tomcat; + +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; + +class TomcatServlet3SyncLibraryTest extends TomcatServlet3LibraryTest { + + @Override + public Class servlet() { + return TestServlet3.Sync.class; + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatDispatchLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatDispatchLibraryTest.java new file mode 100644 index 000000000000..73a82b651a3c --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatDispatchLibraryTest.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.tomcat.dispatch; + +import io.opentelemetry.instrumentation.servlet.v3_0.tomcat.TomcatServlet3LibraryTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; + +abstract class TomcatDispatchLibraryTest extends TomcatServlet3LibraryTest { + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setContextPath(getContextPath() + "/dispatch"); + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3DispatchAsyncLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3DispatchAsyncLibraryTest.java new file mode 100644 index 000000000000..0f80b9f284e6 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3DispatchAsyncLibraryTest.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.tomcat.dispatch; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_PARAMETERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; +import org.apache.catalina.Context; + +class TomcatServlet3DispatchAsyncLibraryTest extends TomcatDispatchLibraryTest { + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setVerifyServerSpanEndTime(false); + } + + @Override + public Class servlet() { + return TestServlet3.Async.class; + } + + @Override + protected void setupServlets(Context context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + QUERY_PARAM.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + ERROR.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + AUTH_REQUIRED.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + CAPTURE_HEADERS.getPath(), TestServlet3.DispatchAsync.class); + addServlet( + context, "/dispatch" + CAPTURE_PARAMETERS.getPath(), TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch" + INDEXED_CHILD.getPath(), TestServlet3.DispatchAsync.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), TestServlet3.DispatchAsync.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + TestServlet3.DispatchAsync.class); + addServlet(context, "/dispatch/recursive", TestServlet3.DispatchRecursive.class); + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } + + @Override + protected boolean assertParentOnRedirect() { + return false; + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3DispatchImmediateLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3DispatchImmediateLibraryTest.java new file mode 100644 index 000000000000..877728755f7e --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3DispatchImmediateLibraryTest.java @@ -0,0 +1,61 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.tomcat.dispatch; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_PARAMETERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; +import org.apache.catalina.Context; + +class TomcatServlet3DispatchImmediateLibraryTest extends TomcatDispatchLibraryTest { + + @Override + public Class servlet() { + return TestServlet3.Sync.class; + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestNotFound(false); + } + + @Override + protected void setupServlets(Context context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), TestServlet3.DispatchImmediate.class); + addServlet(context, "/dispatch" + QUERY_PARAM.getPath(), TestServlet3.DispatchImmediate.class); + addServlet(context, "/dispatch" + ERROR.getPath(), TestServlet3.DispatchImmediate.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), TestServlet3.DispatchImmediate.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), TestServlet3.DispatchImmediate.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), TestServlet3.DispatchImmediate.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), TestServlet3.DispatchImmediate.class); + addServlet( + context, "/dispatch" + CAPTURE_PARAMETERS.getPath(), TestServlet3.DispatchImmediate.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), TestServlet3.DispatchImmediate.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), TestServlet3.DispatchImmediate.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + TestServlet3.DispatchImmediate.class); + addServlet(context, "/dispatch/recursive", TestServlet3.DispatchRecursive.class); + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3ForwardLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3ForwardLibraryTest.java new file mode 100644 index 000000000000..f18eef5bf6f5 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3ForwardLibraryTest.java @@ -0,0 +1,64 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.tomcat.dispatch; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_PARAMETERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.RequestDispatcherServlet; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; +import org.apache.catalina.Context; + +class TomcatServlet3ForwardLibraryTest extends TomcatDispatchLibraryTest { + + @Override + public Class servlet() { + return TestServlet3.Sync.class; // dispatch to sync servlet + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestNotFound(false); + } + + @Override + protected void setupServlets(Context context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + QUERY_PARAM.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + ERROR.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, + "/dispatch" + CAPTURE_PARAMETERS.getPath(), + RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + RequestDispatcherServlet.Forward.class); + } +} diff --git a/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3IncludeLibraryTest.java b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3IncludeLibraryTest.java new file mode 100644 index 000000000000..52a82af9797e --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library-unit-tests/src/test/java/io/opentelemetry/instrumentation/servlet/v3_0/tomcat/dispatch/TomcatServlet3IncludeLibraryTest.java @@ -0,0 +1,64 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0.tomcat.dispatch; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_PARAMETERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.RequestDispatcherServlet; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TestServlet3; +import javax.servlet.Servlet; +import org.apache.catalina.Context; + +class TomcatServlet3IncludeLibraryTest extends TomcatDispatchLibraryTest { + + @Override + public Class servlet() { + return TestServlet3.Sync.class; // dispatch to sync servlet + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestNotFound(false); + options.setTestRedirect(false); + options.setTestCaptureHttpHeaders(false); + options.setTestError(false); + } + + @Override + protected void setupServlets(Context context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + QUERY_PARAM.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + ERROR.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, + "/dispatch" + CAPTURE_PARAMETERS.getPath(), + RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + RequestDispatcherServlet.Include.class); + } +} diff --git a/instrumentation/servlet/servlet-3.0/library/README.md b/instrumentation/servlet/servlet-3.0/library/README.md new file mode 100644 index 000000000000..7f383ca52aae --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library/README.md @@ -0,0 +1,52 @@ +# Library Instrumentation for Java Servlet version 3.0 and higher + +Provides OpenTelemetry instrumentation for Java Servlets through a servlet filter. + +## Quickstart + +### Add these dependencies to your project + +Replace `OPENTELEMETRY_VERSION` with +the [latest release](https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-servlet-3.0). + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + + io.opentelemetry.instrumentation + opentelemetry-servlet-3.0 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```kotlin +implementation("io.opentelemetry.instrumentation:opentelemetry-servlet-3.0:OPENTELEMETRY_VERSION") +``` + +### Usage + +Add the filter to your `web.xml` + +```xml + + + OpenTelemetryServletFilter + io.opentelemetry.instrumentation.servlet.v3_0.OpenTelemetryServletFilter + + true + + + OpenTelemetryServletFilter + /* + + +``` + +Note: GlobalOpenTelemetry must be set before filter initialization. If you are unable to ensure it +is set first, consider creating a subclass of `OpenTelemetryServletFilter` that handles +initialization of GlobalOpenTelemetry in a static initializer or constructor. diff --git a/instrumentation/servlet/servlet-3.0/library/build.gradle.kts b/instrumentation/servlet/servlet-3.0/library/build.gradle.kts new file mode 100644 index 000000000000..d9b10b2ce131 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library/build.gradle.kts @@ -0,0 +1,62 @@ +plugins { + id("otel.library-instrumentation") + id("com.gradleup.shadow") +} + +dependencies { + compileOnly("javax.servlet:javax.servlet-api:3.0.1") + + // Not great practice for library instrumentation to depend on javaagent classes. (Usually better the other way.) + implementation(project(":javaagent-extension-api")) + implementation(project(":instrumentation:servlet:servlet-common:bootstrap")) + implementation(project(":instrumentation:servlet:servlet-javax-common:javaagent")) + implementation(project(":instrumentation:servlet:servlet-common:javaagent")) + implementation(project(":instrumentation:servlet:servlet-3.0:javaagent")) +} + +tasks { + shadowJar { + archiveClassifier = "" + + duplicatesStrategy = DuplicatesStrategy.FAIL + exclude { + it.path.startsWith("META-INF") && !it.path.startsWith("META-INF/io/opentelemetry/instrumentation/") + } + + minimize() + + dependencies { + include(project(":javaagent-extension-api")) + include(project(":instrumentation:servlet:servlet-common:bootstrap")) + include(project(":instrumentation:servlet:servlet-common:javaagent")) + include(project(":instrumentation:servlet:servlet-javax-common:javaagent")) + include(project(":instrumentation:servlet:servlet-3.0:javaagent")) + } + + relocate( // :instrumentation:servlet:servlet-common:bootstrap + "io.opentelemetry.javaagent.bootstrap.servlet", + "io.opentelemetry.instrumentation.servlet.v3_0.shaded.servlet_bootstrap" + ) + relocate( // :javaagent-extension-api + "io.opentelemetry.javaagent.bootstrap", + "io.opentelemetry.instrumentation.servlet.v3_0.shaded.extension_api" + ) { + exclude("io.opentelemetry.javaagent.bootstrap.servlet") + } + relocate( // :instrumentation:servlet:servlet-javax-common:javaagent + "io.opentelemetry.javaagent.instrumentation.servlet.javax", + "io.opentelemetry.instrumentation.servlet.v3_0.shaded.servlet_javax" + ) + relocate( // :instrumentation:servlet:servlet-3.0:javaagent + "io.opentelemetry.javaagent.instrumentation.servlet.v3_0", + "io.opentelemetry.instrumentation.servlet.v3_0.shaded.servlet_v3_0" + ) + relocate( // :instrumentation:servlet:servlet-common:javaagent + "io.opentelemetry.javaagent.instrumentation.servlet", + "io.opentelemetry.instrumentation.servlet.v3_0.shaded.servlet_javaagent" + ) { + exclude("io.opentelemetry.javaagent.instrumentation.servlet.javax") + exclude("io.opentelemetry.javaagent.instrumentation.servlet.v3_0") + } + } +} diff --git a/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OpenTelemetryServletFilter.java b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OpenTelemetryServletFilter.java new file mode 100644 index 000000000000..536298595ea1 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OpenTelemetryServletFilter.java @@ -0,0 +1,67 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0; + +import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.FILTER_MAPPING_RESOLVER; + +import io.opentelemetry.javaagent.bootstrap.CallDepth; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3FilterMappingResolverFactory; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3RequestAdviceScope; +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * OpenTelemetry Library instrumentation for Java Servlet based applications that can't use a Java + * Agent. Due to inherent limitations in the servlet filter API, instrumenting at the filter level + * will miss anything that happens earlier in the filter stack or problems handled directly by the + * app server. For this reason, Java Agent instrumentation is preferred when possible. + */ +@WebFilter("/*") +public class OpenTelemetryServletFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) { + FILTER_MAPPING_RESOLVER.set(this, new Servlet3FilterMappingResolverFactory(filterConfig)); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + // Only HttpServlets are supported. + if (!(request instanceof HttpServletRequest && response instanceof HttpServletResponse)) { + chain.doFilter(request, response); + return; + } + + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + + Throwable throwable = null; + Servlet3RequestAdviceScope adviceScope = + new Servlet3RequestAdviceScope( + CallDepth.forClass(OpenTelemetryServletFilter.class), httpRequest, httpResponse, this); + try { + chain.doFilter( + new OtelHttpServletRequest(httpRequest), new OtelHttpServletResponse(httpResponse)); + } catch (Throwable e) { + throwable = e; + throw e; + } finally { + adviceScope.exit(throwable, httpRequest, httpResponse); + } + } + + @Override + public void destroy() {} +} diff --git a/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OtelAsyncContext.java b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OtelAsyncContext.java new file mode 100644 index 000000000000..fe54f6f22b15 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OtelAsyncContext.java @@ -0,0 +1,90 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0; + +import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.helper; + +import javax.servlet.AsyncContext; +import javax.servlet.AsyncListener; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +/// Delegates all methods except [#start(Runnable)] which wraps the [Runnable]. +public class OtelAsyncContext implements AsyncContext { + private final AsyncContext delegate; + + public OtelAsyncContext(AsyncContext delegate) { + this.delegate = delegate; + } + + @Override + public ServletRequest getRequest() { + return delegate.getRequest(); + } + + @Override + public ServletResponse getResponse() { + return delegate.getResponse(); + } + + @Override + public boolean hasOriginalRequestAndResponse() { + return delegate.hasOriginalRequestAndResponse(); + } + + @Override + public void dispatch() { + delegate.dispatch(); + } + + @Override + public void dispatch(String path) { + delegate.dispatch(path); + } + + @Override + public void dispatch(ServletContext context, String path) { + delegate.dispatch(context, path); + } + + @Override + public void complete() { + delegate.complete(); + } + + @Override + public void start(Runnable run) { + delegate.start(helper().wrapAsyncRunnable(run)); + } + + @Override + public void addListener(AsyncListener listener) { + delegate.addListener(listener); + } + + @Override + public void addListener( + AsyncListener listener, ServletRequest servletRequest, ServletResponse servletResponse) { + delegate.addListener(listener, servletRequest, servletResponse); + } + + @Override + public T createListener(Class clazz) throws ServletException { + return delegate.createListener(clazz); + } + + @Override + public void setTimeout(long timeout) { + delegate.setTimeout(timeout); + } + + @Override + public long getTimeout() { + return delegate.getTimeout(); + } +} diff --git a/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OtelHttpServletRequest.java b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OtelHttpServletRequest.java new file mode 100644 index 000000000000..f85e8309b054 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OtelHttpServletRequest.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0; + +import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.helper; + +import io.opentelemetry.context.Context; +import javax.servlet.AsyncContext; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +/// Wrapper around [HttpServletRequest] that attaches an async listener if [#startAsync()] is +/// invoked and a wrapper around [#getAsyncContext()] to capture exceptions from async [Runnable]s. +public class OtelHttpServletRequest extends HttpServletRequestWrapper { + + public OtelHttpServletRequest(HttpServletRequest request) { + super(request); + } + + @Override + public AsyncContext getAsyncContext() { + return new OtelAsyncContext(super.getAsyncContext()); + } + + @Override + public AsyncContext startAsync() { + try { + return new OtelAsyncContext(super.startAsync()); + } finally { + helper().attachAsyncListener(this, Context.current()); + } + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) { + try { + return new OtelAsyncContext(super.startAsync(servletRequest, servletResponse)); + } finally { + helper().attachAsyncListener(this, Context.current()); + } + } +} diff --git a/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OtelHttpServletResponse.java b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OtelHttpServletResponse.java new file mode 100644 index 000000000000..949bd9ceaeae --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/OtelHttpServletResponse.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.servlet.v3_0; + +import io.opentelemetry.javaagent.bootstrap.CallDepth; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3ResponseAdviceScope; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; + +/// Wrapper around [HttpServletResponse]. +public class OtelHttpServletResponse extends HttpServletResponseWrapper { + + public OtelHttpServletResponse(HttpServletResponse response) { + super(response); + } + + @Override + public void sendError(int sc, String msg) throws IOException { + Servlet3ResponseAdviceScope scope = + new Servlet3ResponseAdviceScope( + CallDepth.forClass(HttpServletResponse.class), this.getClass(), "sendError"); + Throwable throwable = null; + try { + super.sendError(sc, msg); + } catch (Throwable ex) { + throwable = ex; + throw ex; + } finally { + scope.exit(throwable); + } + } + + @Override + public void sendError(int sc) throws IOException { + Servlet3ResponseAdviceScope scope = + new Servlet3ResponseAdviceScope( + CallDepth.forClass(HttpServletResponse.class), this.getClass(), "sendError"); + Throwable throwable = null; + try { + super.sendError(sc); + } catch (Throwable ex) { + throwable = ex; + throw ex; + } finally { + scope.exit(throwable); + } + } + + @Override + public void sendRedirect(String location) throws IOException { + Servlet3ResponseAdviceScope scope = + new Servlet3ResponseAdviceScope( + CallDepth.forClass(HttpServletResponse.class), this.getClass(), "sendRedirect"); + Throwable throwable = null; + try { + super.sendRedirect(location); + } catch (Throwable ex) { + throwable = ex; + throw ex; + } finally { + scope.exit(throwable); + } + } +} diff --git a/instrumentation/servlet/servlet-3.0/testing/build.gradle.kts b/instrumentation/servlet/servlet-3.0/testing/build.gradle.kts index 012f2f49ca0d..467a23e93ccd 100644 --- a/instrumentation/servlet/servlet-3.0/testing/build.gradle.kts +++ b/instrumentation/servlet/servlet-3.0/testing/build.gradle.kts @@ -3,17 +3,22 @@ plugins { } dependencies { + implementation(project(":testing-common")) + implementation(project(":instrumentation:servlet:servlet-common:bootstrap")) + testImplementation(project(":instrumentation:servlet:servlet-3.0:javaagent")) + testImplementation(project(":instrumentation:servlet:servlet-3.0:testing")) compileOnly("javax.servlet:javax.servlet-api:3.0.1") + compileOnly(project(":testing:dependencies-shaded-for-testing", configuration = "shadow")) testInstrumentation(project(":instrumentation:jetty:jetty-8.0:javaagent")) testImplementation(project(":instrumentation:servlet:servlet-common:bootstrap")) - testLibrary("org.eclipse.jetty:jetty-server:8.0.0.v20110901") - testLibrary("org.eclipse.jetty:jetty-servlet:8.0.0.v20110901") - testLibrary("org.apache.tomcat.embed:tomcat-embed-core:8.0.41") - testLibrary("org.apache.tomcat.embed:tomcat-embed-jasper:8.0.41") + library("org.eclipse.jetty:jetty-server:8.0.0.v20110901") + library("org.eclipse.jetty:jetty-servlet:8.0.0.v20110901") + library("org.apache.tomcat.embed:tomcat-embed-core:8.0.41") + library("org.apache.tomcat.embed:tomcat-embed-jasper:8.0.41") latestDepTestLibrary("org.eclipse.jetty:jetty-server:10.+") // see servlet-5.0 module latestDepTestLibrary("org.eclipse.jetty:jetty-servlet:10.+") // see servlet-5.0 module diff --git a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/AbstractServlet3Test.java b/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/AbstractServlet3Test.java similarity index 98% rename from instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/AbstractServlet3Test.java rename to instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/AbstractServlet3Test.java index 882ec82e6c39..b4fa686a28fe 100644 --- a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/AbstractServlet3Test.java +++ b/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/AbstractServlet3Test.java @@ -145,7 +145,7 @@ protected boolean assertParentOnRedirect() { } @Test - void snippetInjectionWithServletOutputStream() { + protected void snippetInjectionWithServletOutputStream() { ExperimentalSnippetHolder.setSnippet( "\n "); AggregatedHttpRequest request = request(HTML_SERVLET_OUTPUT_STREAM, "GET"); @@ -185,7 +185,7 @@ void snippetInjectionWithServletOutputStream() { } @Test - void snippetInjectionWithPrintWriter() { + protected void snippetInjectionWithPrintWriter() { ExperimentalSnippetHolder.setSnippet("\n "); AggregatedHttpRequest request = request(HTML_PRINT_WRITER, "GET"); AggregatedHttpResponse response = client.execute(request).aggregate().join(); diff --git a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/RequestDispatcherServlet.java b/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/RequestDispatcherServlet.java similarity index 100% rename from instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/RequestDispatcherServlet.java rename to instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/RequestDispatcherServlet.java diff --git a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/TestServlet3.java b/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/TestServlet3.java similarity index 100% rename from instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/TestServlet3.java rename to instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/TestServlet3.java diff --git a/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/jetty/AbstractJettyServlet3Test.java b/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/jetty/AbstractJettyServlet3Test.java new file mode 100644 index 000000000000..a88c48dca98f --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/jetty/AbstractJettyServlet3Test.java @@ -0,0 +1,112 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v3_0.jetty; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.AbstractServlet3Test; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.trace.data.SpanData; +import java.io.IOException; +import java.io.Writer; +import java.net.InetSocketAddress; +import javax.servlet.Servlet; +import javax.servlet.http.HttpServletRequest; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ErrorHandler; +import org.eclipse.jetty.servlet.ServletContextHandler; + +public abstract class AbstractJettyServlet3Test + extends AbstractServlet3Test { + + static final boolean IS_BEFORE_94 = isBefore94(); + + public static boolean isBefore94() { + String[] version = Server.getVersion().split("\\."); + int major = Integer.parseInt(version[0]); + int minor = Integer.parseInt(version[1]); + return major < 9 || (major == 9 && minor < 4); + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestNotFound(false); + options.setContextPath("/jetty-context"); + options.setVerifyServerSpanEndTime(!isAsyncTest()); + } + + @Override + protected boolean hasResponseSpan(ServerEndpoint endpoint) { + return (IS_BEFORE_94 && endpoint == EXCEPTION && !isAsyncTest()) + || super.hasResponseSpan(endpoint); + } + + public boolean isAsyncTest() { + return false; + } + + @Override + protected SpanDataAssert assertResponseSpan( + SpanDataAssert span, + SpanData serverSpan, + SpanData controllerSpan, + SpanData handlerSpan, + String method, + ServerEndpoint endpoint) { + if (IS_BEFORE_94 && endpoint.equals(EXCEPTION)) { + span.satisfies(it -> assertThat(it.getName()).matches(".*\\.sendError")) + .hasKind(SpanKind.INTERNAL) + .hasParent(serverSpan); + } + + return super.assertResponseSpan( + span, serverSpan, controllerSpan, handlerSpan, method, endpoint); + } + + @Override + protected Server setupServer() throws Exception { + Server jettyServer = new Server(new InetSocketAddress("localhost", port)); + + ServletContextHandler servletContext = new ServletContextHandler(null, getContextPath()); + servletContext.setErrorHandler( + new ErrorHandler() { + @Override + protected void handleErrorPage( + HttpServletRequest request, Writer writer, int code, String message) + throws IOException { + Throwable th = (Throwable) request.getAttribute("javax.servlet.error.exception"); + writer.write(th != null ? th.getMessage() : message); + } + }); + addFilter(servletContext); + setupServlets(servletContext); + jettyServer.setHandler(servletContext); + + jettyServer.start(); + + return jettyServer; + } + + protected void addFilter(ServletContextHandler servletContext) {} + + @Override + public void stopServer(Server server) throws Exception { + server.stop(); + server.destroy(); + } + + @Override + public void addServlet( + ServletContextHandler servletContext, String path, Class servlet) + throws Exception { + servletContext.addServlet(servlet, path); + } +} diff --git a/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/AbstractTomcatServlet3Test.java b/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/AbstractTomcatServlet3Test.java new file mode 100644 index 000000000000..5bbc53e11d7f --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/AbstractTomcatServlet3Test.java @@ -0,0 +1,221 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v3_0.tomcat; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.AbstractServlet3Test; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.testing.assertj.TraceAssert; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import javax.servlet.Servlet; +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.core.StandardHost; +import org.apache.catalina.startup.Tomcat; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public abstract class AbstractTomcatServlet3Test extends AbstractServlet3Test { + + protected static final ServerEndpoint ACCESS_LOG_SUCCESS = + new ServerEndpoint( + "ACCESS_LOG_SUCCESS", + "success?access-log=true", + SUCCESS.getStatus(), + SUCCESS.getBody(), + false); + protected static final ServerEndpoint ACCESS_LOG_ERROR = + new ServerEndpoint( + "ACCESS_LOG_ERROR", + "error-status?access-log=true", + ERROR.getStatus(), + ERROR.getBody(), + false); + protected final TestAccessLogValve accessLogValue = new TestAccessLogValve(); + + @TempDir protected static File tempDir; + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setContextPath("/tomcat-context"); + options.setTestError(testError()); + } + + public boolean testError() { + return false; + } + + @Override + protected SpanDataAssert assertResponseSpan( + SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) { + if (NOT_FOUND.equals(endpoint)) { + span.satisfies(s -> assertThat(s.getName()).matches(".*\\.sendError")) + .hasKind(SpanKind.INTERNAL) + .hasParent(parentSpan); + } + return super.assertResponseSpan(span, parentSpan, method, endpoint); + } + + @Override + protected boolean hasResponseSpan(ServerEndpoint endpoint) { + return endpoint == NOT_FOUND || super.hasResponseSpan(endpoint); + } + + @Override + protected Tomcat setupServer() throws Exception { + Tomcat tomcatServer = new Tomcat(); + + File baseDir = tempDir; + tomcatServer.setBaseDir(baseDir.getAbsolutePath()); + + tomcatServer.setPort(port); + tomcatServer.getConnector().setEnableLookups(true); // get localhost instead of 127.0.0.1 + + File applicationDir = new File(baseDir, "/webapps/ROOT"); + applicationDir.mkdirs(); + + Context servletContext = + tomcatServer.addWebapp(getContextPath(), applicationDir.getAbsolutePath()); + // Speed up startup by disabling jar scanning: + servletContext.getJarScanner().setJarScanFilter((jarScanType, jarName) -> false); + + setupServlets(servletContext); + + ((StandardHost) tomcatServer.getHost()) + .setErrorReportValveClass(ErrorHandlerValve.class.getName()); + tomcatServer.getHost().getPipeline().addValve(accessLogValue); + + beforeStart(tomcatServer); + + tomcatServer.start(); + + return tomcatServer; + } + + protected void beforeStart(Tomcat tomcatServer) {} + + @BeforeEach + void setUp() { + accessLogValue.getLoggedIds().clear(); + testing().clearAllExportedData(); + } + + @Override + public void stopServer(Tomcat server) throws LifecycleException { + // requires --add-opens=java.base/java.util=ALL-UNNAMED on newer JVMs. + server.stop(); + server.destroy(); + } + + @Override + public void addServlet(Context servletContext, String path, Class servlet) + throws Exception { + String name = UUID.randomUUID().toString(); + Tomcat.addServlet(servletContext, name, servlet.getConstructor().newInstance()); + servletContext.addServletMappingDecoded(path, name); + } + + @ParameterizedTest + @CsvSource({"1", "4"}) + void accessLogHasIdsForCountRequests(int count) { + AggregatedHttpRequest request = request(ACCESS_LOG_SUCCESS, "GET"); + + IntStream.range(0, count) + .mapToObj(i -> client.execute(request).aggregate().join()) + .forEach( + response -> { + assertThat(response.status().code()).isEqualTo(ACCESS_LOG_SUCCESS.getStatus()); + assertThat(response.contentUtf8()).isEqualTo(ACCESS_LOG_SUCCESS.getBody()); + }); + + accessLogValue.waitForLoggedIds(count); + assertThat(accessLogValue.getLoggedIds().size()).isEqualTo(count); + List loggedTraces = + accessLogValue.getLoggedIds().stream().map(Map.Entry::getKey).collect(Collectors.toList()); + List loggedSpans = + accessLogValue.getLoggedIds().stream() + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + + testing() + .waitAndAssertTraces( + IntStream.range(0, count) + .mapToObj( + i -> + (Consumer) + trace -> { + trace.hasSpansSatisfyingExactly( + span -> + assertServerSpan( + span, "GET", ACCESS_LOG_SUCCESS, SUCCESS.getStatus()), + span -> assertControllerSpan(span, null)); + SpanData span = trace.getSpan(0); + assertThat(loggedTraces).contains(span.getTraceId()); + assertThat(loggedSpans).contains(span.getSpanId()); + }) + .collect(Collectors.toList())); + } + + @Test + void accessLogHasIdsForErrorRequest() { + Assumptions.assumeTrue(testError()); + + AggregatedHttpRequest request = request(ACCESS_LOG_ERROR, "GET"); + AggregatedHttpResponse response = client.execute(request).aggregate().join(); + + assertThat(response.status().code()).isEqualTo(ACCESS_LOG_ERROR.getStatus()); + assertThat(response.contentUtf8()).isEqualTo(ACCESS_LOG_ERROR.getBody()); + + List> spanDataAsserts = new ArrayList<>(); + spanDataAsserts.add( + (span, trace) -> assertServerSpan(span, "GET", ACCESS_LOG_ERROR, ERROR.getStatus())); + spanDataAsserts.add((span, trace) -> assertControllerSpan(span, null)); + if (errorEndpointUsesSendError()) { + spanDataAsserts.add( + (span, trace) -> + span.satisfies(s -> assertThat(s.getName()).matches(".*\\.sendError")) + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))); + } + + accessLogValue.waitForLoggedIds(1); + testing() + .waitAndAssertTraces( + trace -> { + trace.hasSpansSatisfyingExactly( + spanDataAsserts.stream() + .map(e -> (Consumer) span -> e.accept(span, trace)) + .collect(Collectors.toList())); + SpanData span = trace.getSpan(0); + Map.Entry entry = accessLogValue.getLoggedIds().get(0); + assertThat(entry.getKey()).isEqualTo(span.getTraceId()); + assertThat(entry.getValue()).isEqualTo(span.getSpanId()); + }); + } +} diff --git a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/ErrorHandlerValve.java b/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/ErrorHandlerValve.java similarity index 100% rename from instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/ErrorHandlerValve.java rename to instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/ErrorHandlerValve.java diff --git a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TestAccessLogValve.java b/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TestAccessLogValve.java similarity index 96% rename from instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TestAccessLogValve.java rename to instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TestAccessLogValve.java index 1be5cbe759a7..fe45818ce518 100644 --- a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TestAccessLogValve.java +++ b/instrumentation/servlet/servlet-3.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TestAccessLogValve.java @@ -55,13 +55,13 @@ public void waitForLoggedIds(int expected) { try { loggedIds.wait(toWait); } catch (InterruptedException e) { - throw new RuntimeException(e); + throw new IllegalStateException(e); } toWait = endTime - System.currentTimeMillis(); } if (toWait <= 0) { - throw new RuntimeException( + throw new IllegalStateException( "Timeout waiting for " + expected + " access log ids, got " + loggedIds.size()); } } diff --git a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/jetty/JettyServlet3Test.java b/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/jetty/JettyServlet3Test.java index e0fd16890141..129e73a67b5c 100644 --- a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/jetty/JettyServlet3Test.java +++ b/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/jetty/JettyServlet3Test.java @@ -5,112 +5,12 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v3_0.jetty; -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; - -import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; -import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; -import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; -import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.AbstractServlet3Test; -import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; -import io.opentelemetry.sdk.trace.data.SpanData; -import java.io.IOException; -import java.io.Writer; -import java.net.InetSocketAddress; -import javax.servlet.Servlet; -import javax.servlet.http.HttpServletRequest; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.ErrorHandler; -import org.eclipse.jetty.servlet.ServletContextHandler; import org.junit.jupiter.api.extension.RegisterExtension; -public abstract class JettyServlet3Test - extends AbstractServlet3Test { - +public abstract class JettyServlet3Test extends AbstractJettyServlet3Test { @RegisterExtension protected static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent(); - - static final boolean IS_BEFORE_94 = isBefore94(); - - public static boolean isBefore94() { - String[] version = Server.getVersion().split("\\."); - int major = Integer.parseInt(version[0]); - int minor = Integer.parseInt(version[1]); - return major < 9 || (major == 9 && minor < 4); - } - - @Override - protected void configure(HttpServerTestOptions options) { - super.configure(options); - options.setTestNotFound(false); - options.setContextPath("/jetty-context"); - options.setVerifyServerSpanEndTime(!isAsyncTest()); - } - - @Override - protected boolean hasResponseSpan(ServerEndpoint endpoint) { - return (IS_BEFORE_94 && endpoint == EXCEPTION && !isAsyncTest()) - || super.hasResponseSpan(endpoint); - } - - public boolean isAsyncTest() { - return false; - } - - @Override - protected SpanDataAssert assertResponseSpan( - SpanDataAssert span, - SpanData serverSpan, - SpanData controllerSpan, - SpanData handlerSpan, - String method, - ServerEndpoint endpoint) { - if (IS_BEFORE_94 && endpoint.equals(EXCEPTION)) { - span.satisfies(it -> assertThat(it.getName()).matches(".*\\.sendError")) - .hasKind(SpanKind.INTERNAL) - .hasParent(serverSpan); - } - - return super.assertResponseSpan( - span, serverSpan, controllerSpan, handlerSpan, method, endpoint); - } - - @Override - protected Server setupServer() throws Exception { - Server jettyServer = new Server(new InetSocketAddress("localhost", port)); - - ServletContextHandler servletContext = new ServletContextHandler(null, getContextPath()); - servletContext.setErrorHandler( - new ErrorHandler() { - @Override - protected void handleErrorPage( - HttpServletRequest request, Writer writer, int code, String message) - throws IOException { - Throwable th = (Throwable) request.getAttribute("javax.servlet.error.exception"); - writer.write(th != null ? th.getMessage() : message); - } - }); - setupServlets(servletContext); - jettyServer.setHandler(servletContext); - - jettyServer.start(); - - return jettyServer; - } - - @Override - public void stopServer(Server server) throws Exception { - server.stop(); - server.destroy(); - } - - @Override - public void addServlet( - ServletContextHandler servletContext, String path, Class servlet) - throws Exception { - servletContext.addServlet(servlet, path); - } } diff --git a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TomcatServlet3Test.java b/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TomcatServlet3Test.java index f9563d35540a..e84eba694bfb 100644 --- a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TomcatServlet3Test.java +++ b/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TomcatServlet3Test.java @@ -5,219 +5,12 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v3_0.tomcat; -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND; -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; -import static org.assertj.core.api.Assertions.assertThat; - -import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; -import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; -import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; -import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.AbstractServlet3Test; -import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; -import io.opentelemetry.sdk.testing.assertj.TraceAssert; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest; -import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import javax.servlet.Servlet; -import org.apache.catalina.Context; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.core.StandardHost; -import org.apache.catalina.startup.Tomcat; -import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.api.io.TempDir; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -public abstract class TomcatServlet3Test extends AbstractServlet3Test { +public abstract class TomcatServlet3Test extends AbstractTomcatServlet3Test { @RegisterExtension protected static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent(); - - private static final ServerEndpoint ACCESS_LOG_SUCCESS = - new ServerEndpoint( - "ACCESS_LOG_SUCCESS", - "success?access-log=true", - SUCCESS.getStatus(), - SUCCESS.getBody(), - false); - private static final ServerEndpoint ACCESS_LOG_ERROR = - new ServerEndpoint( - "ACCESS_LOG_ERROR", - "error-status?access-log=true", - ERROR.getStatus(), - ERROR.getBody(), - false); - private final TestAccessLogValve accessLogValue = new TestAccessLogValve(); - - @TempDir private static File tempDir; - - @Override - protected void configure(HttpServerTestOptions options) { - super.configure(options); - options.setContextPath("/tomcat-context"); - options.setTestError(testError()); - } - - public boolean testError() { - return false; - } - - @Override - protected SpanDataAssert assertResponseSpan( - SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) { - if (NOT_FOUND.equals(endpoint)) { - span.satisfies(s -> assertThat(s.getName()).matches(".*\\.sendError")) - .hasKind(SpanKind.INTERNAL) - .hasParent(parentSpan); - } - return super.assertResponseSpan(span, parentSpan, method, endpoint); - } - - @Override - protected boolean hasResponseSpan(ServerEndpoint endpoint) { - return endpoint == NOT_FOUND || super.hasResponseSpan(endpoint); - } - - @Override - protected Tomcat setupServer() throws Exception { - Tomcat tomcatServer = new Tomcat(); - - File baseDir = tempDir; - tomcatServer.setBaseDir(baseDir.getAbsolutePath()); - - tomcatServer.setPort(port); - tomcatServer.getConnector().setEnableLookups(true); // get localhost instead of 127.0.0.1 - - File applicationDir = new File(baseDir, "/webapps/ROOT"); - applicationDir.mkdirs(); - - Context servletContext = - tomcatServer.addWebapp(getContextPath(), applicationDir.getAbsolutePath()); - // Speed up startup by disabling jar scanning: - servletContext.getJarScanner().setJarScanFilter((jarScanType, jarName) -> false); - - setupServlets(servletContext); - - ((StandardHost) tomcatServer.getHost()) - .setErrorReportValveClass(ErrorHandlerValve.class.getName()); - tomcatServer.getHost().getPipeline().addValve(accessLogValue); - - tomcatServer.start(); - - return tomcatServer; - } - - @BeforeEach - void setUp() { - accessLogValue.getLoggedIds().clear(); - testing().clearAllExportedData(); - } - - @Override - public void stopServer(Tomcat server) throws LifecycleException { - server.stop(); - server.destroy(); - } - - @Override - public void addServlet(Context servletContext, String path, Class servlet) - throws Exception { - String name = UUID.randomUUID().toString(); - Tomcat.addServlet(servletContext, name, servlet.getConstructor().newInstance()); - servletContext.addServletMappingDecoded(path, name); - } - - @ParameterizedTest - @CsvSource({"1", "4"}) - void accessLogHasIdsForCountRequests(int count) { - AggregatedHttpRequest request = request(ACCESS_LOG_SUCCESS, "GET"); - - IntStream.range(0, count) - .mapToObj(i -> client.execute(request).aggregate().join()) - .forEach( - response -> { - assertThat(response.status().code()).isEqualTo(ACCESS_LOG_SUCCESS.getStatus()); - assertThat(response.contentUtf8()).isEqualTo(ACCESS_LOG_SUCCESS.getBody()); - }); - - accessLogValue.waitForLoggedIds(count); - assertThat(accessLogValue.getLoggedIds().size()).isEqualTo(count); - List loggedTraces = - accessLogValue.getLoggedIds().stream().map(Map.Entry::getKey).collect(Collectors.toList()); - List loggedSpans = - accessLogValue.getLoggedIds().stream() - .map(Map.Entry::getValue) - .collect(Collectors.toList()); - - testing() - .waitAndAssertTraces( - IntStream.range(0, count) - .mapToObj( - i -> - (Consumer) - trace -> { - trace.hasSpansSatisfyingExactly( - span -> - assertServerSpan( - span, "GET", ACCESS_LOG_SUCCESS, SUCCESS.getStatus()), - span -> assertControllerSpan(span, null)); - SpanData span = trace.getSpan(0); - assertThat(loggedTraces).contains(span.getTraceId()); - assertThat(loggedSpans).contains(span.getSpanId()); - }) - .collect(Collectors.toList())); - } - - @Test - void accessLogHasIdsForErrorRequest() { - Assumptions.assumeTrue(testError()); - - AggregatedHttpRequest request = request(ACCESS_LOG_ERROR, "GET"); - AggregatedHttpResponse response = client.execute(request).aggregate().join(); - - assertThat(response.status().code()).isEqualTo(ACCESS_LOG_ERROR.getStatus()); - assertThat(response.contentUtf8()).isEqualTo(ACCESS_LOG_ERROR.getBody()); - - List> spanDataAsserts = new ArrayList<>(); - spanDataAsserts.add( - (span, trace) -> assertServerSpan(span, "GET", ACCESS_LOG_ERROR, ERROR.getStatus())); - spanDataAsserts.add((span, trace) -> assertControllerSpan(span, null)); - if (errorEndpointUsesSendError()) { - spanDataAsserts.add( - (span, trace) -> - span.satisfies(s -> assertThat(s.getName()).matches(".*\\.sendError")) - .hasKind(SpanKind.INTERNAL) - .hasParent(trace.getSpan(1))); - } - - accessLogValue.waitForLoggedIds(1); - testing() - .waitAndAssertTraces( - trace -> { - trace.hasSpansSatisfyingExactly( - spanDataAsserts.stream() - .map(e -> (Consumer) span -> e.accept(span, trace)) - .collect(Collectors.toList())); - SpanData span = trace.getSpan(0); - Map.Entry entry = accessLogValue.getLoggedIds().get(0); - assertThat(entry.getKey()).isEqualTo(span.getTraceId()); - assertThat(entry.getValue()).isEqualTo(span.getSpanId()); - }); - } } diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/AsyncRunnableWrapper.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/AsyncRunnableWrapper.java index 31aafe5a6214..f101a8112c53 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/AsyncRunnableWrapper.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/AsyncRunnableWrapper.java @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.servlet; import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; public class AsyncRunnableWrapper implements Runnable { private final ServletHelper helper; @@ -27,7 +28,7 @@ public static Runnable wrap(ServletHelper helper, Runnable @Override public void run() { - try { + try (Scope scope = context.makeCurrent()) { runnable.run(); } catch (Throwable throwable) { helper.recordAsyncException(context, throwable); diff --git a/settings.gradle.kts b/settings.gradle.kts index bdd1ccc0816c..374c1b9357f7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -595,6 +595,8 @@ include(":instrumentation:scala-fork-join-2.8:javaagent") include(":instrumentation:servlet:servlet-2.2:javaagent") include(":instrumentation:servlet:servlet-3.0:javaagent") include(":instrumentation:servlet:servlet-3.0:javaagent-unit-tests") +include(":instrumentation:servlet:servlet-3.0:library") +include(":instrumentation:servlet:servlet-3.0:library-unit-tests") include(":instrumentation:servlet:servlet-3.0:testing") include(":instrumentation:servlet:servlet-5.0:javaagent") include(":instrumentation:servlet:servlet-5.0:javaagent-unit-tests")