Skip to content

Commit d7deb46

Browse files
authored
Avoid setting request attributes to reduce allocations (#316)
Adding a request attribute creates a new HashMap$Node which allocates 32b - Only set transaction request attribute when startAsync is called - Set excluded as ThreadLocal as opposed to request attribute
1 parent c7f5107 commit d7deb46

File tree

5 files changed

+34
-16
lines changed

5 files changed

+34
-16
lines changed

apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/servlet/AsyncInstrumentation.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import co.elastic.apm.bci.HelperClassManager;
2424
import co.elastic.apm.bci.VisibleForAdvice;
2525
import co.elastic.apm.impl.ElasticApmTracer;
26+
import co.elastic.apm.impl.transaction.Transaction;
2627
import net.bytebuddy.asm.Advice;
2728
import net.bytebuddy.description.NamedElement;
2829
import net.bytebuddy.description.method.MethodDescription;
@@ -37,6 +38,7 @@
3738
import java.util.Arrays;
3839
import java.util.Collection;
3940

41+
import static co.elastic.apm.servlet.ServletApiAdvice.TRANSACTION_ATTRIBUTE;
4042
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
4143
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
4244
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
@@ -54,7 +56,7 @@
5456
* See https://github.com/raphw/byte-buddy/issues/465 for more information.
5557
* However, the helper class {@link StartAsyncAdviceHelper} has access to the Servlet API,
5658
* as it is loaded by the child classloader of {@link AsyncContext}
57-
* (see {@link StartAsyncAdvice#onExitStartAsync(AsyncContext)}).
59+
* (see {@link StartAsyncAdvice#onExitStartAsync(HttpServletRequest, AsyncContext)}).
5860
*/
5961
public class AsyncInstrumentation extends ElasticApmInstrumentation {
6062

@@ -122,9 +124,15 @@ public interface StartAsyncAdviceHelper<T> {
122124
public static class StartAsyncAdvice {
123125

124126
@Advice.OnMethodExit
125-
private static void onExitStartAsync(@Advice.Return AsyncContext asyncContext) {
126-
if (asyncHelper != null) {
127-
asyncHelper.getForClassLoaderOfClass(AsyncContext.class).onExitStartAsync(asyncContext);
127+
private static void onExitStartAsync(@Advice.This HttpServletRequest request, @Advice.Return AsyncContext asyncContext) {
128+
if (tracer != null) {
129+
final Transaction transaction = tracer.currentTransaction();
130+
if (transaction != null) {
131+
request.setAttribute(TRANSACTION_ATTRIBUTE, transaction);
132+
if (asyncHelper != null) {
133+
asyncHelper.getForClassLoaderOfClass(AsyncContext.class).onExitStartAsync(asyncContext);
134+
}
135+
}
128136
}
129137
}
130138
}

apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/servlet/FilterChainInstrumentation.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package co.elastic.apm.servlet;
2121

2222
import co.elastic.apm.bci.ElasticApmInstrumentation;
23-
import co.elastic.apm.bci.VisibleForAdvice;
2423
import co.elastic.apm.impl.ElasticApmTracer;
2524
import net.bytebuddy.description.NamedElement;
2625
import net.bytebuddy.description.method.MethodDescription;
@@ -43,9 +42,6 @@
4342
*/
4443
public class FilterChainInstrumentation extends ElasticApmInstrumentation {
4544

46-
@VisibleForAdvice
47-
public static final String EXCLUDE_REQUEST = "elastic.apm.servlet.request.exclude";
48-
4945
@Override
5046
public void init(ElasticApmTracer tracer) {
5147
ServletApiAdvice.init(tracer);

apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/servlet/ServletApiAdvice.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ public class ServletApiAdvice {
5555
@Nullable
5656
@VisibleForAdvice
5757
public static ElasticApmTracer tracer;
58+
@VisibleForAdvice
59+
public static ThreadLocal<Boolean> excluded = new ThreadLocal<Boolean>() {
60+
@Override
61+
protected Boolean initialValue() {
62+
return Boolean.FALSE;
63+
}
64+
};
5865

5966
static void init(ElasticApmTracer tracer) {
6067
ServletApiAdvice.tracer = tracer;
@@ -77,7 +84,7 @@ public static void onEnterServletService(@Advice.Argument(0) ServletRequest serv
7784
if (servletTransactionHelper != null &&
7885
servletRequest instanceof HttpServletRequest &&
7986
servletRequest.getDispatcherType() == DispatcherType.REQUEST &&
80-
!Boolean.TRUE.equals(servletRequest.getAttribute(FilterChainInstrumentation.EXCLUDE_REQUEST))) {
87+
!Boolean.TRUE.equals(excluded.get())) {
8188

8289
final HttpServletRequest request = (HttpServletRequest) servletRequest;
8390
transaction = servletTransactionHelper.onBefore(
@@ -86,10 +93,8 @@ public static void onEnterServletService(@Advice.Argument(0) ServletRequest serv
8693
request.getHeader(TraceContext.TRACE_PARENT_HEADER));
8794
if (transaction == null) {
8895
// if the request is excluded, avoid matching all exclude patterns again on each filter invocation
89-
request.setAttribute(FilterChainInstrumentation.EXCLUDE_REQUEST, Boolean.TRUE);
96+
excluded.set(Boolean.TRUE);
9097
return;
91-
} else {
92-
request.setAttribute(TRANSACTION_ATTRIBUTE, transaction);
9398
}
9499
final Request req = transaction.getContext().getRequest();
95100
if (transaction.isSampled() && request.getCookies() != null) {
@@ -121,6 +126,7 @@ public static void onExitServletService(@Advice.Argument(0) ServletRequest servl
121126
if (tracer == null) {
122127
return;
123128
}
129+
excluded.set(Boolean.FALSE);
124130
if (scope != null) {
125131
scope.close();
126132
}

apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/servlet/ServletTransactionHelper.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import javax.annotation.Nullable;
4040
import java.util.Arrays;
4141
import java.util.HashSet;
42+
import java.util.List;
4243
import java.util.Map;
4344
import java.util.Set;
4445

@@ -185,15 +186,17 @@ private void fillRequestParameters(Transaction transaction, String method, Map<S
185186
}
186187

187188
private boolean isExcluded(String servletPath, String pathInfo, String requestURI, @Nullable String userAgentHeader) {
188-
boolean excludeUrl = WildcardMatcher.anyMatch(webConfiguration.getIgnoreUrls(), servletPath, pathInfo) != null;
189+
final List<WildcardMatcher> ignoreUrls = webConfiguration.getIgnoreUrls();
190+
boolean excludeUrl = WildcardMatcher.anyMatch(ignoreUrls, servletPath, pathInfo) != null;
189191
if (excludeUrl) {
190192
logger.debug("Not tracing this request as the URL {} is ignored by one of the matchers",
191-
requestURI, webConfiguration.getIgnoreUrls());
193+
requestURI, ignoreUrls);
192194
}
193-
boolean excludeAgent = userAgentHeader != null && WildcardMatcher.anyMatch(webConfiguration.getIgnoreUserAgents(), userAgentHeader) != null;
195+
final List<WildcardMatcher> ignoreUserAgents = webConfiguration.getIgnoreUserAgents();
196+
boolean excludeAgent = userAgentHeader != null && WildcardMatcher.anyMatch(ignoreUserAgents, userAgentHeader) != null;
194197
if (excludeAgent) {
195198
logger.debug("Not tracing this request as the User-Agent {} is ignored by one of the matchers",
196-
userAgentHeader, webConfiguration.getIgnoreUserAgents());
199+
userAgentHeader, ignoreUserAgents);
197200
}
198201
return excludeUrl || excludeAgent;
199202
}

apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/servlet/ApmFilterTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949

5050
import static org.assertj.core.api.Java6Assertions.assertThat;
5151
import static org.assertj.core.api.Java6Assertions.assertThatThrownBy;
52+
import static org.mockito.Mockito.times;
53+
import static org.mockito.Mockito.verify;
5254
import static org.mockito.Mockito.when;
5355

5456
class ApmFilterTest extends AbstractInstrumentationTest {
@@ -145,11 +147,14 @@ void testIgnoreUrlStartWithNoMatch() throws IOException, ServletException {
145147

146148
@Test
147149
void testIgnoreUrlEndWith() throws IOException, ServletException {
150+
filterChain = new MockFilterChain(new HttpServlet() {
151+
});
148152
when(webConfiguration.getIgnoreUrls()).thenReturn(Collections.singletonList(WildcardMatcher.valueOf("*.js")));
149153
final MockHttpServletRequest request = new MockHttpServletRequest();
150154
request.setServletPath("/resources");
151155
request.setPathInfo("test.js");
152156
filterChain.doFilter(request, new MockHttpServletResponse());
157+
verify(webConfiguration, times(1)).getIgnoreUrls();
153158
assertThat(reporter.getTransactions()).hasSize(0);
154159
}
155160

0 commit comments

Comments
 (0)