helper() {
+ return HELPER;
+ }
+
+ private Jetty8Singletons() {}
+}
diff --git a/instrumentation/jetty/jetty-8.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/v8_0/package-info.java b/instrumentation/jetty/jetty-8.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/v8_0/package-info.java
index edd7e09db1fd..9d6666e26e0d 100644
--- a/instrumentation/jetty/jetty-8.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/v8_0/package-info.java
+++ b/instrumentation/jetty/jetty-8.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/v8_0/package-info.java
@@ -9,8 +9,7 @@
*
* As instrumentation points differ between servlet instrumentations and this one, this module
* has its own {@code JettyHandlerInstrumentation} and {@code JettyHandlerAdvice}. But this is the
- * only difference between two instrumentations, thus {@link
- * io.opentelemetry.javaagent.instrumentation.jetty.v8_0.Jetty8HttpServerTracer} is a very thin
- * subclass of {@link io.opentelemetry.instrumentation.servlet.v3_0.Servlet3HttpServerTracer}.
+ * only difference between two instrumentations, thus jetty instrumentation largely reuses servlet
+ * instrumentation.
*/
package io.opentelemetry.javaagent.instrumentation.jetty.v8_0;
diff --git a/instrumentation/jetty/jetty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/common/JettyHandlerAdviceHelper.java b/instrumentation/jetty/jetty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/common/JettyHandlerAdviceHelper.java
deleted file mode 100644
index 0af191a78854..000000000000
--- a/instrumentation/jetty/jetty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/common/JettyHandlerAdviceHelper.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.instrumentation.jetty.common;
-
-import io.opentelemetry.context.Context;
-import io.opentelemetry.context.Scope;
-import io.opentelemetry.instrumentation.servlet.ServletHttpServerTracer;
-import io.opentelemetry.javaagent.instrumentation.servlet.common.service.ServletAndFilterAdviceHelper;
-
-public class JettyHandlerAdviceHelper {
- /** Shared method exit implementation for Jetty handler advices. */
- public static void stopSpan(
- ServletHttpServerTracer tracer,
- REQUEST request,
- RESPONSE response,
- Throwable throwable,
- Context context,
- Scope scope) {
- if (scope == null) {
- return;
- }
- scope.close();
-
- if (context == null) {
- // an existing span was found
- return;
- }
-
- tracer.setPrincipal(context, request);
-
- // throwable is read-only, copy it to a new local that can be modified
- Throwable exception = throwable;
- if (exception == null) {
- // on jetty versions before 9.4 exceptions from servlet don't propagate to this method
- // check from request whether a throwable has been stored there
- exception = tracer.errorException(request);
- }
- if (exception != null) {
- tracer.endExceptionally(context, exception, response);
- return;
- }
-
- if (ServletAndFilterAdviceHelper.mustEndOnHandlerMethodExit(tracer, request)) {
- tracer.end(context, response);
- }
- }
-}
diff --git a/instrumentation/jetty/jetty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/common/JettyHelper.java b/instrumentation/jetty/jetty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/common/JettyHelper.java
new file mode 100644
index 000000000000..47fa49cc4b60
--- /dev/null
+++ b/instrumentation/jetty/jetty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/common/JettyHelper.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.jetty.common;
+
+import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTAINER;
+
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.servlet.AppServerBridge;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletResponseContext;
+
+public class JettyHelper extends ServletHelper {
+
+ public JettyHelper(
+ Instrumenter, ServletResponseContext> instrumenter,
+ ServletAccessor accessor) {
+ super(instrumenter, accessor);
+ }
+
+ public Context start(Context parentContext, ServletRequestContext requestContext) {
+ return start(parentContext, requestContext, CONTAINER);
+ }
+
+ @Override
+ protected Context customizeContext(Context context, REQUEST httpServletRequest) {
+ return AppServerBridge.init(context, /* shouldRecordException= */ false);
+ }
+
+ public void end(
+ ServletRequestContext requestContext,
+ REQUEST request,
+ RESPONSE response,
+ Throwable throwable,
+ Context context,
+ Scope scope) {
+
+ if (scope == null) {
+ return;
+ }
+ scope.close();
+
+ if (throwable == null) {
+ // on jetty versions before 9.4 exceptions from servlet don't propagate to this method
+ // check from request whether a throwable has been stored there
+ throwable = errorException(request);
+ }
+
+ ServletResponseContext responseContext =
+ new ServletResponseContext<>(response, throwable);
+ if (throwable != null || mustEndOnHandlerMethodExit(request)) {
+ instrumenter.end(context, requestContext, responseContext, throwable);
+ }
+ }
+
+ private Throwable errorException(REQUEST request) {
+ Object value = accessor.getRequestAttribute(request, errorExceptionAttributeName());
+
+ if (value instanceof Throwable) {
+ return (Throwable) value;
+ } else {
+ return null;
+ }
+ }
+
+ private static String errorExceptionAttributeName() {
+ return "javax.servlet.error.exception";
+ }
+}
diff --git a/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyHttpServerTracer.java b/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyHttpServerTracer.java
index 4f3ef89c009f..297fb40fd221 100644
--- a/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyHttpServerTracer.java
+++ b/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyHttpServerTracer.java
@@ -6,6 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.liberty;
import io.opentelemetry.context.Context;
+import io.opentelemetry.instrumentation.api.servlet.AppServerBridge;
import io.opentelemetry.instrumentation.servlet.v3_0.Servlet3HttpServerTracer;
import javax.servlet.http.HttpServletRequest;
@@ -20,6 +21,12 @@ public Context startSpan(HttpServletRequest request) {
return startSpan(request, "HTTP " + request.getMethod(), /* servlet= */ false);
}
+ @Override
+ protected Context customizeContext(Context context, HttpServletRequest httpServletRequest) {
+ context = super.customizeContext(context, httpServletRequest);
+ return AppServerBridge.init(context);
+ }
+
@Override
protected String getInstrumentationName() {
return "io.opentelemetry.liberty";
diff --git a/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyWebAppInstrumentation.java b/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyWebAppInstrumentation.java
index 437874aac167..66d145e07011 100644
--- a/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyWebAppInstrumentation.java
+++ b/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyWebAppInstrumentation.java
@@ -11,6 +11,7 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
+import io.opentelemetry.instrumentation.api.servlet.AppServerBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.servlet.common.service.ServletAndFilterAdviceHelper;
@@ -91,8 +92,13 @@ public static void stopSpan(
tracer().setPrincipal(context, request);
- if (throwable != null) {
- tracer().endExceptionally(context, throwable, response);
+ Throwable error = throwable;
+ if (error == null) {
+ error = AppServerBridge.getException(context);
+ }
+
+ if (error != null) {
+ tracer().endExceptionally(context, error, response);
return;
}
diff --git a/instrumentation/servlet/servlet-2.2/javaagent/build.gradle.kts b/instrumentation/servlet/servlet-2.2/javaagent/build.gradle.kts
index 3607fbff917b..c8e00410f10e 100644
--- a/instrumentation/servlet/servlet-2.2/javaagent/build.gradle.kts
+++ b/instrumentation/servlet/servlet-2.2/javaagent/build.gradle.kts
@@ -19,8 +19,8 @@ muzzle {
dependencies {
compileOnly("javax.servlet:servlet-api:2.2")
- api(project(":instrumentation:servlet:servlet-2.2:library"))
implementation(project(":instrumentation:servlet:servlet-common:javaagent"))
+ implementation(project(":instrumentation:servlet:servlet-javax-common:library"))
testInstrumentation(project(":instrumentation:servlet:servlet-javax-common:javaagent"))
diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/HttpServletResponseInstrumentation.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/HttpServletResponseInstrumentation.java
index 1cefd9f717a5..905b5573a037 100644
--- a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/HttpServletResponseInstrumentation.java
+++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/HttpServletResponseInstrumentation.java
@@ -16,7 +16,7 @@
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
-import javax.servlet.ServletRequest;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import net.bytebuddy.asm.Advice;
@@ -30,9 +30,9 @@
* created span using just response object.
*
* This instrumentation intercepts status setting methods from Servlet 2.0 specification and
- * stores that status into context store. Then {@link Servlet2Advice#stopSpan(ServletRequest,
- * ServletResponse, Throwable, CallDepth, Context, Scope)} can get it from context and set required
- * span attribute.
+ * stores that status into context store. Then {@link Servlet2Advice#stopSpan(ServletResponse,
+ * Throwable, CallDepth, ServletRequestContext, Context, Scope)} can get it from context and set
+ * required span attribute.
*/
public class HttpServletResponseInstrumentation implements TypeInstrumentation {
@Override
diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Accessor.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Accessor.java
new file mode 100644
index 000000000000..a30f0714c139
--- /dev/null
+++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Accessor.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet.v2_2;
+
+import io.opentelemetry.instrumentation.servlet.ServletAsyncListener;
+import io.opentelemetry.instrumentation.servlet.javax.JavaxServletAccessor;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class Servlet2Accessor extends JavaxServletAccessor {
+ public static final Servlet2Accessor INSTANCE = new Servlet2Accessor();
+
+ private Servlet2Accessor() {}
+
+ @Override
+ public Integer getRequestRemotePort(HttpServletRequest httpServletRequest) {
+ return null;
+ }
+
+ @Override
+ public void addRequestAsyncListener(
+ HttpServletRequest httpServletRequest,
+ ServletAsyncListener listener,
+ Object response) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getResponseStatus(HttpServletResponse httpServletResponse) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getResponseHeader(HttpServletResponse httpServletResponse, String name) {
+ return null;
+ }
+
+ @Override
+ public boolean isResponseCommitted(HttpServletResponse httpServletResponse) {
+ return httpServletResponse.isCommitted();
+ }
+}
diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Advice.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Advice.java
index 3a1526e8d8b9..eb90a3d4cc61 100644
--- a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Advice.java
+++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Advice.java
@@ -5,15 +5,15 @@
package io.opentelemetry.javaagent.instrumentation.servlet.v2_2;
-import static io.opentelemetry.instrumentation.servlet.v2_2.Servlet2HttpServerTracer.tracer;
+import static io.opentelemetry.javaagent.instrumentation.servlet.v2_2.Servlet2Singletons.helper;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.servlet.AppServerBridge;
-import io.opentelemetry.instrumentation.servlet.v2_2.ResponseWithStatus;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
@@ -29,6 +29,7 @@ public static void onEnter(
@Advice.Argument(0) ServletRequest request,
@Advice.Argument(value = 1, typing = Assigner.Typing.DYNAMIC) ServletResponse response,
@Advice.Local("otelCallDepth") CallDepth callDepth,
+ @Advice.Local("otelRequest") ServletRequestContext requestContext,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
callDepth = CallDepth.forClass(AppServerBridge.getCallDepthKey());
@@ -40,9 +41,9 @@ public static void onEnter(
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
- Context serverContext = tracer().getServerContext(httpServletRequest);
+ Context serverContext = helper().getServerContext(httpServletRequest);
if (serverContext != null) {
- Context updatedContext = tracer().updateContext(serverContext, httpServletRequest);
+ Context updatedContext = helper().updateContext(serverContext, httpServletRequest);
if (updatedContext != serverContext) {
// updateContext updated context, need to re-scope
scope = updatedContext.makeCurrent();
@@ -50,7 +51,14 @@ public static void onEnter(
return;
}
- context = tracer().startSpan(httpServletRequest);
+ Context parentContext = Java8BytecodeBridge.currentContext();
+ requestContext = new ServletRequestContext<>(httpServletRequest);
+
+ if (!helper().shouldStart(parentContext, requestContext)) {
+ return;
+ }
+
+ context = helper().startSpan(parentContext, requestContext);
scope = context.makeCurrent();
// reset response status from previous request
// (some servlet containers reuse response objects to reduce memory allocations)
@@ -59,10 +67,10 @@ public static void onEnter(
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
- @Advice.Argument(0) ServletRequest request,
@Advice.Argument(1) ServletResponse response,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelCallDepth") CallDepth callDepth,
+ @Advice.Local("otelRequest") ServletRequestContext requestContext,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
@@ -77,17 +85,14 @@ public static void stopSpan(
// Something else is managing the context, we're in the outermost level of Servlet
// instrumentation and we have an uncaught throwable. Let's add it to the current span.
if (throwable != null) {
- tracer().addUnwrappedThrowable(currentContext, throwable);
+ helper().recordException(currentContext, throwable);
}
- tracer().setPrincipal(currentContext, (HttpServletRequest) request);
}
if (scope == null || context == null) {
return;
}
- tracer().setPrincipal(context, (HttpServletRequest) request);
-
int responseStatusCode = HttpServletResponse.SC_OK;
Integer responseStatus =
InstrumentationContext.get(ServletResponse.class, Integer.class).get(response);
@@ -95,12 +100,8 @@ public static void stopSpan(
responseStatusCode = responseStatus;
}
- ResponseWithStatus responseWithStatus =
- new ResponseWithStatus((HttpServletResponse) response, responseStatusCode);
- if (throwable == null) {
- tracer().end(context, responseWithStatus);
- } else {
- tracer().endExceptionally(context, throwable, responseWithStatus);
- }
+ helper()
+ .stopSpan(
+ context, requestContext, (HttpServletResponse) response, responseStatusCode, throwable);
}
}
diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Helper.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Helper.java
new file mode 100644
index 000000000000..27332030f9fa
--- /dev/null
+++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Helper.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet.v2_2;
+
+import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.SERVLET;
+
+import io.opentelemetry.context.Context;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.javaagent.instrumentation.servlet.BaseServletHelper;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletResponseContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class Servlet2Helper extends BaseServletHelper {
+
+ Servlet2Helper(
+ Instrumenter<
+ ServletRequestContext,
+ ServletResponseContext>
+ instrumenter) {
+ super(instrumenter, Servlet2Accessor.INSTANCE);
+ }
+
+ public Context startSpan(
+ Context parentContext, ServletRequestContext requestContext) {
+ return start(parentContext, requestContext, SERVLET);
+ }
+
+ public void stopSpan(
+ Context context,
+ ServletRequestContext requestContext,
+ HttpServletResponse response,
+ int statusCode,
+ Throwable throwable) {
+
+ ServletResponseContext responseContext =
+ new ServletResponseContext<>(response, throwable);
+ responseContext.setStatus(statusCode);
+
+ instrumenter.end(context, requestContext, responseContext, throwable);
+ }
+}
diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2HttpAttributesExtractor.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2HttpAttributesExtractor.java
new file mode 100644
index 000000000000..61d32340597b
--- /dev/null
+++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2HttpAttributesExtractor.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet.v2_2;
+
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletHttpAttributesExtractor;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletResponseContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class Servlet2HttpAttributesExtractor
+ extends ServletHttpAttributesExtractor {
+ public Servlet2HttpAttributesExtractor(
+ ServletAccessor accessor) {
+ super(accessor);
+ }
+
+ @Override
+ protected @Nullable Integer statusCode(
+ ServletRequestContext requestContext,
+ ServletResponseContext responseContext) {
+ HttpServletResponse response = responseContext.response();
+
+ if (!accessor.isResponseCommitted(response) && responseContext.error() != null) {
+ // if response is not committed and there is a throwable set status to 500 /
+ // INTERNAL_SERVER_ERROR, due to servlet spec
+ // https://javaee.github.io/servlet-spec/downloads/servlet-4.0/servlet-4_0_FINAL.pdf:
+ // "If a servlet generates an error that is not handled by the error page mechanism as
+ // described above, the container must ensure to send a response with status 500."
+ return 500;
+ }
+ if (responseContext.hasStatus()) {
+ return responseContext.getStatus();
+ }
+ return null;
+ }
+}
diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Singletons.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Singletons.java
new file mode 100644
index 000000000000..18003fa51098
--- /dev/null
+++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Singletons.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet.v2_2;
+
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletResponseContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public final class Servlet2Singletons {
+ private static final String INSTRUMENTATION_NAME = "io.opentelemetry.servlet-2.2";
+
+ private static final Servlet2Helper HELPER;
+
+ static {
+ HttpAttributesExtractor<
+ ServletRequestContext, ServletResponseContext>
+ httpAttributesExtractor = new Servlet2HttpAttributesExtractor(Servlet2Accessor.INSTANCE);
+ SpanNameExtractor> spanNameExtractor =
+ new Servlet2SpanNameExtractor<>(Servlet2Accessor.INSTANCE);
+
+ Instrumenter<
+ ServletRequestContext, ServletResponseContext>
+ instrumenter =
+ ServletInstrumenterBuilder.newInstrumenter(
+ INSTRUMENTATION_NAME,
+ Servlet2Accessor.INSTANCE,
+ spanNameExtractor,
+ httpAttributesExtractor);
+ HELPER = new Servlet2Helper(instrumenter);
+ }
+
+ public static Servlet2Helper helper() {
+ return HELPER;
+ }
+
+ private Servlet2Singletons() {}
+}
diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2SpanNameExtractor.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2SpanNameExtractor.java
new file mode 100644
index 000000000000..051a939b4824
--- /dev/null
+++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2SpanNameExtractor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet.v2_2;
+
+import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
+
+public class Servlet2SpanNameExtractor
+ implements SpanNameExtractor> {
+ private final ServletAccessor accessor;
+
+ public Servlet2SpanNameExtractor(ServletAccessor accessor) {
+ this.accessor = accessor;
+ }
+
+ @Override
+ public String extract(ServletRequestContext requestContext) {
+ REQUEST request = requestContext.request();
+ String servletPath = accessor.getRequestServletPath(request);
+ if (!servletPath.isEmpty()) {
+ String contextPath = accessor.getRequestContextPath(request);
+ if (contextPath == null || contextPath.isEmpty() || contextPath.equals("/")) {
+ return servletPath;
+ }
+
+ return contextPath + servletPath;
+ }
+
+ String method = accessor.getRequestMethod(request);
+ if (method != null) {
+ return "HTTP " + method;
+ }
+ return "HTTP request";
+ }
+}
diff --git a/instrumentation/servlet/servlet-2.2/library/build.gradle.kts b/instrumentation/servlet/servlet-2.2/library/build.gradle.kts
deleted file mode 100644
index 2a0dea789b62..000000000000
--- a/instrumentation/servlet/servlet-2.2/library/build.gradle.kts
+++ /dev/null
@@ -1,11 +0,0 @@
-plugins {
- id("otel.library-instrumentation")
-}
-
-dependencies {
- implementation("org.slf4j:slf4j-api")
-
- api(project(":instrumentation:servlet:servlet-javax-common:library"))
-
- compileOnly("javax.servlet:servlet-api:2.2")
-}
diff --git a/instrumentation/servlet/servlet-2.2/library/src/main/java/io/opentelemetry/instrumentation/servlet/v2_2/ResponseWithStatus.java b/instrumentation/servlet/servlet-2.2/library/src/main/java/io/opentelemetry/instrumentation/servlet/v2_2/ResponseWithStatus.java
deleted file mode 100644
index 64bb260b668d..000000000000
--- a/instrumentation/servlet/servlet-2.2/library/src/main/java/io/opentelemetry/instrumentation/servlet/v2_2/ResponseWithStatus.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.instrumentation.servlet.v2_2;
-
-import javax.servlet.http.HttpServletResponse;
-
-public class ResponseWithStatus {
-
- private final HttpServletResponse response;
- private final int status;
-
- public ResponseWithStatus(HttpServletResponse response, int status) {
- this.response = response;
- this.status = status;
- }
-
- public HttpServletResponse getResponse() {
- return response;
- }
-
- public int getStatus() {
- return status;
- }
-}
diff --git a/instrumentation/servlet/servlet-2.2/library/src/main/java/io/opentelemetry/instrumentation/servlet/v2_2/Servlet2Accessor.java b/instrumentation/servlet/servlet-2.2/library/src/main/java/io/opentelemetry/instrumentation/servlet/v2_2/Servlet2Accessor.java
deleted file mode 100644
index 018a4933e32d..000000000000
--- a/instrumentation/servlet/servlet-2.2/library/src/main/java/io/opentelemetry/instrumentation/servlet/v2_2/Servlet2Accessor.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.instrumentation.servlet.v2_2;
-
-import io.opentelemetry.instrumentation.servlet.ServletAsyncListener;
-import io.opentelemetry.instrumentation.servlet.javax.JavaxServletAccessor;
-import javax.servlet.http.HttpServletRequest;
-
-public class Servlet2Accessor extends JavaxServletAccessor {
- public static final Servlet2Accessor INSTANCE = new Servlet2Accessor();
-
- private Servlet2Accessor() {}
-
- @Override
- public Integer getRequestRemotePort(HttpServletRequest httpServletRequest) {
- return null;
- }
-
- @Override
- public void addRequestAsyncListener(
- HttpServletRequest request,
- ServletAsyncListener listener,
- Object response) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getResponseStatus(ResponseWithStatus responseWithStatus) {
- return responseWithStatus.getStatus();
- }
-
- @Override
- public boolean isResponseCommitted(ResponseWithStatus responseWithStatus) {
- return responseWithStatus.getResponse().isCommitted();
- }
-}
diff --git a/instrumentation/servlet/servlet-2.2/library/src/main/java/io/opentelemetry/instrumentation/servlet/v2_2/Servlet2HttpServerTracer.java b/instrumentation/servlet/servlet-2.2/library/src/main/java/io/opentelemetry/instrumentation/servlet/v2_2/Servlet2HttpServerTracer.java
deleted file mode 100644
index 4f62a0e831ad..000000000000
--- a/instrumentation/servlet/servlet-2.2/library/src/main/java/io/opentelemetry/instrumentation/servlet/v2_2/Servlet2HttpServerTracer.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.instrumentation.servlet.v2_2;
-
-import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.SERVLET;
-
-import io.opentelemetry.context.Context;
-import io.opentelemetry.instrumentation.api.servlet.ServerSpanNameSupplier;
-import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
-import io.opentelemetry.instrumentation.servlet.javax.JavaxServletHttpServerTracer;
-import javax.servlet.http.HttpServletRequest;
-
-public class Servlet2HttpServerTracer extends JavaxServletHttpServerTracer {
- private static final Servlet2HttpServerTracer TRACER = new Servlet2HttpServerTracer();
-
- private final ServerSpanNameSupplier serverSpanName =
- (context, request) -> getSpanName(request);
-
- public Servlet2HttpServerTracer() {
- super(Servlet2Accessor.INSTANCE);
- }
-
- public static Servlet2HttpServerTracer tracer() {
- return TRACER;
- }
-
- public Context startSpan(HttpServletRequest request) {
- return startSpan(request, getSpanName(request), true);
- }
-
- @Override
- public Context updateContext(Context context, HttpServletRequest request) {
- ServerSpanNaming.updateServerSpanName(context, SERVLET, serverSpanName, request);
- return super.updateContext(context, request);
- }
-
- @Override
- protected String getInstrumentationName() {
- return "io.opentelemetry.servlet-2.2";
- }
-}
diff --git a/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts b/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts
index 15dfadcf13eb..182869d6219e 100644
--- a/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts
+++ b/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts
@@ -18,7 +18,7 @@ muzzle {
dependencies {
compileOnly("javax.servlet:javax.servlet-api:3.0.1")
api(project(":instrumentation:servlet:servlet-3.0:library"))
- implementation(project(":instrumentation:servlet:servlet-common:javaagent"))
+ api(project(":instrumentation:servlet:servlet-common:javaagent"))
testInstrumentation(project(":instrumentation:jetty:jetty-8.0:javaagent"))
testInstrumentation(project(":instrumentation:servlet:servlet-javax-common: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 296e349306f0..7d5db4ca5df0 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
@@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;
-import static io.opentelemetry.instrumentation.servlet.v3_0.Servlet3HttpServerTracer.tracer;
+import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.helper;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
@@ -15,7 +15,7 @@
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
-import io.opentelemetry.javaagent.instrumentation.servlet.common.service.ServletAndFilterAdviceHelper;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
@@ -34,6 +34,7 @@ public static void onEnter(
@Advice.Argument(value = 0, readOnly = false) ServletRequest request,
@Advice.Argument(value = 1, readOnly = false) ServletResponse response,
@Advice.Local("otelCallDepth") CallDepth callDepth,
+ @Advice.Local("otelRequest") ServletRequestContext requestContext,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
@@ -59,10 +60,10 @@ public static void onEnter(
}
Context currentContext = Java8BytecodeBridge.currentContext();
- Context attachedContext = tracer().getServerContext(httpServletRequest);
- if (attachedContext != null && tracer().needsRescoping(currentContext, attachedContext)) {
+ Context attachedContext = helper().getServerContext(httpServletRequest);
+ if (attachedContext != null && helper().needsRescoping(currentContext, attachedContext)) {
attachedContext =
- tracer().updateContext(attachedContext, httpServletRequest, mappingResolver, servlet);
+ helper().updateContext(attachedContext, httpServletRequest, mappingResolver, servlet);
scope = attachedContext.makeCurrent();
// We are inside nested servlet/filter/app-server span, don't create new span
return;
@@ -75,7 +76,7 @@ public static void onEnter(
// returns a new context that contains servlet context path that is used in other
// instrumentations for naming server span.
Context updatedContext =
- tracer().updateContext(currentContext, httpServletRequest, mappingResolver, servlet);
+ helper().updateContext(currentContext, httpServletRequest, mappingResolver, servlet);
if (currentContext != updatedContext) {
// updateContext updated context, need to re-scope
scope = updatedContext.makeCurrent();
@@ -84,10 +85,16 @@ public static void onEnter(
return;
}
- context = tracer().startSpan(httpServletRequest, mappingResolver, servlet);
+ requestContext = new ServletRequestContext<>(httpServletRequest, mappingResolver);
+
+ if (!helper().shouldStart(currentContext, requestContext)) {
+ return;
+ }
+
+ context = helper().start(currentContext, requestContext, servlet);
scope = context.makeCurrent();
- tracer().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
+ helper().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
@@ -96,22 +103,23 @@ public static void stopSpan(
@Advice.Argument(1) ServletResponse response,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelCallDepth") CallDepth callDepth,
+ @Advice.Local("otelRequest") ServletRequestContext requestContext,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
- boolean topLevel = callDepth.decrementAndGet() == 0;
-
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
return;
}
- ServletAndFilterAdviceHelper.stopSpan(
- tracer(),
- (HttpServletRequest) request,
- (HttpServletResponse) response,
- throwable,
- topLevel,
- context,
- scope);
+ boolean topLevel = callDepth.decrementAndGet() == 0;
+ helper()
+ .end(
+ requestContext,
+ (HttpServletRequest) request,
+ (HttpServletResponse) response,
+ throwable,
+ topLevel,
+ context,
+ scope);
}
}
diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3AsyncStartAdvice.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3AsyncStartAdvice.java
index d1545a4ff30c..9b676aabea58 100644
--- a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3AsyncStartAdvice.java
+++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3AsyncStartAdvice.java
@@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;
-import static io.opentelemetry.instrumentation.servlet.v3_0.Servlet3HttpServerTracer.tracer;
+import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.helper;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import javax.servlet.AsyncContext;
@@ -36,8 +36,8 @@ public static void startAsyncExit(
if (servletRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) servletRequest;
- if (!tracer().isAsyncListenerAttached(request)) {
- tracer().attachAsyncListener(request);
+ if (!helper().isAsyncListenerAttached(request)) {
+ helper().attachAsyncListener(request);
}
}
}
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
new file mode 100644
index 000000000000..2c41e64f47dd
--- /dev/null
+++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Singletons.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;
+
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.servlet.v3_0.Servlet3Accessor;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletResponseContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public final class Servlet3Singletons {
+ private static final String INSTRUMENTATION_NAME = "io.opentelemetry.servlet-3.0";
+
+ private static final Instrumenter<
+ ServletRequestContext, ServletResponseContext>
+ INSTRUMENTER =
+ ServletInstrumenterBuilder.newInstrumenter(
+ INSTRUMENTATION_NAME, Servlet3Accessor.INSTANCE);
+ private static final ServletHelper HELPER =
+ new ServletHelper<>(INSTRUMENTER, Servlet3Accessor.INSTANCE);
+
+ public static ServletHelper helper() {
+ return HELPER;
+ }
+
+ private Servlet3Singletons() {}
+}
diff --git a/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/Servlet3Accessor.java b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/Servlet3Accessor.java
index 5bd5900df897..a3cb44b56f3d 100644
--- a/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/Servlet3Accessor.java
+++ b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/Servlet3Accessor.java
@@ -39,6 +39,11 @@ public int getResponseStatus(HttpServletResponse response) {
return response.getStatus();
}
+ @Override
+ public String getResponseHeader(HttpServletResponse response, String name) {
+ return response.getHeader(name);
+ }
+
@Override
public boolean isResponseCommitted(HttpServletResponse response) {
return response.isCommitted();
diff --git a/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/Servlet3HttpServerTracer.java b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/Servlet3HttpServerTracer.java
index 801fc6bc855c..46b544960819 100644
--- a/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/Servlet3HttpServerTracer.java
+++ b/instrumentation/servlet/servlet-3.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/v3_0/Servlet3HttpServerTracer.java
@@ -16,6 +16,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+@Deprecated
public class Servlet3HttpServerTracer extends JavaxServletHttpServerTracer {
private static final Servlet3HttpServerTracer TRACER = new Servlet3HttpServerTracer();
private static final ServletSpanNameProvider SPAN_NAME_PROVIDER =
diff --git a/instrumentation/servlet/servlet-5.0/javaagent/build.gradle.kts b/instrumentation/servlet/servlet-5.0/javaagent/build.gradle.kts
index 34e703c605b0..a096df262924 100644
--- a/instrumentation/servlet/servlet-5.0/javaagent/build.gradle.kts
+++ b/instrumentation/servlet/servlet-5.0/javaagent/build.gradle.kts
@@ -12,8 +12,8 @@ muzzle {
}
dependencies {
- api(project(":instrumentation:servlet:servlet-5.0:library"))
- implementation(project(":instrumentation:servlet:servlet-common:javaagent"))
+ api(project(":instrumentation:servlet:servlet-common:javaagent"))
+ implementation(project(":instrumentation:servlet:servlet-common:library"))
compileOnly("jakarta.servlet:jakarta.servlet-api:5.0.0")
testLibrary("org.eclipse.jetty:jetty-server:11.0.0")
diff --git a/instrumentation/servlet/servlet-5.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/jakarta/v5_0/JakartaServletAccessor.java b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Accessor.java
similarity index 82%
rename from instrumentation/servlet/servlet-5.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/jakarta/v5_0/JakartaServletAccessor.java
rename to instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Accessor.java
index 690da46fad82..677cddccc516 100644
--- a/instrumentation/servlet/servlet-5.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/jakarta/v5_0/JakartaServletAccessor.java
+++ b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Accessor.java
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-package io.opentelemetry.instrumentation.servlet.jakarta.v5_0;
+package io.opentelemetry.javaagent.instrumentation.servlet.v5_0;
import io.opentelemetry.instrumentation.servlet.ServletAccessor;
import io.opentelemetry.instrumentation.servlet.ServletAsyncListener;
@@ -13,12 +13,12 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.security.Principal;
+import java.util.Collections;
-public class JakartaServletAccessor
- implements ServletAccessor {
- public static final JakartaServletAccessor INSTANCE = new JakartaServletAccessor();
+public class Servlet5Accessor implements ServletAccessor {
+ public static final Servlet5Accessor INSTANCE = new Servlet5Accessor();
- private JakartaServletAccessor() {}
+ private Servlet5Accessor() {}
@Override
public String getRequestContextPath(HttpServletRequest request) {
@@ -80,6 +80,11 @@ public String getRequestHeader(HttpServletRequest request, String name) {
return request.getHeader(name);
}
+ @Override
+ public Iterable getRequestHeaderNames(HttpServletRequest httpServletRequest) {
+ return Collections.list(httpServletRequest.getHeaderNames());
+ }
+
@Override
public String getRequestServletPath(HttpServletRequest request) {
return request.getServletPath();
@@ -100,6 +105,16 @@ public Integer getRequestRemotePort(HttpServletRequest request) {
return request.getRemotePort();
}
+ @Override
+ public String getRequestRemoteHost(HttpServletRequest request) {
+ return request.getRemoteHost();
+ }
+
+ @Override
+ public int getRequestContentLength(HttpServletRequest request) {
+ return request.getContentLength();
+ }
+
@Override
public void addRequestAsyncListener(
HttpServletRequest request,
@@ -117,6 +132,11 @@ public int getResponseStatus(HttpServletResponse response) {
return response.getStatus();
}
+ @Override
+ public String getResponseHeader(HttpServletResponse response, String name) {
+ return response.getHeader(name);
+ }
+
@Override
public boolean isResponseCommitted(HttpServletResponse response) {
return response.isCommitted();
diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Singletons.java b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Singletons.java
new file mode 100644
index 000000000000..5381b1c8cc41
--- /dev/null
+++ b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Singletons.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet.v5_0;
+
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletResponseContext;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+public final class Servlet5Singletons {
+ private static final String INSTRUMENTATION_NAME = "io.opentelemetry.servlet-5.0";
+
+ private static final Instrumenter<
+ ServletRequestContext, ServletResponseContext>
+ INSTRUMENTER =
+ ServletInstrumenterBuilder.newInstrumenter(
+ INSTRUMENTATION_NAME, Servlet5Accessor.INSTANCE);
+ private static final ServletHelper HELPER =
+ new ServletHelper<>(INSTRUMENTER, Servlet5Accessor.INSTANCE);
+
+ public static ServletHelper helper() {
+ return HELPER;
+ }
+
+ private Servlet5Singletons() {}
+}
diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/async/AsyncStartAdvice.java b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/async/AsyncStartAdvice.java
index 71f0eab527ae..0da88f03bf94 100644
--- a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/async/AsyncStartAdvice.java
+++ b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/async/AsyncStartAdvice.java
@@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.async;
-import static io.opentelemetry.instrumentation.servlet.jakarta.v5_0.JakartaServletHttpServerTracer.tracer;
+import static io.opentelemetry.javaagent.instrumentation.servlet.v5_0.Servlet5Singletons.helper;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import jakarta.servlet.AsyncContext;
@@ -34,8 +34,8 @@ public static void startAsyncExit(
}
if (request != null) {
- if (!tracer().isAsyncListenerAttached(request)) {
- tracer().attachAsyncListener(request);
+ if (!helper().isAsyncListenerAttached(request)) {
+ helper().attachAsyncListener(request);
}
}
}
diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/service/JakartaServletServiceAdvice.java b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/service/JakartaServletServiceAdvice.java
index a36366195d22..3972fdc0caa7 100644
--- a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/service/JakartaServletServiceAdvice.java
+++ b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/service/JakartaServletServiceAdvice.java
@@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.service;
-import static io.opentelemetry.instrumentation.servlet.jakarta.v5_0.JakartaServletHttpServerTracer.tracer;
+import static io.opentelemetry.javaagent.instrumentation.servlet.v5_0.Servlet5Singletons.helper;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
@@ -15,7 +15,7 @@
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
-import io.opentelemetry.javaagent.instrumentation.servlet.common.service.ServletAndFilterAdviceHelper;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
import jakarta.servlet.Filter;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletRequest;
@@ -34,6 +34,7 @@ public static void onEnter(
@Advice.Argument(value = 0, readOnly = false) ServletRequest request,
@Advice.Argument(value = 1, readOnly = false) ServletResponse response,
@Advice.Local("otelCallDepth") CallDepth callDepth,
+ @Advice.Local("otelRequest") ServletRequestContext requestContext,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
@@ -59,10 +60,10 @@ public static void onEnter(
}
Context currentContext = Java8BytecodeBridge.currentContext();
- Context attachedContext = tracer().getServerContext(httpServletRequest);
- if (attachedContext != null && tracer().needsRescoping(currentContext, attachedContext)) {
+ Context attachedContext = helper().getServerContext(httpServletRequest);
+ if (attachedContext != null && helper().needsRescoping(currentContext, attachedContext)) {
attachedContext =
- tracer().updateContext(attachedContext, httpServletRequest, mappingResolver, servlet);
+ helper().updateContext(attachedContext, httpServletRequest, mappingResolver, servlet);
scope = attachedContext.makeCurrent();
// We are inside nested servlet/filter/app-server span, don't create new span
return;
@@ -75,7 +76,7 @@ public static void onEnter(
// returns a new context that contains servlet context path that is used in other
// instrumentations for naming server span.
Context updatedContext =
- tracer().updateContext(currentContext, httpServletRequest, mappingResolver, servlet);
+ helper().updateContext(currentContext, httpServletRequest, mappingResolver, servlet);
if (currentContext != updatedContext) {
// updateContext updated context, need to re-scope
scope = updatedContext.makeCurrent();
@@ -84,10 +85,16 @@ public static void onEnter(
return;
}
- context = tracer().startSpan(httpServletRequest, mappingResolver, servlet);
+ requestContext = new ServletRequestContext<>(httpServletRequest, mappingResolver);
+
+ if (!helper().shouldStart(currentContext, requestContext)) {
+ return;
+ }
+
+ context = helper().start(currentContext, requestContext, servlet);
scope = context.makeCurrent();
- tracer().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
+ helper().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
@@ -96,6 +103,7 @@ public static void stopSpan(
@Advice.Argument(1) ServletResponse response,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelCallDepth") CallDepth callDepth,
+ @Advice.Local("otelRequest") ServletRequestContext requestContext,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
@@ -105,13 +113,14 @@ public static void stopSpan(
return;
}
- ServletAndFilterAdviceHelper.stopSpan(
- tracer(),
- (HttpServletRequest) request,
- (HttpServletResponse) response,
- throwable,
- topLevel,
- context,
- scope);
+ helper()
+ .end(
+ requestContext,
+ (HttpServletRequest) request,
+ (HttpServletResponse) response,
+ throwable,
+ topLevel,
+ context,
+ scope);
}
}
diff --git a/instrumentation/servlet/servlet-5.0/library/build.gradle.kts b/instrumentation/servlet/servlet-5.0/library/build.gradle.kts
deleted file mode 100644
index d087dbc9e064..000000000000
--- a/instrumentation/servlet/servlet-5.0/library/build.gradle.kts
+++ /dev/null
@@ -1,13 +0,0 @@
-plugins {
- id("otel.library-instrumentation")
-}
-
-dependencies {
- api(project(":instrumentation:servlet:servlet-common:library"))
-
- compileOnly("jakarta.servlet:jakarta.servlet-api:5.0.0")
-
- testImplementation("jakarta.servlet:jakarta.servlet-api:5.0.0")
- testImplementation("org.mockito:mockito-core:3.6.0")
- testImplementation("org.assertj:assertj-core")
-}
diff --git a/instrumentation/servlet/servlet-5.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/jakarta/v5_0/JakartaHttpServletRequestGetter.java b/instrumentation/servlet/servlet-5.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/jakarta/v5_0/JakartaHttpServletRequestGetter.java
deleted file mode 100644
index 24ab41aab743..000000000000
--- a/instrumentation/servlet/servlet-5.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/jakarta/v5_0/JakartaHttpServletRequestGetter.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.instrumentation.servlet.jakarta.v5_0;
-
-import io.opentelemetry.context.propagation.TextMapGetter;
-import jakarta.servlet.http.HttpServletRequest;
-import java.util.Collections;
-
-public class JakartaHttpServletRequestGetter implements TextMapGetter {
-
- public static final JakartaHttpServletRequestGetter GETTER =
- new JakartaHttpServletRequestGetter();
-
- @Override
- public Iterable keys(HttpServletRequest carrier) {
- return Collections.list(carrier.getHeaderNames());
- }
-
- @Override
- public String get(HttpServletRequest carrier, String key) {
- return carrier.getHeader(key);
- }
-}
diff --git a/instrumentation/servlet/servlet-5.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/jakarta/v5_0/JakartaServletHttpServerTracer.java b/instrumentation/servlet/servlet-5.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/jakarta/v5_0/JakartaServletHttpServerTracer.java
deleted file mode 100644
index 29114c69e857..000000000000
--- a/instrumentation/servlet/servlet-5.0/library/src/main/java/io/opentelemetry/instrumentation/servlet/jakarta/v5_0/JakartaServletHttpServerTracer.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.instrumentation.servlet.jakarta.v5_0;
-
-import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.FILTER;
-import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.SERVLET;
-
-import io.opentelemetry.context.Context;
-import io.opentelemetry.context.propagation.TextMapGetter;
-import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
-import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
-import io.opentelemetry.instrumentation.servlet.ServletHttpServerTracer;
-import io.opentelemetry.instrumentation.servlet.naming.ServletSpanNameProvider;
-import jakarta.servlet.RequestDispatcher;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-public class JakartaServletHttpServerTracer
- extends ServletHttpServerTracer {
- private static final JakartaServletHttpServerTracer TRACER = new JakartaServletHttpServerTracer();
- private static final ServletSpanNameProvider SPAN_NAME_PROVIDER =
- new ServletSpanNameProvider<>(JakartaServletAccessor.INSTANCE);
-
- public JakartaServletHttpServerTracer() {
- super(JakartaServletAccessor.INSTANCE);
- }
-
- public static JakartaServletHttpServerTracer tracer() {
- return TRACER;
- }
-
- public Context startSpan(
- HttpServletRequest request, MappingResolver mappingResolver, boolean servlet) {
- return startSpan(request, SPAN_NAME_PROVIDER.getSpanName(mappingResolver, request), servlet);
- }
-
- public Context updateContext(
- Context context,
- HttpServletRequest request,
- MappingResolver mappingResolver,
- boolean servlet) {
- ServerSpanNaming.updateServerSpanName(
- context,
- servlet ? SERVLET : FILTER,
- () -> SPAN_NAME_PROVIDER.getSpanNameOrNull(mappingResolver, request));
- return updateContext(context, request);
- }
-
- @Override
- protected String getInstrumentationName() {
- return "io.opentelemetry.servlet-5.0";
- }
-
- @Override
- protected TextMapGetter getGetter() {
- return JakartaHttpServletRequestGetter.GETTER;
- }
-
- @Override
- protected String errorExceptionAttributeName() {
- return RequestDispatcher.ERROR_EXCEPTION;
- }
-}
diff --git a/instrumentation/servlet/servlet-5.0/library/src/test/java/JakartaServletHttpServerTracerTest.java b/instrumentation/servlet/servlet-5.0/library/src/test/java/JakartaServletHttpServerTracerTest.java
deleted file mode 100644
index 11b64d9b9242..000000000000
--- a/instrumentation/servlet/servlet-5.0/library/src/test/java/JakartaServletHttpServerTracerTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import io.opentelemetry.instrumentation.servlet.jakarta.v5_0.JakartaServletHttpServerTracer;
-import jakarta.servlet.http.HttpServletRequest;
-import org.junit.jupiter.api.Test;
-
-public class JakartaServletHttpServerTracerTest {
- private static final JakartaServletHttpServerTracer tracer =
- JakartaServletHttpServerTracer.tracer();
-
- @Test
- void testGetSpanName_emptySpanName() {
- HttpServletRequest request = mock(HttpServletRequest.class);
- when(request.getServletPath()).thenReturn("");
- when(request.getMethod()).thenReturn("PUT");
- String spanName = tracer.getSpanName(request);
- assertThat(spanName).isEqualTo("HTTP PUT");
- }
-
- @Test
- void testGetSpanName_nullSpanName() {
- HttpServletRequest request = mock(HttpServletRequest.class);
- when(request.getServletPath()).thenReturn(null);
- assertThatThrownBy(() -> tracer.getSpanName(request)).isInstanceOf(NullPointerException.class);
- }
-
- @Test
- void testGetSpanName_nullContextPath() {
- HttpServletRequest request = mock(HttpServletRequest.class);
- when(request.getServletPath()).thenReturn("/swizzler");
- when(request.getContextPath()).thenReturn(null);
- String spanName = tracer.getSpanName(request);
- assertThat(spanName).isEqualTo("/swizzler");
- }
-
- @Test
- void testGetSpanName_emptyContextPath() {
- HttpServletRequest request = mock(HttpServletRequest.class);
- when(request.getServletPath()).thenReturn("/swizzler");
- when(request.getContextPath()).thenReturn("");
- String spanName = tracer.getSpanName(request);
- assertThat(spanName).isEqualTo("/swizzler");
- }
-
- @Test
- void testGetSpanName_slashContextPath() {
- HttpServletRequest request = mock(HttpServletRequest.class);
- when(request.getServletPath()).thenReturn("/swizzler");
- when(request.getContextPath()).thenReturn("/");
- String spanName = tracer.getSpanName(request);
- assertThat(spanName).isEqualTo("/swizzler");
- }
-
- @Test
- void testGetSpanName_appendsSpanNameToContext() {
- HttpServletRequest request = mock(HttpServletRequest.class);
- when(request.getServletPath()).thenReturn("/swizzler");
- when(request.getContextPath()).thenReturn("/path/to");
- String spanName = tracer.getSpanName(request);
- assertThat(spanName).isEqualTo("/path/to/swizzler");
- }
-}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/AsyncRequestCompletionListener.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/AsyncRequestCompletionListener.java
new file mode 100644
index 000000000000..a1d3ac3839f5
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/AsyncRequestCompletionListener.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+import io.opentelemetry.context.Context;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.servlet.ServletAsyncListener;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class AsyncRequestCompletionListener
+ implements ServletAsyncListener {
+ private final ServletHelper servletHelper;
+ private final Instrumenter, ServletResponseContext>
+ instrumenter;
+ private final ServletRequestContext requestContext;
+ private final Context context;
+ private final AtomicBoolean responseHandled = new AtomicBoolean();
+
+ public AsyncRequestCompletionListener(
+ ServletHelper servletHelper,
+ Instrumenter, ServletResponseContext> instrumenter,
+ ServletRequestContext requestContext,
+ Context context) {
+ this.servletHelper = servletHelper;
+ this.instrumenter = instrumenter;
+ this.requestContext = requestContext;
+ this.context = context;
+ }
+
+ @Override
+ public void onComplete(RESPONSE response) {
+ if (responseHandled.compareAndSet(false, true)) {
+ ServletResponseContext responseContext =
+ new ServletResponseContext<>(response, null);
+ instrumenter.end(context, requestContext, responseContext, null);
+ }
+ }
+
+ @Override
+ public void onTimeout(long timeout) {
+ if (responseHandled.compareAndSet(false, true)) {
+ RESPONSE response = servletHelper.getAsyncListenerResponse(requestContext.request());
+ ServletResponseContext responseContext =
+ new ServletResponseContext<>(response, null);
+ responseContext.setTimeout(timeout);
+ instrumenter.end(context, requestContext, responseContext, null);
+ }
+ }
+
+ @Override
+ public void onError(Throwable throwable, RESPONSE response) {
+ if (responseHandled.compareAndSet(false, true)) {
+ ServletResponseContext responseContext =
+ new ServletResponseContext<>(response, throwable);
+ instrumenter.end(context, requestContext, responseContext, throwable);
+ }
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/BaseServletHelper.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/BaseServletHelper.java
new file mode 100644
index 000000000000..69c61427fbd9
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/BaseServletHelper.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.FILTER;
+import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.SERVLET;
+
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanContext;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.servlet.AppServerBridge;
+import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
+import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
+import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
+import io.opentelemetry.instrumentation.api.tracer.HttpServerTracer;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import io.opentelemetry.instrumentation.servlet.naming.ServletSpanNameProvider;
+
+public abstract class BaseServletHelper {
+ protected final Instrumenter, ServletResponseContext>
+ instrumenter;
+ protected final ServletAccessor accessor;
+ private final ServletSpanNameProvider spanNameProvider;
+
+ protected BaseServletHelper(
+ Instrumenter, ServletResponseContext> instrumenter,
+ ServletAccessor accessor) {
+ this.instrumenter = instrumenter;
+ this.accessor = accessor;
+ this.spanNameProvider = new ServletSpanNameProvider<>(accessor);
+ }
+
+ public boolean shouldStart(Context parentContext, ServletRequestContext requestContext) {
+ return instrumenter.shouldStart(parentContext, requestContext);
+ }
+
+ protected Context start(
+ Context parentContext,
+ ServletRequestContext requestContext,
+ ServerSpanNaming.Source namingSource) {
+ Context context = instrumenter.start(parentContext, requestContext);
+
+ REQUEST request = requestContext.request();
+ SpanContext spanContext = Span.fromContext(context).getSpanContext();
+ // we do this e.g. so that servlet containers can use these values in their access logs
+ // TODO: These are only available when using servlet instrumentation or when server
+ // instrumentation extends servlet instrumentation e.g. jetty. Either remove or make sure they
+ // also work on tomcat and wildfly.
+ accessor.setRequestAttribute(request, "trace_id", spanContext.getTraceId());
+ accessor.setRequestAttribute(request, "span_id", spanContext.getSpanId());
+
+ context = ServerSpanNaming.init(context, namingSource);
+ context = addServletContextPath(context, request);
+ context = customizeContext(context, request);
+
+ attachServerContext(context, request);
+
+ return context;
+ }
+
+ /** Override in subclass to customize context that is returned by {@code startSpan}. */
+ protected Context customizeContext(Context context, REQUEST request) {
+ return context;
+ }
+
+ private Context addServletContextPath(Context context, REQUEST request) {
+ String contextPath = accessor.getRequestContextPath(request);
+ if (contextPath != null && !contextPath.isEmpty() && !contextPath.equals("/")) {
+ return context.with(ServletContextPath.CONTEXT_KEY, contextPath);
+ }
+ return context;
+ }
+
+ public Context getServerContext(REQUEST request) {
+ Object context = accessor.getRequestAttribute(request, HttpServerTracer.CONTEXT_ATTRIBUTE);
+ return context instanceof Context ? (Context) context : null;
+ }
+
+ private void attachServerContext(Context context, REQUEST request) {
+ accessor.setRequestAttribute(request, HttpServerTracer.CONTEXT_ATTRIBUTE, context);
+ }
+
+ public void recordException(Context context, Throwable throwable) {
+ AppServerBridge.recordException(context, throwable);
+ }
+
+ /**
+ * When server spans are managed by app server instrumentation we need to add context path of
+ * current request to context if it isn't already added. Servlet instrumentation adds it when it
+ * starts server span.
+ */
+ public Context updateContext(Context context, REQUEST request) {
+ String contextPath = context.get(ServletContextPath.CONTEXT_KEY);
+ if (contextPath == null) {
+ context = addServletContextPath(context, request);
+ }
+
+ return context;
+ }
+
+ public Context updateContext(
+ Context context, REQUEST request, MappingResolver mappingResolver, boolean servlet) {
+ ServerSpanNaming.updateServerSpanName(
+ context,
+ servlet ? SERVLET : FILTER,
+ () -> spanNameProvider.getSpanNameOrNull(mappingResolver, request));
+ return updateContext(context, request);
+ }
+
+ /*
+ Given request already has a context associated with it.
+ As there should not be nested spans of kind SERVER, we should NOT create a new span here.
+
+ But it may happen that there is no span in current Context or it is from a different trace.
+ E.g. in case of async servlet request processing we create span for incoming request in one thread,
+ but actual request continues processing happens in another thread.
+ Depending on servlet container implementation, this processing may again arrive into this method.
+ E.g. Jetty handles async requests in a way that calls HttpServlet.service method twice.
+
+ In this case we have to put the span from the request into current context before continuing.
+ */
+ public boolean needsRescoping(Context currentContext, Context attachedContext) {
+ return !sameTrace(Span.fromContext(currentContext), Span.fromContext(attachedContext));
+ }
+
+ private static boolean sameTrace(Span oneSpan, Span otherSpan) {
+ return oneSpan.getSpanContext().getTraceId().equals(otherSpan.getSpanContext().getTraceId());
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletAdditionalAttributesExtractor.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletAdditionalAttributesExtractor.java
new file mode 100644
index 000000000000..8077bc9749a8
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletAdditionalAttributesExtractor.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+import static io.opentelemetry.api.common.AttributeKey.longKey;
+
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.common.AttributesBuilder;
+import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import io.opentelemetry.instrumentation.servlet.ServletHttpServerTracer;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+import java.security.Principal;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class ServletAdditionalAttributesExtractor
+ extends AttributesExtractor, ServletResponseContext> {
+ private static final AttributeKey SERVLET_TIMEOUT = longKey("servlet.timeout");
+
+ private final ServletAccessor accessor;
+
+ public ServletAdditionalAttributesExtractor(ServletAccessor accessor) {
+ this.accessor = accessor;
+ }
+
+ @Override
+ protected void onStart(
+ AttributesBuilder attributes, ServletRequestContext requestContext) {}
+
+ @Override
+ protected void onEnd(
+ AttributesBuilder attributes,
+ ServletRequestContext requestContext,
+ @Nullable ServletResponseContext responseContext,
+ @Nullable Throwable error) {
+ Principal principal = accessor.getRequestUserPrincipal(requestContext.request());
+ if (principal != null) {
+ set(attributes, SemanticAttributes.ENDUSER_ID, principal.getName());
+ }
+ if (!ServletHttpServerTracer.CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) {
+ return;
+ }
+ if (responseContext != null && responseContext.hasTimeout()) {
+ set(attributes, SERVLET_TIMEOUT, responseContext.getTimeout());
+ }
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletErrorCauseExtractor.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletErrorCauseExtractor.java
new file mode 100644
index 000000000000..9b7c5471db4f
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletErrorCauseExtractor.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+import io.opentelemetry.instrumentation.api.instrumenter.ErrorCauseExtractor;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+
+public class ServletErrorCauseExtractor implements ErrorCauseExtractor {
+ private final ServletAccessor accessor;
+
+ public ServletErrorCauseExtractor(ServletAccessor accessor) {
+ this.accessor = accessor;
+ }
+
+ @Override
+ public Throwable extractCause(Throwable error) {
+ if (accessor.isServletException(error) && error.getCause() != null) {
+ error = error.getCause();
+ }
+ return ErrorCauseExtractor.jdk().extractCause(error);
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHelper.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHelper.java
new file mode 100644
index 000000000000..75043e628798
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHelper.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.FILTER;
+import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.SERVLET;
+
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import io.opentelemetry.instrumentation.servlet.ServletHttpServerTracer;
+
+public class ServletHelper extends BaseServletHelper {
+
+ public ServletHelper(
+ Instrumenter, ServletResponseContext> instrumenter,
+ ServletAccessor accessor) {
+ super(instrumenter, accessor);
+ }
+
+ public Context start(
+ Context parentContext, ServletRequestContext requestContext, boolean servlet) {
+ ServerSpanNaming.Source namingSource = servlet ? SERVLET : FILTER;
+ return start(parentContext, requestContext, namingSource);
+ }
+
+ public void end(
+ ServletRequestContext requestContext,
+ REQUEST request,
+ RESPONSE response,
+ Throwable throwable,
+ boolean topLevel,
+ Context context,
+ Scope scope) {
+
+ if (scope != null) {
+ scope.close();
+ }
+
+ if (context == null && topLevel) {
+ Context currentContext = Context.current();
+ // Something else is managing the context, we're in the outermost level of Servlet
+ // instrumentation and we have an uncaught throwable. Let's add it to the current span.
+ if (throwable != null) {
+ recordException(currentContext, throwable);
+ }
+ }
+
+ if (scope == null || context == null) {
+ return;
+ }
+
+ ServletResponseContext responseContext =
+ new ServletResponseContext<>(response, throwable);
+ if (throwable != null) {
+ instrumenter.end(context, requestContext, responseContext, throwable);
+ return;
+ }
+
+ if (mustEndOnHandlerMethodExit(request)) {
+ instrumenter.end(context, requestContext, responseContext, null);
+ }
+ }
+
+ /**
+ * Helper method to determine whether the appserver handler/servlet service/servlet filter method
+ * that started a span must also end it, even if no error was detected. Extracted as a separate
+ * method to avoid duplicating the comments on the logic behind this choice.
+ */
+ public boolean mustEndOnHandlerMethodExit(REQUEST request) {
+ if (isAsyncListenerAttached(request)) {
+ // This request is handled asynchronously and startAsync instrumentation has already attached
+ // the listener.
+ return false;
+ }
+
+ // This means that startAsync was not called (assuming startAsync instrumentation works
+ // correctly on this servlet engine), therefore the request was handled synchronously, and
+ // handler method end must also end the span.
+ return true;
+ }
+
+ /**
+ * Response object must be attached to a request prior to {@link
+ * #attachAsyncListener(ServletRequestContext)} being called, as otherwise in some environments it
+ * is not possible to access response from async event in listeners.
+ */
+ public void setAsyncListenerResponse(REQUEST request, RESPONSE response) {
+ accessor.setRequestAttribute(
+ request, ServletHttpServerTracer.ASYNC_LISTENER_RESPONSE_ATTRIBUTE, response);
+ }
+
+ public RESPONSE getAsyncListenerResponse(REQUEST request) {
+ return (RESPONSE)
+ accessor.getRequestAttribute(
+ request, ServletHttpServerTracer.ASYNC_LISTENER_RESPONSE_ATTRIBUTE);
+ }
+
+ public void attachAsyncListener(REQUEST request) {
+ ServletRequestContext requestContext = new ServletRequestContext<>(request, null);
+ attachAsyncListener(requestContext);
+ }
+
+ private void attachAsyncListener(ServletRequestContext requestContext) {
+ REQUEST request = requestContext.request();
+ Context context = getServerContext(request);
+
+ if (context != null) {
+ Object response = getAsyncListenerResponse(request);
+
+ accessor.addRequestAsyncListener(
+ request,
+ new AsyncRequestCompletionListener<>(this, instrumenter, requestContext, context),
+ response);
+ accessor.setRequestAttribute(request, ServletHttpServerTracer.ASYNC_LISTENER_ATTRIBUTE, true);
+ }
+ }
+
+ public boolean isAsyncListenerAttached(REQUEST request) {
+ return accessor.getRequestAttribute(request, ServletHttpServerTracer.ASYNC_LISTENER_ATTRIBUTE)
+ != null;
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHttpAttributesExtractor.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHttpAttributesExtractor.java
new file mode 100644
index 000000000000..035d4d3aecd0
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHttpAttributesExtractor.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
+import io.opentelemetry.instrumentation.api.internal.UriBuilder;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class ServletHttpAttributesExtractor
+ extends HttpAttributesExtractor<
+ ServletRequestContext, ServletResponseContext> {
+ protected final ServletAccessor accessor;
+
+ public ServletHttpAttributesExtractor(ServletAccessor accessor) {
+ this.accessor = accessor;
+ }
+
+ @Override
+ protected @Nullable String method(ServletRequestContext requestContext) {
+ return accessor.getRequestMethod(requestContext.request());
+ }
+
+ @Override
+ protected @Nullable String url(ServletRequestContext requestContext) {
+ REQUEST request = requestContext.request();
+
+ return UriBuilder.uri(
+ accessor.getRequestScheme(request),
+ accessor.getRequestServerName(request),
+ accessor.getRequestServerPort(request),
+ accessor.getRequestUri(request),
+ accessor.getRequestQueryString(request));
+ }
+
+ @Override
+ protected @Nullable String target(ServletRequestContext requestContext) {
+ /*
+ String target = httpServletRequest.getRequestURI();
+ String queryString = httpServletRequest.getQueryString();
+ if (queryString != null) {
+ target += "?" + queryString;
+ }
+ return target;
+ */
+ return null;
+ }
+
+ @Override
+ protected @Nullable String host(ServletRequestContext requestContext) {
+ /*
+ REQUEST request = requestContext.request();
+ return accessor.getRequestServerName(request) + ":" + accessor.getRequestServerPort(request);
+ */
+ return null;
+ }
+
+ @Override
+ protected @Nullable String scheme(ServletRequestContext requestContext) {
+ // return accessor.getRequestScheme(requestContext.request());
+ return null;
+ }
+
+ @Override
+ protected @Nullable String userAgent(ServletRequestContext requestContext) {
+ return accessor.getRequestHeader(requestContext.request(), "User-Agent");
+ }
+
+ @Override
+ protected @Nullable Long requestContentLength(
+ ServletRequestContext requestContext,
+ @Nullable ServletResponseContext responseContext) {
+ /*
+ int contentLength = accessor.getRequestContentLength(requestContext.request());
+ if (contentLength > -1) {
+ return (long) contentLength;
+ }
+ */
+ return null;
+ }
+
+ @Override
+ protected @Nullable Long requestContentLengthUncompressed(
+ ServletRequestContext requestContext,
+ @Nullable ServletResponseContext responseContext) {
+ return null;
+ }
+
+ @Override
+ protected @Nullable String flavor(
+ ServletRequestContext requestContext,
+ @Nullable ServletResponseContext responseContext) {
+ String flavor = accessor.getRequestProtocol(requestContext.request());
+ if (flavor != null) {
+ // remove HTTP/ prefix to comply with semantic conventions
+ if (flavor.startsWith("HTTP/")) {
+ flavor = flavor.substring("HTTP/".length());
+ }
+ }
+ return flavor;
+ }
+
+ @Override
+ protected @Nullable Integer statusCode(
+ ServletRequestContext requestContext,
+ ServletResponseContext responseContext) {
+ RESPONSE response = responseContext.response();
+
+ if (!accessor.isResponseCommitted(response) && responseContext.error() != null) {
+ // if response is not committed and there is a throwable set status to 500 /
+ // INTERNAL_SERVER_ERROR, due to servlet spec
+ // https://javaee.github.io/servlet-spec/downloads/servlet-4.0/servlet-4_0_FINAL.pdf:
+ // "If a servlet generates an error that is not handled by the error page mechanism as
+ // described above, the container must ensure to send a response with status 500."
+ return 500;
+ }
+ return accessor.getResponseStatus(response);
+ }
+
+ @Override
+ protected @Nullable Long responseContentLength(
+ ServletRequestContext requestContext,
+ ServletResponseContext responseContext) {
+ /*
+ String contentLength = servletAccessor.getResponseHeader(responseContext.response(), "Content-Length");
+ if (contentLength != null) {
+ try {
+ return Long.valueOf(contentLength);
+ } catch (NumberFormatException ignored) {
+ // ignore
+ }
+ }
+ */
+ return null;
+ }
+
+ @Override
+ protected @Nullable Long responseContentLengthUncompressed(
+ ServletRequestContext requestContext,
+ ServletResponseContext responseContext) {
+ return null;
+ }
+
+ @Override
+ protected @Nullable String route(ServletRequestContext requestContext) {
+ return null;
+ }
+
+ @Override
+ protected @Nullable String serverName(
+ ServletRequestContext requestContext,
+ @Nullable ServletResponseContext responseContext) {
+ // return servletAccessor.getRequestServerName(requestContext.request());
+ return null;
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletInstrumenterBuilder.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletInstrumenterBuilder.java
new file mode 100644
index 000000000000..8f41ef6325bf
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletInstrumenterBuilder.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import io.opentelemetry.javaagent.instrumentation.api.instrumenter.PeerServiceAttributesExtractor;
+
+public final class ServletInstrumenterBuilder {
+
+ private ServletInstrumenterBuilder() {}
+
+ public static
+ Instrumenter, ServletResponseContext>
+ newInstrumenter(
+ String instrumentationName,
+ ServletAccessor accessor,
+ SpanNameExtractor> spanNameExtractor,
+ HttpAttributesExtractor<
+ ServletRequestContext, ServletResponseContext>
+ httpAttributesExtractor) {
+ SpanStatusExtractor, ServletResponseContext>
+ spanStatusExtractor = HttpSpanStatusExtractor.create(httpAttributesExtractor);
+ ServletNetAttributesExtractor netAttributesExtractor =
+ new ServletNetAttributesExtractor<>(accessor);
+ ServletErrorCauseExtractor errorCauseExtractor =
+ new ServletErrorCauseExtractor<>(accessor);
+ AttributesExtractor, ServletResponseContext>
+ additionalAttributesExtractor = new ServletAdditionalAttributesExtractor<>(accessor);
+
+ return Instrumenter
+ ., ServletResponseContext>newBuilder(
+ GlobalOpenTelemetry.get(), instrumentationName, spanNameExtractor)
+ .setSpanStatusExtractor(spanStatusExtractor)
+ .setErrorCauseExtractor(errorCauseExtractor)
+ .addAttributesExtractor(httpAttributesExtractor)
+ .addAttributesExtractor(netAttributesExtractor)
+ .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesExtractor))
+ .addAttributesExtractor(additionalAttributesExtractor)
+ .addRequestMetrics(HttpServerMetrics.get())
+ .newServerInstrumenter(new ServletRequestGetter<>(accessor));
+ }
+
+ public static
+ Instrumenter, ServletResponseContext>
+ newInstrumenter(String instrumentationName, ServletAccessor accessor) {
+ HttpAttributesExtractor, ServletResponseContext>
+ httpAttributesExtractor = new ServletHttpAttributesExtractor<>(accessor);
+ SpanNameExtractor> spanNameExtractor =
+ new ServletSpanNameExtractor<>(accessor);
+
+ return newInstrumenter(
+ instrumentationName, accessor, spanNameExtractor, httpAttributesExtractor);
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletNetAttributesExtractor.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletNetAttributesExtractor.java
new file mode 100644
index 000000000000..f5f03cf15b52
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletNetAttributesExtractor.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class ServletNetAttributesExtractor
+ extends NetAttributesExtractor<
+ ServletRequestContext, ServletResponseContext> {
+ private final ServletAccessor accessor;
+
+ public ServletNetAttributesExtractor(ServletAccessor accessor) {
+ this.accessor = accessor;
+ }
+
+ @Override
+ public @Nullable String transport(ServletRequestContext requestContext) {
+ // return SemanticAttributes.NetTransportValues.IP_TCP;
+ return null;
+ }
+
+ @Override
+ public @Nullable String peerName(
+ ServletRequestContext requestContext,
+ @Nullable ServletResponseContext responseContext) {
+ // return accessor.getRequestRemoteHost(requestContext.request());
+ return null;
+ }
+
+ @Override
+ public @Nullable Integer peerPort(
+ ServletRequestContext requestContext,
+ @Nullable ServletResponseContext responseContext) {
+ return accessor.getRequestRemotePort(requestContext.request());
+ }
+
+ @Override
+ public @Nullable String peerIp(
+ ServletRequestContext requestContext,
+ @Nullable ServletResponseContext responseContext) {
+ return accessor.getRequestRemoteAddr(requestContext.request());
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletRequestContext.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletRequestContext.java
new file mode 100644
index 000000000000..131bef180e00
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletRequestContext.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class ServletRequestContext {
+ private final T request;
+ private final MappingResolver mappingResolver;
+
+ public ServletRequestContext(T request) {
+ this(request, null);
+ }
+
+ public ServletRequestContext(T request, MappingResolver mappingResolver) {
+ this.request = request;
+ this.mappingResolver = mappingResolver;
+ }
+
+ public T request() {
+ return request;
+ }
+
+ @Nullable
+ public MappingResolver mappingResolver() {
+ return mappingResolver;
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletRequestGetter.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletRequestGetter.java
new file mode 100644
index 000000000000..1616c54ba899
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletRequestGetter.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+
+public class ServletRequestGetter
+ implements TextMapGetter> {
+ protected final ServletAccessor accessor;
+
+ public ServletRequestGetter(ServletAccessor accessor) {
+ this.accessor = accessor;
+ }
+
+ @Override
+ public Iterable keys(ServletRequestContext carrier) {
+ return accessor.getRequestHeaderNames(carrier.request());
+ }
+
+ @Override
+ public String get(ServletRequestContext carrier, String key) {
+ return accessor.getRequestHeader(carrier.request(), key);
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletResponseContext.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletResponseContext.java
new file mode 100644
index 000000000000..d186549b53d1
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletResponseContext.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+public class ServletResponseContext {
+ private final T response;
+ private final Throwable error;
+ // used for servlet 2.2 where request status can't be extracted from HttpServletResponse
+ private Integer status;
+ private Long timeout;
+
+ public ServletResponseContext(T response, Throwable error) {
+ this.response = response;
+ this.error = error;
+ }
+
+ public T response() {
+ return response;
+ }
+
+ public Throwable error() {
+ return error;
+ }
+
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public boolean hasStatus() {
+ return status != null;
+ }
+
+ public void setTimeout(long timeout) {
+ this.timeout = timeout;
+ }
+
+ public long getTimeout() {
+ return timeout;
+ }
+
+ public boolean hasTimeout() {
+ return timeout != null;
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletSpanNameExtractor.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletSpanNameExtractor.java
new file mode 100644
index 000000000000..457d215b7097
--- /dev/null
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletSpanNameExtractor.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.servlet;
+
+import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
+import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class ServletSpanNameExtractor
+ implements SpanNameExtractor> {
+ private final ServletAccessor accessor;
+
+ public ServletSpanNameExtractor(ServletAccessor accessor) {
+ this.accessor = accessor;
+ }
+
+ private @Nullable String route(ServletRequestContext requestContext) {
+ MappingResolver mappingResolver = requestContext.mappingResolver();
+ if (mappingResolver == null) {
+ return null;
+ }
+
+ REQUEST request = requestContext.request();
+ String servletPath = accessor.getRequestServletPath(request);
+ String pathInfo = accessor.getRequestPathInfo(request);
+ String contextPath = accessor.getRequestContextPath(request);
+ boolean hasContextPath =
+ contextPath != null && !contextPath.isEmpty() && !contextPath.equals("/");
+
+ String route = requestContext.mappingResolver().resolve(servletPath, pathInfo);
+ if (route == null) {
+ if (hasContextPath) {
+ return contextPath + "/*";
+ }
+ return null;
+ }
+ // prepend context path
+ return contextPath + route;
+ }
+
+ @Override
+ public String extract(ServletRequestContext requestContext) {
+ String route = route(requestContext);
+ if (route != null) {
+ return route;
+ }
+ REQUEST request = requestContext.request();
+ String method = accessor.getRequestMethod(request);
+ if (method != null) {
+ return "HTTP " + method;
+ }
+ return "HTTP request";
+ }
+}
diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/service/ServletAndFilterAdviceHelper.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/service/ServletAndFilterAdviceHelper.java
index fb7e1f12fb9c..503ddfcb34ac 100644
--- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/service/ServletAndFilterAdviceHelper.java
+++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/service/ServletAndFilterAdviceHelper.java
@@ -10,6 +10,7 @@
import io.opentelemetry.instrumentation.servlet.ServletHttpServerTracer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
+@Deprecated
public class ServletAndFilterAdviceHelper {
public static void stopSpan(
ServletHttpServerTracer tracer,
diff --git a/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/ServletAccessor.java b/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/ServletAccessor.java
index ff10d69402b8..87ce609b62f0 100644
--- a/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/ServletAccessor.java
+++ b/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/ServletAccessor.java
@@ -42,6 +42,8 @@ public interface ServletAccessor {
String getRequestHeader(REQUEST request, String name);
+ Iterable getRequestHeaderNames(REQUEST request);
+
String getRequestServletPath(REQUEST request);
String getRequestPathInfo(REQUEST request);
@@ -50,11 +52,17 @@ public interface ServletAccessor {
Integer getRequestRemotePort(REQUEST request);
+ String getRequestRemoteHost(REQUEST request);
+
+ int getRequestContentLength(REQUEST request);
+
void addRequestAsyncListener(
REQUEST request, ServletAsyncListener listener, Object response);
int getResponseStatus(RESPONSE response);
+ String getResponseHeader(RESPONSE response, String name);
+
boolean isResponseCommitted(RESPONSE response);
boolean isServletException(Throwable throwable);
diff --git a/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/ServletHttpServerTracer.java b/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/ServletHttpServerTracer.java
index 2b3f07862979..6f9988cab87b 100644
--- a/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/ServletHttpServerTracer.java
+++ b/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/ServletHttpServerTracer.java
@@ -27,6 +27,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+@Deprecated
public abstract class ServletHttpServerTracer
extends HttpServerTracer {
@@ -37,7 +38,7 @@ public abstract class ServletHttpServerTracer
public static final String ASYNC_LISTENER_RESPONSE_ATTRIBUTE =
ServletHttpServerTracer.class.getName() + ".AsyncListenerResponse";
- private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES =
+ public static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES =
Config.get().getBoolean("otel.instrumentation.servlet.experimental-span-attributes", false);
private final ServletAccessor accessor;
diff --git a/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/TagSettingAsyncListener.java b/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/TagSettingAsyncListener.java
index 4597609e423c..088778bbebf9 100644
--- a/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/TagSettingAsyncListener.java
+++ b/instrumentation/servlet/servlet-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/TagSettingAsyncListener.java
@@ -8,6 +8,7 @@
import io.opentelemetry.context.Context;
import java.util.concurrent.atomic.AtomicBoolean;
+@Deprecated
public class TagSettingAsyncListener implements ServletAsyncListener {
private final ServletHttpServerTracer tracer;
private final AtomicBoolean responseHandled;
diff --git a/instrumentation/servlet/servlet-javax-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/javax/JavaxServletAccessor.java b/instrumentation/servlet/servlet-javax-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/javax/JavaxServletAccessor.java
index f1310f9a3522..c92253ce8835 100644
--- a/instrumentation/servlet/servlet-javax-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/javax/JavaxServletAccessor.java
+++ b/instrumentation/servlet/servlet-javax-common/library/src/main/java/io/opentelemetry/instrumentation/servlet/javax/JavaxServletAccessor.java
@@ -7,6 +7,7 @@
import io.opentelemetry.instrumentation.servlet.ServletAccessor;
import java.security.Principal;
+import java.util.Collections;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -66,11 +67,21 @@ public String getRequestRemoteAddr(HttpServletRequest request) {
return request.getRemoteAddr();
}
+ @Override
+ public String getRequestRemoteHost(HttpServletRequest httpServletRequest) {
+ return httpServletRequest.getRemoteHost();
+ }
+
@Override
public String getRequestHeader(HttpServletRequest request, String name) {
return request.getHeader(name);
}
+ @Override
+ public Iterable getRequestHeaderNames(HttpServletRequest httpServletRequest) {
+ return Collections.list(httpServletRequest.getHeaderNames());
+ }
+
@Override
public String getRequestServletPath(HttpServletRequest request) {
return request.getServletPath();
@@ -86,6 +97,11 @@ public Principal getRequestUserPrincipal(HttpServletRequest request) {
return request.getUserPrincipal();
}
+ @Override
+ public int getRequestContentLength(HttpServletRequest request) {
+ return request.getContentLength();
+ }
+
@Override
public boolean isServletException(Throwable throwable) {
return throwable instanceof ServletException;
diff --git a/instrumentation/servlet/servlet-javax-common/library/src/test/java/RequestOnlyTracer.java b/instrumentation/servlet/servlet-javax-common/library/src/test/java/RequestOnlyTracer.java
index c493437e22b7..f8191b20a561 100644
--- a/instrumentation/servlet/servlet-javax-common/library/src/test/java/RequestOnlyTracer.java
+++ b/instrumentation/servlet/servlet-javax-common/library/src/test/java/RequestOnlyTracer.java
@@ -28,6 +28,11 @@ public int getResponseStatus(Void unused) {
throw new UnsupportedOperationException();
}
+ @Override
+ public String getResponseHeader(Void unused, String name) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public boolean isResponseCommitted(Void unused) {
throw new UnsupportedOperationException();
diff --git a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10AttachResponseAdvice.java b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10AttachResponseAdvice.java
index 50ca5b66d2f3..a83c5da92e10 100644
--- a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10AttachResponseAdvice.java
+++ b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10AttachResponseAdvice.java
@@ -5,8 +5,8 @@
package io.opentelemetry.javaagent.instrumentation.tomcat.v10_0;
-import io.opentelemetry.instrumentation.servlet.jakarta.v5_0.JakartaServletHttpServerTracer;
-import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatServerHandlerAdviceHelper;
+import static io.opentelemetry.javaagent.instrumentation.tomcat.v10_0.Tomcat10Singletons.helper;
+
import net.bytebuddy.asm.Advice;
import org.apache.coyote.Request;
import org.apache.coyote.Response;
@@ -20,11 +20,7 @@ public static void attachResponse(
@Advice.Return boolean success) {
if (success) {
- TomcatServerHandlerAdviceHelper.attachResponseToRequest(
- Tomcat10ServletEntityProvider.INSTANCE,
- JakartaServletHttpServerTracer.tracer(),
- request,
- response);
+ helper().attachResponseToRequest(request, response);
}
}
}
diff --git a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10ServerHandlerAdvice.java b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10ServerHandlerAdvice.java
index 23435036bd58..7303c263aa1b 100644
--- a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10ServerHandlerAdvice.java
+++ b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10ServerHandlerAdvice.java
@@ -5,12 +5,11 @@
package io.opentelemetry.javaagent.instrumentation.tomcat.v10_0;
-import static io.opentelemetry.javaagent.instrumentation.tomcat.v10_0.Tomcat10Tracer.tracer;
+import static io.opentelemetry.javaagent.instrumentation.tomcat.v10_0.Tomcat10Singletons.helper;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
-import io.opentelemetry.instrumentation.servlet.jakarta.v5_0.JakartaServletHttpServerTracer;
-import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatServerHandlerAdviceHelper;
+import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import net.bytebuddy.asm.Advice;
import org.apache.coyote.Request;
import org.apache.coyote.Response;
@@ -24,11 +23,13 @@ public static void onEnter(
@Advice.Argument(1) Response response,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
- if (!tracer().shouldStartSpan(request)) {
+
+ Context parentContext = Java8BytecodeBridge.currentContext();
+ if (!helper().shouldStart(parentContext, request)) {
return;
}
- context = tracer().startServerSpan(request);
+ context = helper().start(parentContext, request);
scope = context.makeCurrent();
}
@@ -41,14 +42,6 @@ public static void stopSpan(
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
- TomcatServerHandlerAdviceHelper.stopSpan(
- tracer(),
- Tomcat10ServletEntityProvider.INSTANCE,
- JakartaServletHttpServerTracer.tracer(),
- request,
- response,
- throwable,
- context,
- scope);
+ helper().end(request, response, throwable, context, scope);
}
}
diff --git a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10Singletons.java b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10Singletons.java
new file mode 100644
index 000000000000..0e4b62fcbc0b
--- /dev/null
+++ b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10Singletons.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.tomcat.v10_0;
+
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.Servlet5Accessor;
+import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.Servlet5Singletons;
+import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatHelper;
+import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatInstrumenterBuilder;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+
+public final class Tomcat10Singletons {
+ private static final String INSTRUMENTATION_NAME = "io.opentelemetry.tomcat-10.0";
+ private static final Instrumenter INSTRUMENTER =
+ TomcatInstrumenterBuilder.newInstrumenter(
+ INSTRUMENTATION_NAME, Servlet5Accessor.INSTANCE, Tomcat10ServletEntityProvider.INSTANCE);
+ private static final TomcatHelper HELPER =
+ new TomcatHelper<>(
+ INSTRUMENTER, Tomcat10ServletEntityProvider.INSTANCE, Servlet5Singletons.helper());
+
+ public static TomcatHelper helper() {
+ return HELPER;
+ }
+
+ private Tomcat10Singletons() {}
+}
diff --git a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10Tracer.java b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10Tracer.java
deleted file mode 100644
index 2bf2e151d8a9..000000000000
--- a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10Tracer.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.instrumentation.tomcat.v10_0;
-
-import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatTracer;
-
-public class Tomcat10Tracer extends TomcatTracer {
- private static final Tomcat10Tracer TRACER = new Tomcat10Tracer();
-
- public static TomcatTracer tracer() {
- return TRACER;
- }
-
- @Override
- protected String getInstrumentationName() {
- return "io.opentelemetry.tomcat-10.0";
- }
-}
diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7AttachResponseAdvice.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7AttachResponseAdvice.java
index 229fd397d1da..22dfac2dd3cb 100644
--- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7AttachResponseAdvice.java
+++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7AttachResponseAdvice.java
@@ -5,8 +5,8 @@
package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0;
-import io.opentelemetry.instrumentation.servlet.v3_0.Servlet3HttpServerTracer;
-import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatServerHandlerAdviceHelper;
+import static io.opentelemetry.javaagent.instrumentation.tomcat.v7_0.Tomcat7Singletons.helper;
+
import net.bytebuddy.asm.Advice;
import org.apache.coyote.Request;
import org.apache.coyote.Response;
@@ -20,11 +20,7 @@ public static void attachResponse(
@Advice.Return boolean success) {
if (success) {
- TomcatServerHandlerAdviceHelper.attachResponseToRequest(
- Tomcat7ServletEntityProvider.INSTANCE,
- Servlet3HttpServerTracer.tracer(),
- request,
- response);
+ helper().attachResponseToRequest(request, response);
}
}
}
diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7ServerHandlerAdvice.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7ServerHandlerAdvice.java
index de6c47efe6a1..437e6ff37636 100644
--- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7ServerHandlerAdvice.java
+++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7ServerHandlerAdvice.java
@@ -5,12 +5,11 @@
package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0;
-import static io.opentelemetry.javaagent.instrumentation.tomcat.v7_0.Tomcat7Tracer.tracer;
+import static io.opentelemetry.javaagent.instrumentation.tomcat.v7_0.Tomcat7Singletons.helper;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
-import io.opentelemetry.instrumentation.servlet.v3_0.Servlet3HttpServerTracer;
-import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatServerHandlerAdviceHelper;
+import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import net.bytebuddy.asm.Advice;
import org.apache.coyote.Request;
import org.apache.coyote.Response;
@@ -24,11 +23,13 @@ public static void onEnter(
@Advice.Argument(1) Response response,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
- if (!tracer().shouldStartSpan(request)) {
+
+ Context parentContext = Java8BytecodeBridge.currentContext();
+ if (!helper().shouldStart(parentContext, request)) {
return;
}
- context = tracer().startServerSpan(request);
+ context = helper().start(parentContext, request);
scope = context.makeCurrent();
}
@@ -41,14 +42,6 @@ public static void stopSpan(
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
- TomcatServerHandlerAdviceHelper.stopSpan(
- tracer(),
- Tomcat7ServletEntityProvider.INSTANCE,
- Servlet3HttpServerTracer.tracer(),
- request,
- response,
- throwable,
- context,
- scope);
+ helper().end(request, response, throwable, context, scope);
}
}
diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7Singletons.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7Singletons.java
new file mode 100644
index 000000000000..17b227633d88
--- /dev/null
+++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7Singletons.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0;
+
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.servlet.v3_0.Servlet3Accessor;
+import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons;
+import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatHelper;
+import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatInstrumenterBuilder;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+
+public final class Tomcat7Singletons {
+ private static final String INSTRUMENTATION_NAME = "io.opentelemetry.tomcat-7.0";
+ private static final Instrumenter INSTRUMENTER =
+ TomcatInstrumenterBuilder.newInstrumenter(
+ INSTRUMENTATION_NAME, Servlet3Accessor.INSTANCE, Tomcat7ServletEntityProvider.INSTANCE);
+ private static final TomcatHelper HELPER =
+ new TomcatHelper<>(
+ INSTRUMENTER, Tomcat7ServletEntityProvider.INSTANCE, Servlet3Singletons.helper());
+
+ public static TomcatHelper helper() {
+ return HELPER;
+ }
+
+ private Tomcat7Singletons() {}
+}
diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7Tracer.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7Tracer.java
deleted file mode 100644
index b011db9195ec..000000000000
--- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7Tracer.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0;
-
-import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatTracer;
-
-public class Tomcat7Tracer extends TomcatTracer {
- private static final Tomcat7Tracer TRACER = new Tomcat7Tracer();
-
- public static TomcatTracer tracer() {
- return TRACER;
- }
-
- @Override
- protected String getInstrumentationName() {
- return "io.opentelemetry.tomcat-7.0";
- }
-}
diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatAdditionalAttributesExtractor.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatAdditionalAttributesExtractor.java
new file mode 100644
index 000000000000..1b4b80230dcb
--- /dev/null
+++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatAdditionalAttributesExtractor.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.tomcat.common;
+
+import io.opentelemetry.api.common.AttributesBuilder;
+import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+import java.security.Principal;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class TomcatAdditionalAttributesExtractor
+ extends AttributesExtractor {
+ private final ServletAccessor accessor;
+ private final TomcatServletEntityProvider servletEntityProvider;
+
+ public TomcatAdditionalAttributesExtractor(
+ ServletAccessor accessor,
+ TomcatServletEntityProvider servletEntityProvider) {
+ this.accessor = accessor;
+ this.servletEntityProvider = servletEntityProvider;
+ }
+
+ @Override
+ protected void onStart(AttributesBuilder attributes, Request request) {}
+
+ @Override
+ protected void onEnd(
+ AttributesBuilder attributes,
+ Request request,
+ @Nullable Response response,
+ @Nullable Throwable error) {
+ REQUEST servletRequest = servletEntityProvider.getServletRequest(request);
+ Principal principal = accessor.getRequestUserPrincipal(servletRequest);
+ if (principal != null) {
+ set(attributes, SemanticAttributes.ENDUSER_ID, principal.getName());
+ }
+ }
+}
diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHelper.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHelper.java
new file mode 100644
index 000000000000..4b513eb1dd4f
--- /dev/null
+++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHelper.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.tomcat.common;
+
+import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTAINER;
+
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.servlet.AppServerBridge;
+import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+
+public class TomcatHelper {
+ protected final Instrumenter instrumenter;
+ protected final TomcatServletEntityProvider servletEntityProvider;
+ private final ServletHelper servletHelper;
+
+ public TomcatHelper(
+ Instrumenter instrumenter,
+ TomcatServletEntityProvider servletEntityProvider,
+ ServletHelper servletHelper) {
+ this.instrumenter = instrumenter;
+ this.servletEntityProvider = servletEntityProvider;
+ this.servletHelper = servletHelper;
+ }
+
+ public boolean shouldStart(Context parentContext, Request request) {
+ return instrumenter.shouldStart(parentContext, request);
+ }
+
+ public Context start(Context parentContext, Request request) {
+ Context context = instrumenter.start(parentContext, request);
+
+ context = ServerSpanNaming.init(context, CONTAINER);
+ return AppServerBridge.init(context);
+ }
+
+ public void end(
+ Request request, Response response, Throwable throwable, Context context, Scope scope) {
+ if (scope == null) {
+ return;
+ }
+ scope.close();
+
+ if (throwable == null) {
+ throwable = AppServerBridge.getException(context);
+ }
+
+ if (throwable != null || mustEndOnHandlerMethodExit(request)) {
+ instrumenter.end(context, request, response, throwable);
+ }
+ }
+
+ private boolean mustEndOnHandlerMethodExit(Request request) {
+ REQUEST servletRequest = servletEntityProvider.getServletRequest(request);
+ return servletRequest != null && servletHelper.mustEndOnHandlerMethodExit(servletRequest);
+ }
+
+ public void attachResponseToRequest(Request request, Response response) {
+ REQUEST servletRequest = servletEntityProvider.getServletRequest(request);
+ RESPONSE servletResponse = servletEntityProvider.getServletResponse(response);
+
+ if (servletRequest != null && servletResponse != null) {
+ servletHelper.setAsyncListenerResponse(servletRequest, servletResponse);
+ }
+ }
+}
diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHttpAttributesExtractor.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHttpAttributesExtractor.java
new file mode 100644
index 000000000000..aa6b8c332c16
--- /dev/null
+++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHttpAttributesExtractor.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.tomcat.common;
+
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
+import io.opentelemetry.instrumentation.api.internal.UriBuilder;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class TomcatHttpAttributesExtractor extends HttpAttributesExtractor {
+
+ @Override
+ protected String method(Request request) {
+ return request.method().toString();
+ }
+
+ @Override
+ protected String url(Request request) {
+ MessageBytes schemeMessageBytes = request.scheme();
+ String scheme = schemeMessageBytes.isNull() ? "http" : schemeMessageBytes.toString();
+ String host = request.serverName().toString();
+ int serverPort = request.getServerPort();
+ String path = request.requestURI().toString();
+ String query = request.queryString().toString();
+
+ return UriBuilder.uri(scheme, host, serverPort, path, query);
+ }
+
+ @Override
+ protected @Nullable String target(Request request) {
+ return null;
+ }
+
+ @Override
+ protected @Nullable String host(Request request) {
+ // return request.serverName().toString() + ":" + request.getServerPort();
+ return null;
+ }
+
+ @Override
+ protected @Nullable String scheme(Request request) {
+ /*
+ MessageBytes schemeMessageBytes = request.scheme();
+ return schemeMessageBytes.isNull() ? "http" : schemeMessageBytes.toString();
+ */
+ return null;
+ }
+
+ @Override
+ protected @Nullable String userAgent(Request request) {
+ return request.getHeader("User-Agent");
+ }
+
+ @Override
+ protected @Nullable Long requestContentLength(Request request, @Nullable Response response) {
+ /*
+ long contentLength = request.getContentLengthLong();
+ return contentLength != -1 ? contentLength : null;
+ */
+ return null;
+ }
+
+ @Override
+ protected @Nullable Long requestContentLengthUncompressed(
+ Request request, @Nullable Response response) {
+ return null;
+ }
+
+ @Override
+ protected @Nullable String flavor(Request request, @Nullable Response response) {
+ String flavor = request.protocol().toString();
+ if (flavor != null) {
+ // remove HTTP/ prefix to comply with semantic conventions
+ if (flavor.startsWith("HTTP/")) {
+ flavor = flavor.substring("HTTP/".length());
+ }
+ }
+ return flavor;
+ }
+
+ @Override
+ protected @Nullable Integer statusCode(Request request, Response response) {
+ return response.getStatus();
+ }
+
+ @Override
+ protected @Nullable Long responseContentLength(Request request, Response response) {
+ /*
+ long contentLength = response.getContentLengthLong();
+ return contentLength != -1 ? contentLength : null;
+ */
+ return null;
+ }
+
+ @Override
+ protected @Nullable Long responseContentLengthUncompressed(Request request, Response response) {
+ return null;
+ }
+
+ @Override
+ protected @Nullable String route(Request request) {
+ return null;
+ }
+
+ @Override
+ protected @Nullable String serverName(Request request, @Nullable Response response) {
+ // return request.serverName().toString();
+ return null;
+ }
+}
diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumenterBuilder.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumenterBuilder.java
new file mode 100644
index 000000000000..dd0b55994369
--- /dev/null
+++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumenterBuilder.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.tomcat.common;
+
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor;
+import io.opentelemetry.instrumentation.servlet.ServletAccessor;
+import io.opentelemetry.javaagent.instrumentation.api.instrumenter.PeerServiceAttributesExtractor;
+import io.opentelemetry.javaagent.instrumentation.servlet.ServletErrorCauseExtractor;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+
+public final class TomcatInstrumenterBuilder {
+
+ private TomcatInstrumenterBuilder() {}
+
+ public static Instrumenter newInstrumenter(
+ String instrumentationName,
+ ServletAccessor accessor,
+ TomcatServletEntityProvider servletEntityProvider) {
+ HttpAttributesExtractor httpAttributesExtractor =
+ new TomcatHttpAttributesExtractor();
+ SpanNameExtractor spanNameExtractor =
+ HttpSpanNameExtractor.create(httpAttributesExtractor);
+ SpanStatusExtractor spanStatusExtractor =
+ HttpSpanStatusExtractor.create(httpAttributesExtractor);
+ NetAttributesExtractor netAttributesExtractor =
+ new TomcatNetAttributesExtractor();
+ AttributesExtractor additionalAttributeExtractor =
+ new TomcatAdditionalAttributesExtractor<>(accessor, servletEntityProvider);
+
+ return Instrumenter.newBuilder(
+ GlobalOpenTelemetry.get(), instrumentationName, spanNameExtractor)
+ .setSpanStatusExtractor(spanStatusExtractor)
+ .setErrorCauseExtractor(new ServletErrorCauseExtractor<>(accessor))
+ .addAttributesExtractor(httpAttributesExtractor)
+ .addAttributesExtractor(netAttributesExtractor)
+ .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesExtractor))
+ .addAttributesExtractor(additionalAttributeExtractor)
+ .addRequestMetrics(HttpServerMetrics.get())
+ .newServerInstrumenter(TomcatRequestGetter.GETTER);
+ }
+}
diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatNetAttributesExtractor.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatNetAttributesExtractor.java
new file mode 100644
index 000000000000..4552f2d58284
--- /dev/null
+++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatNetAttributesExtractor.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.tomcat.common;
+
+import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class TomcatNetAttributesExtractor extends NetAttributesExtractor {
+
+ @Override
+ public @Nullable String transport(Request request) {
+ // return SemanticAttributes.NetTransportValues.IP_TCP;
+ return null;
+ }
+
+ @Override
+ public @Nullable String peerName(Request request, @Nullable Response response) {
+ /*
+ request.action(ActionCode.REQ_HOST_ATTRIBUTE, request);
+ return request.remoteHost().toString();
+ */
+ return null;
+ }
+
+ @Override
+ public @Nullable Integer peerPort(Request request, @Nullable Response response) {
+ request.action(ActionCode.REQ_REMOTEPORT_ATTRIBUTE, request);
+ return request.getRemotePort();
+ }
+
+ @Override
+ public @Nullable String peerIp(Request request, @Nullable Response response) {
+ request.action(ActionCode.REQ_HOST_ADDR_ATTRIBUTE, request);
+ return request.remoteAddr().toString();
+ }
+}
diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatRequestGetter.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatRequestGetter.java
new file mode 100644
index 000000000000..1162391148c8
--- /dev/null
+++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatRequestGetter.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.tomcat.common;
+
+import io.opentelemetry.context.propagation.TextMapGetter;
+import java.util.Collections;
+import org.apache.coyote.Request;
+
+public class TomcatRequestGetter implements TextMapGetter {
+
+ public static final TomcatRequestGetter GETTER = new TomcatRequestGetter();
+
+ @Override
+ public Iterable keys(Request carrier) {
+ return Collections.list(carrier.getMimeHeaders().names());
+ }
+
+ @Override
+ public String get(Request carrier, String key) {
+ return carrier.getHeader(key);
+ }
+}
diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatServerHandlerAdviceHelper.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatServerHandlerAdviceHelper.java
deleted file mode 100644
index 5583e137fb4e..000000000000
--- a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatServerHandlerAdviceHelper.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.instrumentation.tomcat.common;
-
-import io.opentelemetry.context.Context;
-import io.opentelemetry.context.Scope;
-import io.opentelemetry.instrumentation.servlet.ServletHttpServerTracer;
-import io.opentelemetry.javaagent.instrumentation.servlet.common.service.ServletAndFilterAdviceHelper;
-import org.apache.coyote.Request;
-import org.apache.coyote.Response;
-
-public class TomcatServerHandlerAdviceHelper {
- /**
- * Shared stop method used by advices for different Tomcat versions.
- *
- * @param tracer Tracer for non-async path (uses Tomcat Coyote request/response)
- * @param servletTracer Tracer for async path (uses servlet request/response)
- * @param request Tomcat Coyote request object
- * @param response Tomcat Coyote request object
- * @param HttpServletRequest class
- * @param HttpServletResponse class
- */
- public static void stopSpan(
- TomcatTracer tracer,
- TomcatServletEntityProvider servletEntityProvider,
- ServletHttpServerTracer servletTracer,
- Request request,
- Response response,
- Throwable throwable,
- Context context,
- Scope scope) {
- if (scope != null) {
- scope.close();
- }
-
- if (context == null) {
- return;
- }
-
- if (throwable != null) {
- if (response.isCommitted()) {
- tracer.endExceptionally(context, throwable, response);
- } else {
- // If the response is not committed, then response headers, including response code, are
- // not yet written to the output stream.
- tracer.endExceptionally(context, throwable);
- }
- return;
- }
-
- if (response.isCommitted()) {
- tracer.end(context, response);
- return;
- }
-
- REQUEST servletRequest = servletEntityProvider.getServletRequest(request);
-
- if (servletRequest != null
- && ServletAndFilterAdviceHelper.mustEndOnHandlerMethodExit(servletTracer, servletRequest)) {
- tracer.end(context, response);
- }
- }
-
- /**
- * Must be attached in Tomcat instrumentations since Tomcat valves can use startAsync outside of
- * servlet scope.
- */
- public static void attachResponseToRequest(
- TomcatServletEntityProvider servletEntityProvider,
- ServletHttpServerTracer servletTracer,
- Request request,
- Response response) {
-
- REQUEST servletRequest = servletEntityProvider.getServletRequest(request);
- RESPONSE servletResponse = servletEntityProvider.getServletResponse(response);
-
- if (servletRequest != null && servletResponse != null) {
- servletTracer.setAsyncListenerResponse(servletRequest, servletResponse);
- }
- }
-}
diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatTracer.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatTracer.java
deleted file mode 100644
index fd81f61395d8..000000000000
--- a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatTracer.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.instrumentation.tomcat.common;
-
-import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTAINER;
-
-import io.opentelemetry.context.Context;
-import io.opentelemetry.context.propagation.TextMapGetter;
-import io.opentelemetry.instrumentation.api.internal.UriBuilder;
-import io.opentelemetry.instrumentation.api.servlet.AppServerBridge;
-import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
-import io.opentelemetry.instrumentation.api.tracer.HttpServerTracer;
-import java.util.Collections;
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.Request;
-import org.apache.coyote.Response;
-import org.apache.tomcat.util.buf.MessageBytes;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Abstract tracer for all Tomcat versions. This class must not access any methods of fields of
- * Tomcat classes which have the javax.servlet/jakarta.servlet
packages in their
- * signature - these must only be accessed by the version-specific subclasses.
- */
-public abstract class TomcatTracer extends HttpServerTracer
- implements TextMapGetter {
-
- private static final Logger logger = LoggerFactory.getLogger(TomcatTracer.class);
-
- public boolean shouldStartSpan(Request request) {
- Context attachedContext = getServerContext(request);
- if (attachedContext == null) {
- return true;
- }
- logger.debug(
- "Unexpected context found before server handler even started: {}", attachedContext);
- return false;
- }
-
- public Context startServerSpan(Request request) {
- return startSpan(request, request, request, "HTTP " + request.method().toString());
- }
-
- @Override
- protected Context customizeContext(Context context, Request request) {
- context = ServerSpanNaming.init(context, CONTAINER);
- return AppServerBridge.init(context);
- }
-
- @Override
- public Context getServerContext(Request storage) {
- Object attribute = storage.getAttribute(CONTEXT_ATTRIBUTE);
- return attribute instanceof Context ? (Context) attribute : null;
- }
-
- @Override
- protected Integer peerPort(Request connection) {
- connection.action(ActionCode.REQ_REMOTEPORT_ATTRIBUTE, connection);
- return connection.getRemotePort();
- }
-
- @Override
- protected String peerHostIp(Request connection) {
- connection.action(ActionCode.REQ_HOST_ADDR_ATTRIBUTE, connection);
- return connection.remoteAddr().toString();
- }
-
- @Override
- protected String flavor(Request connection, Request request) {
- return request.protocol().toString();
- }
-
- @Override
- protected TextMapGetter getGetter() {
- return this;
- }
-
- @Override
- protected String url(Request request) {
- MessageBytes schemeMessageBytes = request.scheme();
- String scheme = schemeMessageBytes.isNull() ? "http" : schemeMessageBytes.toString();
- String host = request.serverName().toString();
- int serverPort = request.getServerPort();
- String path = request.requestURI().toString();
- String query = request.queryString().toString();
-
- return UriBuilder.uri(scheme, host, serverPort, path, query);
- }
-
- @Override
- protected String method(Request request) {
- return request.method().toString();
- }
-
- @Override
- protected String requestHeader(Request request, String name) {
- return request.getHeader(name);
- }
-
- @Override
- protected int responseStatus(Response response) {
- return response.getStatus();
- }
-
- @Override
- protected void attachServerContext(Context context, Request storage) {
- storage.setAttribute(CONTEXT_ATTRIBUTE, context);
- }
-
- @Override
- public Iterable keys(Request request) {
- return Collections.list(request.getMimeHeaders().names());
- }
-
- @Override
- public String get(Request request, String key) {
- return request.getHeader(key);
- }
-}
diff --git a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpServerTracer.java b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpServerTracer.java
index 311e74c4e212..fbb7621e68eb 100644
--- a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpServerTracer.java
+++ b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpServerTracer.java
@@ -74,6 +74,10 @@ public void exchangeCompleted(Context context, HttpServerExchange exchange) {
}
private static void endSpan(Context context, Throwable throwable, HttpServerExchange exchange) {
+ if (throwable == null) {
+ throwable = AppServerBridge.getException(context);
+ }
+
if (throwable != null) {
tracer().endExceptionally(context, throwable, exchange);
} else {
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 8f9bcba4a797..75053c1f4e4d 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -298,11 +298,9 @@ include(":instrumentation:servlet:servlet-common:library")
include(":instrumentation:servlet:servlet-common:javaagent")
include(":instrumentation:servlet:servlet-javax-common:library")
include(":instrumentation:servlet:servlet-javax-common:javaagent")
-include(":instrumentation:servlet:servlet-2.2:library")
include(":instrumentation:servlet:servlet-2.2:javaagent")
include(":instrumentation:servlet:servlet-3.0:library")
include(":instrumentation:servlet:servlet-3.0:javaagent")
-include(":instrumentation:servlet:servlet-5.0:library")
include(":instrumentation:servlet:servlet-5.0:javaagent")
include(":instrumentation:spark-2.3:javaagent")
include(":instrumentation:spring:spring-batch-3.0:javaagent")