From 736260f87cb24170f73f6c211f7f67cf43d45d56 Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Thu, 7 Aug 2025 13:23:55 +0200 Subject: [PATCH 1/2] Support async servlet for RUM injection --- .../buffer/InjectingPipeOutputStream.java | 5 +- .../buffer/InjectingPipeWriter.java | 5 +- .../RumAsyncContextInstrumentation.java | 64 +++++++++++++++++++ .../RumHttpServletRequestWrapper.java | 45 +++++++++++++ .../RumHttpServletResponseWrapper.java | 11 ++++ .../servlet3/Servlet3Advice.java | 15 +++-- .../servlet3/Servlet3Instrumentation.java | 1 + .../servlet3/WrappedServletOutputStream.java | 4 ++ .../JakartaServletInstrumentation.java | 21 ++++-- .../RumAsyncContextInstrumentation.java | 64 +++++++++++++++++++ .../RumHttpServletRequestWrapper.java | 45 +++++++++++++ .../RumHttpServletResponseWrapper.java | 11 ++++ .../servlet5/WrappedServletOutputStream.java | 4 ++ .../servlet5/AsyncRumServlet.groovy | 58 +++++++++++++++++ .../groovy/TomcatServletTest.groovy | 11 ++++ .../rum/AbstractRumServerSmokeTest.groovy | 6 +- .../java/com/example/HtmlAsyncServlet.java | 46 +++++++++++++ .../src/main/java/com/example/Main.java | 5 ++ .../java/com/example/HtmlAsyncServlet.java | 46 +++++++++++++ .../src/main/java/com/example/Main.java | 5 ++ .../java/com/example/HtmlAsyncServlet.java | 46 +++++++++++++ .../src/main/java/com/example/Main.java | 5 ++ .../main/java/datadog/trace/api/Config.java | 4 +- .../datadog/trace/api/InstrumenterConfig.java | 12 +++- 24 files changed, 519 insertions(+), 20 deletions(-) create mode 100644 dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumAsyncContextInstrumentation.java create mode 100644 dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumHttpServletRequestWrapper.java create mode 100644 dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumAsyncContextInstrumentation.java create mode 100644 dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumHttpServletRequestWrapper.java create mode 100644 dd-java-agent/instrumentation/servlet/request-5/src/testFixtures/groovy/datadog/trace/instrumentation/servlet5/AsyncRumServlet.groovy create mode 100644 dd-smoke-tests/rum/tomcat-10/src/main/java/com/example/HtmlAsyncServlet.java create mode 100644 dd-smoke-tests/rum/tomcat-11/src/main/java/com/example/HtmlAsyncServlet.java create mode 100644 dd-smoke-tests/rum/tomcat-9/src/main/java/com/example/HtmlAsyncServlet.java diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/buffer/InjectingPipeOutputStream.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/buffer/InjectingPipeOutputStream.java index 93bb8f956fe..8f91a8e38cd 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/buffer/InjectingPipeOutputStream.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/buffer/InjectingPipeOutputStream.java @@ -172,7 +172,6 @@ private void drain() throws IOException { public void commit() throws IOException { if (filter || wasDraining) { - filter = false; drain(); } } @@ -190,4 +189,8 @@ public void close() throws IOException { downstream.close(); } } + + public void setFilter(boolean filter) { + this.filter = filter; + } } diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/buffer/InjectingPipeWriter.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/buffer/InjectingPipeWriter.java index e435b23d132..7a3d4a75f19 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/buffer/InjectingPipeWriter.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/buffer/InjectingPipeWriter.java @@ -173,7 +173,6 @@ private void drain() throws IOException { public void commit() throws IOException { if (filter || wasDraining) { - filter = false; drain(); } } @@ -191,4 +190,8 @@ public void close() throws IOException { downstream.close(); } } + + public void setFilter(boolean filter) { + this.filter = filter; + } } diff --git a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumAsyncContextInstrumentation.java b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumAsyncContextInstrumentation.java new file mode 100644 index 00000000000..96f1849ab25 --- /dev/null +++ b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumAsyncContextInstrumentation.java @@ -0,0 +1,64 @@ +package datadog.trace.instrumentation.servlet3; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf; +import static datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.DD_RUM_INJECTED; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.InstrumenterConfig; +import javax.servlet.AsyncContext; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumenterModule.class) +public class RumAsyncContextInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { + + public RumAsyncContextInstrumentation() { + super("servlet", "servlet-3", "servlet-3-async-context"); + } + + @Override + public String hierarchyMarkerType() { + return "javax.servlet.AsyncContext"; + } + + @Override + public String[] helperClassNames() { + return new String[] { + packageName + ".RumHttpServletResponseWrapper", packageName + ".WrappedServletOutputStream", + }; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named(hierarchyMarkerType())); + } + + @Override + protected boolean defaultEnabled() { + return super.defaultEnabled() && InstrumenterConfig.get().isRumEnabled(); + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + transformer.applyAdvice( + isMethod().and(namedOneOf("complete", "dispatch")), getClass().getName() + "$CommitAdvice"); + } + + public static class CommitAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void commitRumBuffer(@Advice.This final AsyncContext asyncContext) { + final Object maybeRumWrappedResponse = + asyncContext.getRequest().getAttribute(DD_RUM_INJECTED); + if (maybeRumWrappedResponse instanceof RumHttpServletResponseWrapper) { + ((RumHttpServletResponseWrapper) maybeRumWrappedResponse).commit(); + } + } + } +} diff --git a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumHttpServletRequestWrapper.java b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumHttpServletRequestWrapper.java new file mode 100644 index 00000000000..e0b4240097e --- /dev/null +++ b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumHttpServletRequestWrapper.java @@ -0,0 +1,45 @@ +package datadog.trace.instrumentation.servlet3; + +import static datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.DD_RUM_INJECTED; + +import javax.servlet.AsyncContext; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; + +public class RumHttpServletRequestWrapper extends HttpServletRequestWrapper { + + private final HttpServletResponse response; + + public RumHttpServletRequestWrapper( + final HttpServletRequest request, final HttpServletResponse response) { + super(request); + this.response = response; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + // need to hide this method otherwise we cannot control the wrapped response used asynchronously + return startAsync(getRequest(), response); + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) + throws IllegalStateException { + // deactivate the previous wrapper + final Object maybeRumWrappedResponse = (servletRequest.getAttribute(DD_RUM_INJECTED)); + if (maybeRumWrappedResponse instanceof RumHttpServletResponseWrapper) { + ((RumHttpServletResponseWrapper) maybeRumWrappedResponse).commit(); + ((RumHttpServletResponseWrapper) maybeRumWrappedResponse).stopFiltering(); + } + ServletResponse actualResponse = servletResponse; + // rewrap it + if (servletResponse instanceof HttpServletResponse) { + actualResponse = new RumHttpServletResponseWrapper((HttpServletResponse) servletResponse); + servletRequest.setAttribute(DD_RUM_INJECTED, actualResponse); + } + return super.startAsync(servletRequest, actualResponse); + } +} diff --git a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumHttpServletResponseWrapper.java b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumHttpServletResponseWrapper.java index 9ba84665901..a5defc56dfe 100644 --- a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumHttpServletResponseWrapper.java +++ b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumHttpServletResponseWrapper.java @@ -131,6 +131,7 @@ public void setContentType(String type) { } if (!shouldInject) { commit(); + stopFiltering(); } super.setContentType(type); } @@ -149,4 +150,14 @@ public void commit() { } } } + + public void stopFiltering() { + shouldInject = false; + if (wrappedPipeWriter != null) { + wrappedPipeWriter.setFilter(false); + } + if (outputStream != null) { + outputStream.setFilter(false); + } + } } diff --git a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Advice.java b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Advice.java index 3b1adba0938..96755749047 100644 --- a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Advice.java +++ b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Advice.java @@ -46,11 +46,16 @@ public static boolean onEnter( final HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; - if (RumInjector.get().isEnabled() && httpServletRequest.getAttribute(DD_RUM_INJECTED) == null) { - httpServletRequest.setAttribute(DD_RUM_INJECTED, Boolean.TRUE); - rumServletWrapper = new RumHttpServletResponseWrapper(httpServletResponse); - httpServletResponse = rumServletWrapper; - response = httpServletResponse; + if (RumInjector.get().isEnabled()) { + final Object maybeRumWrapper = httpServletRequest.getAttribute(DD_RUM_INJECTED); + if (maybeRumWrapper instanceof RumHttpServletResponseWrapper) { + rumServletWrapper = (RumHttpServletResponseWrapper) maybeRumWrapper; + } else { + rumServletWrapper = new RumHttpServletResponseWrapper((HttpServletResponse) response); + httpServletRequest.setAttribute(DD_RUM_INJECTED, rumServletWrapper); + response = rumServletWrapper; + request = new RumHttpServletRequestWrapper(httpServletRequest, rumServletWrapper); + } } Object dispatchSpan = request.getAttribute(DD_DISPATCH_SPAN_ATTRIBUTE); diff --git a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Instrumentation.java b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Instrumentation.java index d7732045329..4835d9d2b0b 100644 --- a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Instrumentation.java +++ b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Instrumentation.java @@ -52,6 +52,7 @@ public String[] helperClassNames() { packageName + ".Servlet3Decorator", packageName + ".ServletRequestURIAdapter", packageName + ".FinishAsyncDispatchListener", + packageName + ".RumHttpServletRequestWrapper", packageName + ".RumHttpServletResponseWrapper", packageName + ".WrappedServletOutputStream", "datadog.trace.instrumentation.servlet.ServletBlockingHelper", diff --git a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/WrappedServletOutputStream.java b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/WrappedServletOutputStream.java index 951eceb5db8..109a55491d8 100644 --- a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/WrappedServletOutputStream.java +++ b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/WrappedServletOutputStream.java @@ -86,4 +86,8 @@ public void setWriteListener(WriteListener writeListener) { public void commit() throws IOException { filtered.commit(); } + + public void setFilter(boolean filter) { + filtered.setFilter(filter); + } } diff --git a/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/JakartaServletInstrumentation.java b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/JakartaServletInstrumentation.java index 91e3c989019..d3630cc1fd8 100644 --- a/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/JakartaServletInstrumentation.java +++ b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/JakartaServletInstrumentation.java @@ -42,7 +42,9 @@ public String hierarchyMarkerType() { @Override public String[] helperClassNames() { return new String[] { - packageName + ".RumHttpServletResponseWrapper", packageName + ".WrappedServletOutputStream", + packageName + ".RumHttpServletRequestWrapper", + packageName + ".RumHttpServletResponseWrapper", + packageName + ".WrappedServletOutputStream", }; } @@ -67,7 +69,7 @@ public void methodAdvice(MethodTransformer transformer) { public static class JakartaServletAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static AgentSpan before( - @Advice.Argument(0) final ServletRequest request, + @Advice.Argument(value = 0, readOnly = false) ServletRequest request, @Advice.Argument(value = 1, readOnly = false) ServletResponse response, @Advice.Local("rumServletWrapper") RumHttpServletResponseWrapper rumServletWrapper) { if (!(request instanceof HttpServletRequest)) { @@ -77,11 +79,16 @@ public static AgentSpan before( if (response instanceof HttpServletResponse) { final HttpServletRequest httpServletRequest = (HttpServletRequest) request; - if (RumInjector.get().isEnabled() - && httpServletRequest.getAttribute(DD_RUM_INJECTED) == null) { - httpServletRequest.setAttribute(DD_RUM_INJECTED, Boolean.TRUE); - rumServletWrapper = new RumHttpServletResponseWrapper((HttpServletResponse) response); - response = rumServletWrapper; + if (RumInjector.get().isEnabled()) { + final Object maybeRumWrapper = httpServletRequest.getAttribute(DD_RUM_INJECTED); + if (maybeRumWrapper instanceof RumHttpServletResponseWrapper) { + rumServletWrapper = (RumHttpServletResponseWrapper) maybeRumWrapper; + } else { + rumServletWrapper = new RumHttpServletResponseWrapper((HttpServletResponse) response); + httpServletRequest.setAttribute(DD_RUM_INJECTED, rumServletWrapper); + response = rumServletWrapper; + request = new RumHttpServletRequestWrapper(httpServletRequest, rumServletWrapper); + } } } diff --git a/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumAsyncContextInstrumentation.java b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumAsyncContextInstrumentation.java new file mode 100644 index 00000000000..a301d877dea --- /dev/null +++ b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumAsyncContextInstrumentation.java @@ -0,0 +1,64 @@ +package datadog.trace.instrumentation.servlet5; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf; +import static datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.DD_RUM_INJECTED; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.InstrumenterConfig; +import jakarta.servlet.AsyncContext; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumenterModule.class) +public class RumAsyncContextInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { + + public RumAsyncContextInstrumentation() { + super("servlet", "servlet-5", "servlet-5-async-context"); + } + + @Override + public String hierarchyMarkerType() { + return "jakarta.servlet.AsyncContext"; + } + + @Override + public String[] helperClassNames() { + return new String[] { + packageName + ".RumHttpServletResponseWrapper", packageName + ".WrappedServletOutputStream", + }; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named(hierarchyMarkerType())); + } + + @Override + protected boolean defaultEnabled() { + return super.defaultEnabled() && InstrumenterConfig.get().isRumEnabled(); + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + transformer.applyAdvice( + isMethod().and(namedOneOf("complete", "dispatch")), getClass().getName() + "$CommitAdvice"); + } + + public static class CommitAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void commitRumBuffer(@Advice.This final AsyncContext asyncContext) { + final Object maybeRumWrappedResponse = + asyncContext.getRequest().getAttribute(DD_RUM_INJECTED); + if (maybeRumWrappedResponse instanceof RumHttpServletResponseWrapper) { + ((RumHttpServletResponseWrapper) maybeRumWrappedResponse).commit(); + } + } + } +} diff --git a/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumHttpServletRequestWrapper.java b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumHttpServletRequestWrapper.java new file mode 100644 index 00000000000..c2a05680488 --- /dev/null +++ b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumHttpServletRequestWrapper.java @@ -0,0 +1,45 @@ +package datadog.trace.instrumentation.servlet5; + +import static datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.DD_RUM_INJECTED; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import jakarta.servlet.http.HttpServletResponse; + +public class RumHttpServletRequestWrapper extends HttpServletRequestWrapper { + + private final HttpServletResponse response; + + public RumHttpServletRequestWrapper( + final HttpServletRequest request, final HttpServletResponse response) { + super(request); + this.response = response; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + // need to hide this method otherwise we cannot control the wrapped response used asynchronously + return startAsync(getRequest(), response); + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) + throws IllegalStateException { + // deactivate the previous wrapper + final Object maybeRumWrappedResponse = (servletRequest.getAttribute(DD_RUM_INJECTED)); + if (maybeRumWrappedResponse instanceof RumHttpServletResponseWrapper) { + ((RumHttpServletResponseWrapper) maybeRumWrappedResponse).commit(); + ((RumHttpServletResponseWrapper) maybeRumWrappedResponse).stopFiltering(); + } + ServletResponse actualResponse = servletResponse; + // rewrap it + if (servletResponse instanceof HttpServletResponse) { + actualResponse = new RumHttpServletResponseWrapper((HttpServletResponse) servletResponse); + servletRequest.setAttribute(DD_RUM_INJECTED, actualResponse); + } + return super.startAsync(servletRequest, actualResponse); + } +} diff --git a/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumHttpServletResponseWrapper.java b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumHttpServletResponseWrapper.java index 4b846b3c2db..4b91afd3890 100644 --- a/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumHttpServletResponseWrapper.java +++ b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumHttpServletResponseWrapper.java @@ -108,6 +108,7 @@ public void setContentType(String type) { } if (!shouldInject) { commit(); + stopFiltering(); } super.setContentType(type); } @@ -126,4 +127,14 @@ public void commit() { } } } + + public void stopFiltering() { + shouldInject = false; + if (wrappedPipeWriter != null) { + wrappedPipeWriter.setFilter(false); + } + if (outputStream != null) { + outputStream.setFilter(false); + } + } } diff --git a/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/WrappedServletOutputStream.java b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/WrappedServletOutputStream.java index f2338ee1a71..db956377708 100644 --- a/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/WrappedServletOutputStream.java +++ b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/WrappedServletOutputStream.java @@ -53,4 +53,8 @@ public boolean isReady() { public void setWriteListener(WriteListener writeListener) { delegate.setWriteListener(writeListener); } + + public void setFilter(boolean filter) { + filtered.setFilter(filter); + } } diff --git a/dd-java-agent/instrumentation/servlet/request-5/src/testFixtures/groovy/datadog/trace/instrumentation/servlet5/AsyncRumServlet.groovy b/dd-java-agent/instrumentation/servlet/request-5/src/testFixtures/groovy/datadog/trace/instrumentation/servlet5/AsyncRumServlet.groovy new file mode 100644 index 00000000000..7a1f736ceca --- /dev/null +++ b/dd-java-agent/instrumentation/servlet/request-5/src/testFixtures/groovy/datadog/trace/instrumentation/servlet5/AsyncRumServlet.groovy @@ -0,0 +1,58 @@ +package datadog.trace.instrumentation.servlet5 + +import jakarta.servlet.AsyncContext +import jakarta.servlet.ServletException +import jakarta.servlet.http.HttpServlet +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse + +class AsyncRumServlet extends HttpServlet { + private final String mimeType + + AsyncRumServlet(String mime) { + this.mimeType = mime + } + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + // write a partial content + resp.getWriter().println("\n" + + "") + // finish it later + final AsyncContext asyncContext = req.startAsync() + final String mime = mimeType + new Timer().schedule(new TimerTask() { + @Override + void run() { + def writer = asyncContext.getResponse().getWriter() + try { + asyncContext.getResponse().setContentType(mime) + writer.println( + "\n" + + " \n" + + " This is the title of the webpage!\n" + + " \n" + + " \n" + + "

This is an example paragraph. Anything in the body tag will appear on the page, just like this p tag and its contents.

\n" + + " \n" + + "") + } finally { + asyncContext.complete() + } + } + + }, 2000) + } +} + +class HtmlAsyncRumServlet extends AsyncRumServlet { + HtmlAsyncRumServlet() { + super("text/html") + } +} + +class XmlAsyncRumServlet extends AsyncRumServlet { + XmlAsyncRumServlet() { + super("text/xml") + } +} diff --git a/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServletTest.groovy b/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServletTest.groovy index 999d9d62c8a..ca541ea817f 100644 --- a/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServletTest.groovy +++ b/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServletTest.groovy @@ -1,7 +1,9 @@ import datadog.trace.agent.test.base.HttpServer import datadog.trace.api.ProcessTags +import datadog.trace.instrumentation.servlet5.HtmlAsyncRumServlet import datadog.trace.instrumentation.servlet5.HtmlRumServlet import datadog.trace.instrumentation.servlet5.TestServlet5 +import datadog.trace.instrumentation.servlet5.XmlAsyncRumServlet import datadog.trace.instrumentation.servlet5.XmlRumServlet import jakarta.servlet.Filter import jakarta.servlet.Servlet @@ -307,6 +309,15 @@ class TomcatRumInjectionForkedTest extends TomcatServletTest { } } +class TomcatAsyncRumInjectionForkedTest extends TomcatRumInjectionForkedTest { + @Override + protected void setupServlets(Context context) { + super.setupServlets(context) + addServlet(context, "/gimme-html", HtmlAsyncRumServlet) + addServlet(context, "/gimme-xml", XmlAsyncRumServlet) + } +} + diff --git a/dd-smoke-tests/rum/src/main/groovy/datadog/smoketest/rum/AbstractRumServerSmokeTest.groovy b/dd-smoke-tests/rum/src/main/groovy/datadog/smoketest/rum/AbstractRumServerSmokeTest.groovy index a4b79227e36..d26cffe3d44 100644 --- a/dd-smoke-tests/rum/src/main/groovy/datadog/smoketest/rum/AbstractRumServerSmokeTest.groovy +++ b/dd-smoke-tests/rum/src/main/groovy/datadog/smoketest/rum/AbstractRumServerSmokeTest.groovy @@ -14,9 +14,9 @@ class AbstractRumServerSmokeTest extends AbstractServerSmokeTest { "-Ddd.rum.remote.configuration.id=12345", ] - void 'test RUM SDK injection on html'() { + void 'test RUM SDK injection on html for path #servletPath'() { given: - def url = "http://localhost:${httpPort}/html" + def url = "http://localhost:${httpPort}/${servletPath}" def request = new Request.Builder() .url(url) .get() @@ -28,6 +28,8 @@ class AbstractRumServerSmokeTest extends AbstractServerSmokeTest { then: response.code() == 200 assertRumInjected(response) + where: + servletPath << ["html", "html_async"] } void 'test RUM SDK injection skip on unsupported mime type'() { diff --git a/dd-smoke-tests/rum/tomcat-10/src/main/java/com/example/HtmlAsyncServlet.java b/dd-smoke-tests/rum/tomcat-10/src/main/java/com/example/HtmlAsyncServlet.java new file mode 100644 index 00000000000..dc568cfc5b9 --- /dev/null +++ b/dd-smoke-tests/rum/tomcat-10/src/main/java/com/example/HtmlAsyncServlet.java @@ -0,0 +1,46 @@ +package com.example; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Timer; +import java.util.TimerTask; + +public class HtmlAsyncServlet extends HttpServlet { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + final AsyncContext asyncContext = req.startAsync(); + new Timer(false) + .schedule( + new TimerTask() { + @Override + public void run() { + try { + asyncContext + .getResponse() + .getWriter() + .write( + "" + + "" + + "" + + " " + + " " + + " Hello Servlet" + + "" + + "" + + "

Hello from Tomcat 9 Servlet!

" + + "

This is a demo HTML page served by Java servlet.

" + + "" + + ""); + asyncContext.complete(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + }, + 2000); + } +} diff --git a/dd-smoke-tests/rum/tomcat-10/src/main/java/com/example/Main.java b/dd-smoke-tests/rum/tomcat-10/src/main/java/com/example/Main.java index d9992f37c92..524e62907c0 100644 --- a/dd-smoke-tests/rum/tomcat-10/src/main/java/com/example/Main.java +++ b/dd-smoke-tests/rum/tomcat-10/src/main/java/com/example/Main.java @@ -1,5 +1,6 @@ package com.example; +import jakarta.servlet.ServletRegistration; import java.io.File; import org.apache.catalina.Context; import org.apache.catalina.LifecycleException; @@ -26,6 +27,10 @@ public static void main(String[] args) throws LifecycleException { context.addServletContainerInitializer( (c, ctx) -> { ctx.addServlet("htmlServlet", new HtmlServlet()).addMapping("/html"); + final ServletRegistration.Dynamic registration = + ctx.addServlet("htmlAsyncServlet", new HtmlAsyncServlet()); + registration.addMapping("/html_async"); + registration.setAsyncSupported(true); ctx.addServlet("xmlServlet", new XmlServlet()).addMapping("/xml"); }, null); diff --git a/dd-smoke-tests/rum/tomcat-11/src/main/java/com/example/HtmlAsyncServlet.java b/dd-smoke-tests/rum/tomcat-11/src/main/java/com/example/HtmlAsyncServlet.java new file mode 100644 index 00000000000..dc568cfc5b9 --- /dev/null +++ b/dd-smoke-tests/rum/tomcat-11/src/main/java/com/example/HtmlAsyncServlet.java @@ -0,0 +1,46 @@ +package com.example; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Timer; +import java.util.TimerTask; + +public class HtmlAsyncServlet extends HttpServlet { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + final AsyncContext asyncContext = req.startAsync(); + new Timer(false) + .schedule( + new TimerTask() { + @Override + public void run() { + try { + asyncContext + .getResponse() + .getWriter() + .write( + "" + + "" + + "" + + " " + + " " + + " Hello Servlet" + + "" + + "" + + "

Hello from Tomcat 9 Servlet!

" + + "

This is a demo HTML page served by Java servlet.

" + + "" + + ""); + asyncContext.complete(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + }, + 2000); + } +} diff --git a/dd-smoke-tests/rum/tomcat-11/src/main/java/com/example/Main.java b/dd-smoke-tests/rum/tomcat-11/src/main/java/com/example/Main.java index d9992f37c92..524e62907c0 100644 --- a/dd-smoke-tests/rum/tomcat-11/src/main/java/com/example/Main.java +++ b/dd-smoke-tests/rum/tomcat-11/src/main/java/com/example/Main.java @@ -1,5 +1,6 @@ package com.example; +import jakarta.servlet.ServletRegistration; import java.io.File; import org.apache.catalina.Context; import org.apache.catalina.LifecycleException; @@ -26,6 +27,10 @@ public static void main(String[] args) throws LifecycleException { context.addServletContainerInitializer( (c, ctx) -> { ctx.addServlet("htmlServlet", new HtmlServlet()).addMapping("/html"); + final ServletRegistration.Dynamic registration = + ctx.addServlet("htmlAsyncServlet", new HtmlAsyncServlet()); + registration.addMapping("/html_async"); + registration.setAsyncSupported(true); ctx.addServlet("xmlServlet", new XmlServlet()).addMapping("/xml"); }, null); diff --git a/dd-smoke-tests/rum/tomcat-9/src/main/java/com/example/HtmlAsyncServlet.java b/dd-smoke-tests/rum/tomcat-9/src/main/java/com/example/HtmlAsyncServlet.java new file mode 100644 index 00000000000..81406c68ba4 --- /dev/null +++ b/dd-smoke-tests/rum/tomcat-9/src/main/java/com/example/HtmlAsyncServlet.java @@ -0,0 +1,46 @@ +package com.example; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Timer; +import java.util.TimerTask; +import javax.servlet.AsyncContext; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class HtmlAsyncServlet extends HttpServlet { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + final AsyncContext asyncContext = req.startAsync(); + new Timer(false) + .schedule( + new TimerTask() { + @Override + public void run() { + try { + asyncContext + .getResponse() + .getWriter() + .write( + "" + + "" + + "" + + " " + + " " + + " Hello Servlet" + + "" + + "" + + "

Hello from Tomcat 9 Servlet!

" + + "

This is a demo HTML page served by Java servlet.

" + + "" + + ""); + asyncContext.complete(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + }, + 2000); + } +} diff --git a/dd-smoke-tests/rum/tomcat-9/src/main/java/com/example/Main.java b/dd-smoke-tests/rum/tomcat-9/src/main/java/com/example/Main.java index d9992f37c92..ad56bd12c28 100644 --- a/dd-smoke-tests/rum/tomcat-9/src/main/java/com/example/Main.java +++ b/dd-smoke-tests/rum/tomcat-9/src/main/java/com/example/Main.java @@ -1,6 +1,7 @@ package com.example; import java.io.File; +import javax.servlet.ServletRegistration; import org.apache.catalina.Context; import org.apache.catalina.LifecycleException; import org.apache.catalina.startup.Tomcat; @@ -26,6 +27,10 @@ public static void main(String[] args) throws LifecycleException { context.addServletContainerInitializer( (c, ctx) -> { ctx.addServlet("htmlServlet", new HtmlServlet()).addMapping("/html"); + final ServletRegistration.Dynamic registration = + ctx.addServlet("htmlAsyncServlet", new HtmlAsyncServlet()); + registration.addMapping("/html_async"); + registration.setAsyncSupported(true); ctx.addServlet("xmlServlet", new XmlServlet()).addMapping("/xml"); }, null); diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index b634f08bf6a..a4542bb76ea 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -121,7 +121,6 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_POLL_INTERVAL_SECONDS; import static datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_TARGETS_KEY; import static datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_TARGETS_KEY_ID; -import static datadog.trace.api.ConfigDefaults.DEFAULT_RUM_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_RUM_MAJOR_VERSION; import static datadog.trace.api.ConfigDefaults.DEFAULT_SCOPE_DEPTH_LIMIT; import static datadog.trace.api.ConfigDefaults.DEFAULT_SCOPE_ITERATION_KEEP_ALIVE; @@ -472,7 +471,6 @@ import static datadog.trace.api.config.RumConfig.RUM_APPLICATION_ID; import static datadog.trace.api.config.RumConfig.RUM_CLIENT_TOKEN; import static datadog.trace.api.config.RumConfig.RUM_DEFAULT_PRIVACY_LEVEL; -import static datadog.trace.api.config.RumConfig.RUM_ENABLED; import static datadog.trace.api.config.RumConfig.RUM_ENVIRONMENT; import static datadog.trace.api.config.RumConfig.RUM_MAJOR_VERSION; import static datadog.trace.api.config.RumConfig.RUM_REMOTE_CONFIGURATION_ID; @@ -2745,7 +2743,7 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) this.stackTraceLengthLimit = configProvider.getInteger(STACK_TRACE_LENGTH_LIMIT, defaultStackTraceLengthLimit); - this.rumEnabled = configProvider.getBoolean(RUM_ENABLED, DEFAULT_RUM_ENABLED); + this.rumEnabled = InstrumenterConfig.get().isRumEnabled(); this.rumInjectorConfig = parseRumConfig(configProvider); log.debug("New instance: {}", this); diff --git a/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java b/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java index 851a84d5b17..532d1472c6e 100644 --- a/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java +++ b/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java @@ -8,6 +8,7 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_LLM_OBS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_MEASURE_METHODS; import static datadog.trace.api.ConfigDefaults.DEFAULT_RESOLVER_RESET_INTERVAL; +import static datadog.trace.api.ConfigDefaults.DEFAULT_RUM_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_RUNTIME_CONTEXT_FIELD_INJECTION; import static datadog.trace.api.ConfigDefaults.DEFAULT_SERIALVERSIONUID_FIELD_INJECTION; import static datadog.trace.api.ConfigDefaults.DEFAULT_TELEMETRY_ENABLED; @@ -32,6 +33,7 @@ import static datadog.trace.api.config.ProfilingConfig.PROFILING_DIRECT_ALLOCATION_ENABLED_DEFAULT; import static datadog.trace.api.config.ProfilingConfig.PROFILING_ENABLED; import static datadog.trace.api.config.ProfilingConfig.PROFILING_ENABLED_DEFAULT; +import static datadog.trace.api.config.RumConfig.RUM_ENABLED; import static datadog.trace.api.config.TraceInstrumentationConfig.AKKA_FORK_JOIN_EXECUTOR_TASK_NAME; import static datadog.trace.api.config.TraceInstrumentationConfig.AKKA_FORK_JOIN_POOL_NAME; import static datadog.trace.api.config.TraceInstrumentationConfig.AKKA_FORK_JOIN_TASK_NAME; @@ -168,6 +170,8 @@ public class InstrumenterConfig { private final Collection additionalJaxRsAnnotations; + private final boolean rumEnabled; + private InstrumenterConfig() { this(ConfigProvider.createDefault()); } @@ -195,7 +199,7 @@ private InstrumenterConfig() { profilingEnabled = ProfilingEnablement.of( configProvider.getString(PROFILING_ENABLED, String.valueOf(PROFILING_ENABLED_DEFAULT))); - + rumEnabled = configProvider.getBoolean(RUM_ENABLED, DEFAULT_RUM_ENABLED); if (!Platform.isNativeImageBuilder()) { ciVisibilityEnabled = configProvider.getBoolean(CIVISIBILITY_ENABLED, DEFAULT_CIVISIBILITY_ENABLED); @@ -567,6 +571,10 @@ public boolean isLegacyInstrumentationEnabled( Arrays.asList(integrationNames), "", ".legacy.tracing.enabled", defaultEnabled); } + public boolean isRumEnabled() { + return rumEnabled; + } + // This has to be placed after all other static fields to give them a chance to initialize @SuppressFBWarnings("SI_INSTANCE_BEFORE_FINALS_ASSIGNED") private static final InstrumenterConfig INSTANCE = @@ -669,6 +677,8 @@ public String toString() { + websocketTracingEnabled + ", pekkoSchedulerEnabled=" + pekkoSchedulerEnabled + + ", rumEnabled=" + + rumEnabled + '}'; } } From 54d6a7ec250ffb83d09336bf4fd9853e4b310b0c Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Fri, 8 Aug 2025 15:00:07 +0200 Subject: [PATCH 2/2] Move isRumEnabled to instrumenter config --- .../servlet3/RumAsyncContextInstrumentation.java | 4 ++-- .../servlet5/RumAsyncContextInstrumentation.java | 4 ++-- .../src/main/java/datadog/trace/api/Config.java | 10 +--------- .../java/datadog/trace/api/rum/RumInjector.java | 8 +++++--- .../datadog/trace/api/rum/RumInjectorTest.groovy | 16 ++++++++++------ 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumAsyncContextInstrumentation.java b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumAsyncContextInstrumentation.java index 96f1849ab25..c6b420fdc65 100644 --- a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumAsyncContextInstrumentation.java +++ b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumAsyncContextInstrumentation.java @@ -41,8 +41,8 @@ public ElementMatcher hierarchyMatcher() { } @Override - protected boolean defaultEnabled() { - return super.defaultEnabled() && InstrumenterConfig.get().isRumEnabled(); + public boolean isEnabled() { + return super.isEnabled() && InstrumenterConfig.get().isRumEnabled(); } @Override diff --git a/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumAsyncContextInstrumentation.java b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumAsyncContextInstrumentation.java index a301d877dea..a4150f17ad1 100644 --- a/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumAsyncContextInstrumentation.java +++ b/dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumAsyncContextInstrumentation.java @@ -41,8 +41,8 @@ public ElementMatcher hierarchyMatcher() { } @Override - protected boolean defaultEnabled() { - return super.defaultEnabled() && InstrumenterConfig.get().isRumEnabled(); + public boolean isEnabled() { + return super.isEnabled() && InstrumenterConfig.get().isRumEnabled(); } @Override diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index a4542bb76ea..aae53ec7e07 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -1224,7 +1224,6 @@ public static String getHostName() { private final boolean optimizedMapEnabled; private final int stackTraceLengthLimit; - private final boolean rumEnabled; private final RumInjectorConfig rumInjectorConfig; // Read order: System Properties -> Env Variables, [-> properties file], [-> default value] @@ -2743,14 +2742,13 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) this.stackTraceLengthLimit = configProvider.getInteger(STACK_TRACE_LENGTH_LIMIT, defaultStackTraceLengthLimit); - this.rumEnabled = InstrumenterConfig.get().isRumEnabled(); this.rumInjectorConfig = parseRumConfig(configProvider); log.debug("New instance: {}", this); } private RumInjectorConfig parseRumConfig(ConfigProvider configProvider) { - if (!this.rumEnabled) { + if (!instrumenterConfig.isRumEnabled()) { return null; } try { @@ -5034,10 +5032,6 @@ public int getCloudPayloadTaggingMaxTags() { return cloudPayloadTaggingMaxTags; } - public boolean isRumEnabled() { - return this.rumEnabled; - } - public RumInjectorConfig getRumInjectorConfig() { return this.rumInjectorConfig; } @@ -5722,8 +5716,6 @@ public String toString() { + cloudResponsePayloadTagging + ", experimentalPropagateProcessTagsEnabled=" + experimentalPropagateProcessTagsEnabled - + ", rumEnabled=" - + rumEnabled + ", rumInjectorConfig=" + (rumInjectorConfig == null ? "null" : rumInjectorConfig.jsonPayload()) + '}'; diff --git a/internal-api/src/main/java/datadog/trace/api/rum/RumInjector.java b/internal-api/src/main/java/datadog/trace/api/rum/RumInjector.java index 03917bc0cfc..5501c80e62c 100644 --- a/internal-api/src/main/java/datadog/trace/api/rum/RumInjector.java +++ b/internal-api/src/main/java/datadog/trace/api/rum/RumInjector.java @@ -1,13 +1,15 @@ package datadog.trace.api.rum; import datadog.trace.api.Config; +import datadog.trace.api.InstrumenterConfig; import datadog.trace.api.cache.DDCache; import datadog.trace.api.cache.DDCaches; import java.util.function.Function; import javax.annotation.Nullable; public final class RumInjector { - private static final RumInjector INSTANCE = new RumInjector(Config.get()); + private static final RumInjector INSTANCE = + new RumInjector(Config.get(), InstrumenterConfig.get()); private static final String MARKER = ""; private static final char[] MARKER_CHARS = MARKER.toCharArray(); private static final Function MARKER_BYTES = @@ -27,8 +29,8 @@ public final class RumInjector { private final DDCache markerCache; private final Function snippetBytes; - RumInjector(Config config) { - boolean rumEnabled = config.isRumEnabled(); + RumInjector(Config config, InstrumenterConfig instrumenterConfig) { + boolean rumEnabled = instrumenterConfig.isRumEnabled(); RumInjectorConfig injectorConfig = config.getRumInjectorConfig(); // If both RUM is enabled and injector config is valid if (rumEnabled && injectorConfig != null) { diff --git a/internal-api/src/test/groovy/datadog/trace/api/rum/RumInjectorTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/rum/RumInjectorTest.groovy index c64f756cac0..98988e40242 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/rum/RumInjectorTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/rum/RumInjectorTest.groovy @@ -1,6 +1,7 @@ package datadog.trace.api.rum import datadog.trace.api.Config +import datadog.trace.api.InstrumenterConfig import datadog.trace.test.util.DDSpecification import static org.mockito.Mockito.mock @@ -12,11 +13,12 @@ class RumInjectorTest extends DDSpecification { void 'disabled injector'(){ setup: Config config = mock(Config) + InstrumenterConfig instrumenterConfig = mock(InstrumenterConfig) RumInjector injector when: - when(config.isRumEnabled()).thenReturn(false) - injector = new RumInjector(config) + when(instrumenterConfig.isRumEnabled()).thenReturn(false) + injector = new RumInjector(config, instrumenterConfig) then: !injector.isEnabled() @@ -27,12 +29,13 @@ class RumInjectorTest extends DDSpecification { void 'invalid config injector'() { setup: Config config = mock(Config) + InstrumenterConfig instrumenterConfig = mock(InstrumenterConfig) RumInjector injector when: - when(config.isRumEnabled()).thenReturn(true) + when(instrumenterConfig.isRumEnabled()).thenReturn(true) when(config.rumInjectorConfig).thenReturn(null) - injector = new RumInjector(config) + injector = new RumInjector(config, instrumenterConfig) then: !injector.isEnabled() @@ -45,14 +48,15 @@ class RumInjectorTest extends DDSpecification { void 'enabled injector'() { setup: Config config = mock(Config) + InstrumenterConfig instrumenterConfig = mock(InstrumenterConfig) def injectorConfig = mock(RumInjectorConfig) RumInjector injector when: - when(config.isRumEnabled()).thenReturn(true) + when(instrumenterConfig.isRumEnabled()).thenReturn(true) when(config.rumInjectorConfig).thenReturn(injectorConfig) when(injectorConfig.snippet).thenReturn("") - injector = new RumInjector(config) + injector = new RumInjector(config, instrumenterConfig) then: injector.isEnabled()