Skip to content

Commit 6282d6b

Browse files
PerfectSlayerdevflow.devflow-routing-intake
andauthored
Mute tracing for agent http calls (#10690)
feat(java-net): Mute tracing for agent http calls fix(java-net): Limit context injection into headers only when making request feat(communication): Reuse prepareRequest to deduplicate request creation and add missing marking headers Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
1 parent edc4559 commit 6282d6b

8 files changed

Lines changed: 90 additions & 35 deletions

File tree

communication/src/main/java/datadog/communication/ddagent/DDAgentFeaturesDiscovery.java

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package datadog.communication.ddagent;
22

3-
import static datadog.communication.http.OkHttpUtils.DATADOG_CONTAINER_ID;
43
import static datadog.communication.http.OkHttpUtils.DATADOG_CONTAINER_TAGS_HASH;
4+
import static datadog.communication.http.OkHttpUtils.msgpackRequestBodyOf;
5+
import static datadog.communication.http.OkHttpUtils.prepareRequest;
56
import static datadog.communication.serialization.msgpack.MsgPackWriter.FIXARRAY;
7+
import static java.util.Collections.emptyMap;
68
import static java.util.Collections.emptySet;
79
import static java.util.Collections.singletonList;
810
import static java.util.Collections.unmodifiableSet;
@@ -11,7 +13,6 @@
1113
import com.squareup.moshi.Moshi;
1214
import com.squareup.moshi.Types;
1315
import datadog.common.container.ContainerInfo;
14-
import datadog.communication.http.OkHttpUtils;
1516
import datadog.metrics.api.Monitoring;
1617
import datadog.metrics.api.Recording;
1718
import datadog.metrics.impl.statsd.DDAgentStatsDClientManager;
@@ -151,13 +152,9 @@ private void doDiscovery(State newState) {
151152
// 3. fallback if the endpoint couldn't be found or the response couldn't be parsed
152153
try (Recording recording = discoveryTimer.start()) {
153154
boolean fallback = true;
154-
final Request.Builder requestBuilder =
155-
new Request.Builder().url(agentBaseUrl.resolve("info").url());
156-
final String containerId = ContainerInfo.get().getContainerId();
157-
if (containerId != null) {
158-
requestBuilder.header(DATADOG_CONTAINER_ID, containerId);
159-
}
160-
try (Response response = client.newCall(requestBuilder.build()).execute()) {
155+
final Request request =
156+
prepareRequest(agentBaseUrl.resolve("info"), emptyMap()).get().build();
157+
try (Response response = client.newCall(request).execute()) {
161158
if (response.isSuccessful()) {
162159
processInfoResponseHeaders(response);
163160
fallback = !processInfoResponse(newState, response.body().string());
@@ -202,11 +199,8 @@ private String probeTracesEndpoint(State newState, String[] endpoints) {
202199
try (Response response =
203200
client
204201
.newCall(
205-
new Request.Builder()
206-
.put(
207-
OkHttpUtils.msgpackRequestBodyOf(
208-
singletonList(ByteBuffer.wrap(PROBE_MESSAGE))))
209-
.url(agentBaseUrl.resolve(candidate))
202+
prepareRequest(agentBaseUrl.resolve(candidate), emptyMap())
203+
.put(msgpackRequestBodyOf(singletonList(ByteBuffer.wrap(PROBE_MESSAGE))))
210204
.build())
211205
.execute()) {
212206
if (response.code() != 404) {

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ public abstract class HttpClientDecorator<REQUEST, RESPONSE> extends UriBasedCli
3838

3939
private static final Logger log = LoggerFactory.getLogger(HttpClientDecorator.class);
4040

41+
private static final String DATADOG_META_LANG_HEADER_NAME = "Datadog-Meta-Lang";
42+
private static final String DD_CLIENT_LIBRARY_LANGUAGE_HEADER_NAME = "DD-Client-Library-Language";
43+
4144
private static final BitSet CLIENT_ERROR_STATUSES = Config.get().getHttpClientErrorStatuses();
4245

4346
private static final UTF8BytesString DEFAULT_RESOURCE_NAME = UTF8BytesString.create("/");
@@ -50,6 +53,15 @@ public abstract class HttpClientDecorator<REQUEST, RESPONSE> extends UriBasedCli
5053

5154
protected abstract URI url(REQUEST request) throws URISyntaxException;
5255

56+
/**
57+
* Returns {@code true} if the request was made by the Datadog agent itself. Such requests must
58+
* not be traced to avoid self-tracing loops.
59+
*/
60+
public boolean isAgentRequest(final REQUEST request) {
61+
return getRequestHeader(request, DATADOG_META_LANG_HEADER_NAME) != null
62+
|| getRequestHeader(request, DD_CLIENT_LIBRARY_LANGUAGE_HEADER_NAME) != null;
63+
}
64+
5365
protected abstract int status(RESPONSE response);
5466

5567
protected abstract String getRequestHeader(REQUEST request, String headerName);

dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/main/java11/datadog/trace/instrumentation/httpclient/HeadersAdvice.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515
public class HeadersAdvice {
1616
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
1717
public static void methodExit(@Advice.Return(readOnly = false) HttpHeaders headers) {
18-
// Note: adding duplicate keys will throw an IllegalArgumentException so we need to dedupe
19-
// case insensitively
20-
final Map<String, List<String>> headerMap = new TreeMap<>(CASE_INSENSITIVE_ORDER);
21-
headerMap.putAll(headers.map());
22-
DECORATE.injectContext(getCurrentContext(), headerMap, SETTER);
23-
headers = HttpHeaders.of(headerMap, KEEP);
18+
// Check if we should be injecting context into the headers first
19+
if (DECORATE.isContextInjectionAllowed()) {
20+
// Note: adding duplicate keys will throw an IllegalArgumentException so we need to dedupe
21+
// case insensitively
22+
final Map<String, List<String>> headerMap = new TreeMap<>(CASE_INSENSITIVE_ORDER);
23+
headerMap.putAll(headers.map());
24+
DECORATE.injectContext(getCurrentContext(), headerMap, SETTER);
25+
headers = HttpHeaders.of(headerMap, KEEP);
26+
}
2427
}
2528
}

dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/main/java11/datadog/trace/instrumentation/httpclient/JavaNetClientDecorator.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,23 @@
33
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
44
import datadog.trace.bootstrap.instrumentation.decorator.HttpClientDecorator;
55
import java.net.URI;
6-
import java.net.URISyntaxException;
76
import java.net.http.HttpRequest;
87
import java.net.http.HttpResponse;
98

109
public class JavaNetClientDecorator extends HttpClientDecorator<HttpRequest, HttpResponse<?>> {
11-
12-
public static final CharSequence COMPONENT = UTF8BytesString.create("java-http-client");
10+
public static final String INSTRUMENTATION_NAME = "java-http-client";
11+
public static final CharSequence COMPONENT = UTF8BytesString.create(INSTRUMENTATION_NAME);
1312

1413
public static final JavaNetClientDecorator DECORATE = new JavaNetClientDecorator();
1514

1615
public static final UTF8BytesString OPERATION_NAME =
1716
UTF8BytesString.create(DECORATE.operationName());
1817

18+
private static final ThreadLocal<Boolean> INJECT_CONTEXT = new ThreadLocal<>();
19+
1920
@Override
2021
protected String[] instrumentationNames() {
21-
return new String[] {"java-http-client"};
22+
return new String[] {INSTRUMENTATION_NAME};
2223
}
2324

2425
@Override
@@ -32,7 +33,7 @@ protected String method(HttpRequest httpRequest) {
3233
}
3334

3435
@Override
35-
protected URI url(HttpRequest httpRequest) throws URISyntaxException {
36+
protected URI url(HttpRequest httpRequest) {
3637
return httpRequest.uri();
3738
}
3839

@@ -55,4 +56,24 @@ protected String getRequestHeader(HttpRequest request, String headerName) {
5556
protected String getResponseHeader(HttpResponse<?> response, String headerName) {
5657
return response.headers().firstValue(headerName).orElse(null);
5758
}
59+
60+
/**
61+
* Checks whether context injection into HTTP headers is currently allowed.
62+
*
63+
* @return {@code true} if context injection is allowed for the current thread, {@code false}
64+
* otherwise
65+
*/
66+
public boolean isContextInjectionAllowed() {
67+
return INJECT_CONTEXT.get() != null && INJECT_CONTEXT.get();
68+
}
69+
70+
/** Enables context injection into HTTP headers for the current thread. */
71+
public void allowContextInjection() {
72+
INJECT_CONTEXT.set(true);
73+
}
74+
75+
/** Disables context injection into HTTP headers for the current thread. */
76+
public void blockContextInjection() {
77+
INJECT_CONTEXT.remove();
78+
}
5879
}

dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/main/java11/datadog/trace/instrumentation/httpclient/SendAdvice.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
44
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
55
import static datadog.trace.instrumentation.httpclient.JavaNetClientDecorator.DECORATE;
6+
import static datadog.trace.instrumentation.httpclient.JavaNetClientDecorator.INSTRUMENTATION_NAME;
7+
import static datadog.trace.instrumentation.httpclient.JavaNetClientDecorator.OPERATION_NAME;
68

79
import datadog.appsec.api.blocking.BlockingException;
810
import datadog.trace.bootstrap.CallDepthThreadLocalMap;
@@ -17,16 +19,19 @@ public class SendAdvice {
1719
@Advice.OnMethodEnter(suppress = Throwable.class)
1820
public static AgentScope methodEnter(@Advice.Argument(value = 0) final HttpRequest httpRequest) {
1921
try {
22+
if (DECORATE.isAgentRequest(httpRequest)) {
23+
return null;
24+
}
2025
// Here we avoid having the advice applied twice in case we have nested call of this
21-
// intercepted
22-
// method.
26+
// intercepted method.
2327
// In this particular case, in HttpClientImpl the send method is calling sendAsync under the
24-
// hood and we do not want to instrument twice.
28+
// hood, and we do not want to instrument twice.
2529
final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class);
2630
if (callDepth > 0) {
2731
return null;
2832
}
29-
final AgentSpan span = startSpan(JavaNetClientDecorator.OPERATION_NAME);
33+
DECORATE.allowContextInjection();
34+
final AgentSpan span = startSpan(INSTRUMENTATION_NAME, OPERATION_NAME);
3035
final AgentScope scope = activateSpan(span);
3136

3237
DECORATE.afterStart(span);
@@ -36,6 +41,7 @@ public static AgentScope methodEnter(@Advice.Argument(value = 0) final HttpReque
3641
return scope;
3742
} catch (BlockingException e) {
3843
CallDepthThreadLocalMap.reset(HttpClient.class);
44+
DECORATE.blockContextInjection();
3945
// re-throw blocking exceptions
4046
throw e;
4147
}
@@ -50,6 +56,7 @@ public static void methodExit(
5056
return;
5157
}
5258
CallDepthThreadLocalMap.reset(HttpClient.class);
59+
DECORATE.blockContextInjection();
5360

5461
AgentSpan span = scope.span();
5562
if (null != throwable) {

dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/main/java11/datadog/trace/instrumentation/httpclient/SendAsyncAdvice.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
44
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.captureSpan;
5+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
56
import static datadog.trace.instrumentation.httpclient.JavaNetClientDecorator.DECORATE;
7+
import static datadog.trace.instrumentation.httpclient.JavaNetClientDecorator.INSTRUMENTATION_NAME;
8+
import static datadog.trace.instrumentation.httpclient.JavaNetClientDecorator.OPERATION_NAME;
69

710
import datadog.appsec.api.blocking.BlockingException;
811
import datadog.trace.bootstrap.CallDepthThreadLocalMap;
912
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
1013
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
11-
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
1214
import java.net.http.HttpClient;
1315
import java.net.http.HttpRequest;
1416
import java.net.http.HttpResponse;
@@ -21,16 +23,19 @@ public static AgentScope methodEnter(
2123
@Advice.Argument(value = 0) final HttpRequest httpRequest,
2224
@Advice.Argument(value = 1, readOnly = false) HttpResponse.BodyHandler<?> bodyHandler) {
2325
try {
26+
if (DECORATE.isAgentRequest(httpRequest)) {
27+
return null;
28+
}
2429
// Here we avoid having the advice applied twice in case we have nested call of this
25-
// intercepted
26-
// method.
30+
// intercepted method.
2731
// In this particular case, in HttpClientImpl the send method is calling sendAsync under the
28-
// hood and we do not want to instrument twice.
32+
// hood, and we do not want to instrument twice.
2933
final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class);
3034
if (callDepth > 0) {
3135
return null;
3236
}
33-
final AgentSpan span = AgentTracer.startSpan(JavaNetClientDecorator.OPERATION_NAME);
37+
DECORATE.allowContextInjection();
38+
final AgentSpan span = startSpan(INSTRUMENTATION_NAME, OPERATION_NAME);
3439
final AgentScope scope = activateSpan(span);
3540
if (bodyHandler != null) {
3641
bodyHandler = new BodyHandlerWrapper<>(bodyHandler, captureSpan(span));
@@ -43,6 +48,7 @@ public static AgentScope methodEnter(
4348
return scope;
4449
} catch (BlockingException e) {
4550
CallDepthThreadLocalMap.reset(HttpClient.class);
51+
DECORATE.blockContextInjection();
4652
// re-throw blocking exceptions
4753
throw e;
4854
}
@@ -59,6 +65,7 @@ public static void methodExit(
5965
}
6066
// clear the call depth once finished
6167
CallDepthThreadLocalMap.reset(HttpClient.class);
68+
DECORATE.blockContextInjection();
6269

6370
AgentSpan span = scope.span();
6471
if (throwable != null) {

dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/test/groovy/datadog/trace/instrumentation/httpclient/JavaHttpClientTest.groovy

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@ abstract class JavaHttpClientTest extends HttpClientTest {
4444
false
4545
}
4646

47+
def "request to agent not traced"() {
48+
when:
49+
def status = doRequest("GET", server.address.resolve("/success"), ["Datadog-Meta-Lang": "java"])
50+
51+
then:
52+
status == 200
53+
assertTraces(1) {
54+
server.distributedRequestTrace(it)
55+
}
56+
}
57+
4758
def 'should not inject duplicate headers'() {
4859
when:
4960
def status = doRequest("GET", server.address.resolve("/success"),

dd-java-agent/instrumentation/okhttp/okhttp-3.0/src/main/java/datadog/trace/instrumentation/okhttp3/TracingInterceptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
public class TracingInterceptor implements Interceptor {
1818
@Override
1919
public Response intercept(final Chain chain) throws IOException {
20-
if (chain.request().header("Datadog-Meta-Lang") != null) {
20+
if (DECORATE.isAgentRequest(chain.request())) {
2121
return chain.proceed(chain.request());
2222
}
2323

0 commit comments

Comments
 (0)