diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/InitServiceNameInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/InitServiceNameInstrumentation.java
index 4d9ef40f39..7238e21be0 100644
--- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/InitServiceNameInstrumentation.java
+++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/InitServiceNameInstrumentation.java
@@ -45,6 +45,8 @@
*
{@link jakarta.servlet.Filter#init(jakarta.servlet.FilterConfig)}
* {@link javax.servlet.Servlet#init(javax.servlet.ServletConfig)}
* {@link jakarta.servlet.Servlet#init(jakarta.servlet.ServletConfig)}
+ * {@link javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)}
+ * {@link jakarta.servlet.ServletContextListener#contextInitialized(jakarta.servlet.ServletContextEvent)}
*
*
* Determines the service name based on the webapp's {@code META-INF/MANIFEST.MF} file early in the startup process.
@@ -54,19 +56,24 @@ public abstract class InitServiceNameInstrumentation extends AbstractServletInst
@Override
public ElementMatcher super NamedElement> getTypeMatcherPreFilter() {
- return nameContains("Filter").or(nameContains("Servlet"));
+ return nameContains("Filter").or(nameContains("Servlet")).or(nameContains("Listener"));
}
@Override
public ElementMatcher super TypeDescription> getTypeMatcher() {
- return not(isInterface()).and(hasSuperType(namedOneOf("javax.servlet.Filter", "javax.servlet.Servlet", "jakarta.servlet.Filter", "jakarta.servlet.Servlet")));
+ return not(isInterface()).and(hasSuperType(namedOneOf(
+ "javax.servlet.ServletContextListener", "javax.servlet.Filter", "javax.servlet.Servlet",
+ "jakarta.servlet.ServletContextListener", "jakarta.servlet.Filter", "jakarta.servlet.Servlet")));
}
@Override
public ElementMatcher super MethodDescription> getMethodMatcher() {
return named("init")
.and(takesArguments(1))
- .and(takesArgument(0, nameEndsWith("Config")));
+ .and(takesArgument(0, nameEndsWith("Config")))
+ .or(named("contextInitialized")
+ .and(takesArguments(1))
+ .and(takesArgument(0, nameEndsWith("ServletContextEvent"))));
}
public static class JavaxInitServiceNameInstrumentation extends InitServiceNameInstrumentation {
@@ -80,15 +87,14 @@ public String rootClassNameThatClassloaderCanLoad() {
public static class AdviceClass {
@Advice.OnMethodEnter(suppress = Throwable.class, inline = false)
- public static void onEnter(@Advice.Argument(0) @Nullable Object config) {
- if (config == null) {
- return;
- }
+ public static void onEnter(@Advice.Argument(0) @Nullable Object arg) {
javax.servlet.ServletContext servletContext;
- if (config instanceof javax.servlet.FilterConfig) {
- servletContext = adapter.getServletContextFromFilterConfig((javax.servlet.FilterConfig) config);
- } else if (config instanceof javax.servlet.ServletConfig) {
- servletContext = adapter.getServletContextFromServletConfig((javax.servlet.ServletConfig) config);
+ if (arg instanceof javax.servlet.FilterConfig) {
+ servletContext = adapter.getServletContextFromFilterConfig((javax.servlet.FilterConfig) arg);
+ } else if (arg instanceof javax.servlet.ServletConfig) {
+ servletContext = adapter.getServletContextFromServletConfig((javax.servlet.ServletConfig) arg);
+ } else if (arg instanceof javax.servlet.ServletContextEvent) {
+ servletContext = adapter.getServletContextFromServletContextEvent((javax.servlet.ServletContextEvent) arg);
} else {
return;
}
@@ -109,15 +115,14 @@ public String rootClassNameThatClassloaderCanLoad() {
public static class AdviceClass {
@Advice.OnMethodEnter(suppress = Throwable.class, inline = false)
- public static void onEnter(@Advice.Argument(0) @Nullable Object config) {
- if (config == null) {
- return;
- }
+ public static void onEnter(@Advice.Argument(0) @Nullable Object arg) {
jakarta.servlet.ServletContext servletContext;
- if (config instanceof jakarta.servlet.FilterConfig) {
- servletContext = adapter.getServletContextFromFilterConfig((jakarta.servlet.FilterConfig) config);
- } else if (config instanceof jakarta.servlet.ServletConfig) {
- servletContext = adapter.getServletContextFromServletConfig((jakarta.servlet.ServletConfig) config);
+ if (arg instanceof jakarta.servlet.FilterConfig) {
+ servletContext = adapter.getServletContextFromFilterConfig((jakarta.servlet.FilterConfig) arg);
+ } else if (arg instanceof jakarta.servlet.ServletConfig) {
+ servletContext = adapter.getServletContextFromServletConfig((jakarta.servlet.ServletConfig) arg);
+ } else if (arg instanceof jakarta.servlet.ServletContextEvent) {
+ servletContext = adapter.getServletContextFromServletContextEvent((jakarta.servlet.ServletContextEvent) arg);
} else {
return;
}
diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java
index 7af1b387be..74d76223a3 100644
--- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java
+++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java
@@ -58,8 +58,8 @@ public abstract class ServletApiAdvice {
private static final List requestExceptionAttributes = Arrays.asList("javax.servlet.error.exception", "jakarta.servlet.error.exception", "exception", "org.springframework.web.servlet.DispatcherServlet.EXCEPTION", "co.elastic.apm.exception");
@Nullable
- public static Object onServletEnter(
- ServletApiAdapter adapter,
+ public static Object onServletEnter(
+ ServletApiAdapter adapter,
Object servletRequest) {
ElasticApmTracer tracer = GlobalTracer.getTracerImpl();
@@ -159,8 +159,8 @@ public static void onExitServlet(
- ServletApiAdapter adapter,
+ public static void onExitServlet(
+ ServletApiAdapter adapter,
Object servletRequest,
Object servletResponse,
@Nullable Object transactionOrScopeOrSpan,
diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JakartaServletApiAdapter.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JakartaServletApiAdapter.java
index 976b5409ef..c2e8369675 100644
--- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JakartaServletApiAdapter.java
+++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JakartaServletApiAdapter.java
@@ -26,6 +26,7 @@
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
@@ -38,7 +39,7 @@
import java.util.Enumeration;
import java.util.Map;
-public class JakartaServletApiAdapter implements ServletApiAdapter {
+public class JakartaServletApiAdapter implements ServletApiAdapter {
public static final JakartaServletApiAdapter INSTANCE = new JakartaServletApiAdapter();
@@ -214,6 +215,11 @@ public boolean isInstanceOfHttpServlet(Object object) {
return object instanceof HttpServlet;
}
+ @Override
+ public ServletContext getServletContextFromServletContextEvent(ServletContextEvent servletContextEvent) {
+ return servletContextEvent.getServletContext();
+ }
+
@Override
public ServletContext getServletContextFromServletConfig(ServletConfig filterConfig) {
return filterConfig.getServletContext();
diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JavaxServletApiAdapter.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JavaxServletApiAdapter.java
index 9dbb19faaa..877e267683 100644
--- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JavaxServletApiAdapter.java
+++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JavaxServletApiAdapter.java
@@ -28,6 +28,7 @@
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -38,7 +39,7 @@
import java.util.Enumeration;
import java.util.Map;
-public class JavaxServletApiAdapter implements ServletApiAdapter {
+public class JavaxServletApiAdapter implements ServletApiAdapter {
private static final JavaxServletApiAdapter INSTANCE = new JavaxServletApiAdapter();
@@ -213,6 +214,11 @@ public boolean isInstanceOfHttpServlet(Object object) {
return object instanceof HttpServlet;
}
+ @Override
+ public ServletContext getServletContextFromServletContextEvent(ServletContextEvent servletContextEvent) {
+ return servletContextEvent.getServletContext();
+ }
+
@Override
public ServletContext getServletContextFromServletConfig(ServletConfig filterConfig) {
return filterConfig.getServletContext();
diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/ServletAdapter.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/ServletAdapter.java
index 0f5a2ae074..9e1e39148d 100644
--- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/ServletAdapter.java
+++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/ServletAdapter.java
@@ -21,10 +21,12 @@
import co.elastic.apm.agent.sdk.state.GlobalState;
@GlobalState
-public interface ServletAdapter {
+public interface ServletAdapter {
boolean isInstanceOfHttpServlet(Object object);
+ ServletContext getServletContextFromServletContextEvent(ServletContextEvent servletContextEvent);
+
ServletContext getServletContextFromServletConfig(ServletConfig filterConfig);
}
diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/ServletApiAdapter.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/ServletApiAdapter.java
index e8bdbe6d6f..5f085536a5 100644
--- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/ServletApiAdapter.java
+++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/ServletApiAdapter.java
@@ -21,10 +21,10 @@
import co.elastic.apm.agent.sdk.state.GlobalState;
@GlobalState
-public interface ServletApiAdapter extends
+public interface ServletApiAdapter extends
ServletRequestResponseAdapter,
ServletContextAdapter,
- ServletAdapter,
+ ServletAdapter,
FilterAdapter {
}
diff --git a/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/InitServiceNameInstrumentationTest.java b/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/InitServiceNameInstrumentationTest.java
index e143bf806e..d70add640d 100644
--- a/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/InitServiceNameInstrumentationTest.java
+++ b/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/InitServiceNameInstrumentationTest.java
@@ -23,11 +23,14 @@
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockServletConfig;
+import org.springframework.mock.web.MockServletContext;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.Servlet;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@@ -38,6 +41,19 @@
class InitServiceNameInstrumentationTest extends AbstractInstrumentationTest {
+ @Test
+ void testContextInitialized() {
+ ServletContextListener servletContextListener = new NoopServletContextListener();
+
+ CustomManifestLoader cl = new CustomManifestLoader(() -> getClass().getResourceAsStream("/TEST-MANIFEST.MF"));
+ CustomManifestLoader.withThreadContextClassLoader(cl, () -> {
+ servletContextListener.contextInitialized(new ServletContextEvent(new MockServletContext()));
+ tracer.startRootTransaction(cl).end();
+ });
+
+ assertServiceInfo();
+ }
+
@Test
void testServletInit() {
Servlet servlet = new HttpServlet() {
@@ -71,6 +87,16 @@ private void assertServiceInfo() {
assertThat(traceContext.getServiceVersion()).isEqualTo("1.42.0");
}
+ private static class NoopServletContextListener implements ServletContextListener {
+ @Override
+ public void contextInitialized(ServletContextEvent servletContextEvent) {
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent servletContextEvent) {
+ }
+ }
+
private static class NoopFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {