Skip to content

Commit 3fefc0d

Browse files
author
Cecile Terpin
committed
add latency trace interceptor
1 parent 4d76fed commit 3fefc0d

File tree

7 files changed

+112
-1
lines changed

7 files changed

+112
-1
lines changed

dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public final class ConfigDefaults {
6969
static final boolean DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX = false;
7070
static final boolean DEFAULT_DB_CLIENT_HOST_SPLIT_BY_HOST = false;
7171
static final String DEFAULT_DB_DBM_PROPAGATION_MODE_MODE = "disabled";
72+
static final int DEFAULT_TRACE_LATENCY_INTERCEPTOR_VALUE = -1;
7273
static final int DEFAULT_SCOPE_DEPTH_LIMIT = 100;
7374
static final int DEFAULT_SCOPE_ITERATION_KEEP_ALIVE = 30; // in seconds
7475
static final int DEFAULT_PARTIAL_FLUSH_MIN_SPANS = 1000;

dd-trace-api/src/main/java/datadog/trace/api/config/TracerConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public final class TracerConfig {
7373
public static final String TRACE_HTTP_CLIENT_ERROR_STATUSES = "trace.http.client.error.statuses";
7474

7575
public static final String SPLIT_BY_TAGS = "trace.split-by-tags";
76-
76+
public static final String TRACE_LATENCY_INTERCEPTOR_VALUE = "trace.latency.interceptor.value";
7777
public static final String SCOPE_DEPTH_LIMIT = "trace.scope.depth.limit";
7878
public static final String SCOPE_STRICT_MODE = "trace.scope.strict.mode";
7979
public static final String SCOPE_ITERATION_KEEP_ALIVE = "trace.scope.iteration.keep.alive";

dd-trace-api/src/main/java/datadog/trace/api/interceptor/AbstractTraceInterceptor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ public enum Priority {
2222
DD_INTAKE(2),
2323
GIT_METADATA(3),
2424

25+
// trace custom sampling
26+
ROOT_SPAN_LATENCY(Integer.MAX_VALUE - 2),
27+
2528
// trace data collection
2629
CI_VISIBILITY_TELEMETRY(Integer.MAX_VALUE - 1),
2730
SERVICE_NAME_COLLECTING(Integer.MAX_VALUE);

dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import datadog.trace.core.scopemanager.ContinuableScopeManager;
8989
import datadog.trace.core.taginterceptor.RuleFlags;
9090
import datadog.trace.core.taginterceptor.TagInterceptor;
91+
import datadog.trace.core.traceinterceptor.LatencyTraceInterceptor;
9192
import datadog.trace.lambda.LambdaHandler;
9293
import datadog.trace.relocate.api.RatelimitedLogger;
9394
import datadog.trace.util.AgentTaskScheduler;
@@ -745,6 +746,10 @@ private CoreTracer(
745746
addTraceInterceptor(GitMetadataTraceInterceptor.INSTANCE);
746747
}
747748

749+
if (config.isTraceLatencyInterceptorEnabled()) {
750+
addTraceInterceptor(LatencyTraceInterceptor.INSTANCE);
751+
}
752+
748753
this.instrumentationGateway = instrumentationGateway;
749754
callbackProviderAppSec = instrumentationGateway.getCallbackProvider(RequestContextSlot.APPSEC);
750755
callbackProviderIast = instrumentationGateway.getCallbackProvider(RequestContextSlot.IAST);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package datadog.trace.core.traceinterceptor;
2+
3+
import datadog.trace.api.Config;
4+
import datadog.trace.api.DDTags;
5+
import datadog.trace.api.interceptor.AbstractTraceInterceptor;
6+
import datadog.trace.api.interceptor.MutableSpan;
7+
import datadog.trace.api.interceptor.TraceInterceptor;
8+
import java.util.Collection;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
12+
public class LatencyTraceInterceptor extends AbstractTraceInterceptor {
13+
private static final Logger log = LoggerFactory.getLogger(LatencyTraceInterceptor.class);
14+
// duration configured in ms, need to be converted in nano seconds
15+
private static final int LATENCY = Config.get().getTraceLatencyInterceptorValue() * 1000000;
16+
17+
public static final TraceInterceptor INSTANCE =
18+
new LatencyTraceInterceptor(Priority.ROOT_SPAN_LATENCY);
19+
20+
protected LatencyTraceInterceptor(Priority priority) {
21+
super(priority);
22+
}
23+
24+
@Override
25+
public Collection<? extends MutableSpan> onTraceComplete(
26+
Collection<? extends MutableSpan> latencyTrace) {
27+
if (latencyTrace.isEmpty()) {
28+
return latencyTrace;
29+
}
30+
MutableSpan rootSpan = latencyTrace.iterator().next().getLocalRootSpan();
31+
if (rootSpan != null && rootSpan.getDurationNano() > LATENCY) {
32+
rootSpan.setTag(DDTags.MANUAL_KEEP, true);
33+
}
34+
return latencyTrace;
35+
}
36+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package datadog.trace.core.traceinterceptor
2+
3+
import datadog.trace.api.DDTags
4+
import datadog.trace.common.writer.ListWriter
5+
6+
import datadog.trace.core.test.DDCoreSpecification
7+
8+
import spock.lang.Timeout
9+
10+
@Timeout(10)
11+
class LatencyTraceInterceptorTest extends DDCoreSpecification {
12+
13+
14+
def "test set sampling priority according to latency"() {
15+
setup:
16+
def writer = new ListWriter()
17+
def props = new Properties()
18+
props.setProperty("trace.partial.flush.enabled", partialFlushEnabled)
19+
props.setProperty("trace.latency.interceptor.value", latencyThreshold)
20+
21+
def tracer = tracerBuilder().withProperties(props).writer(writer).build()
22+
23+
def spanSetup = tracer.buildSpan("test","my_operation_name").withTag(tagname, true).start()
24+
sleep(duration)
25+
spanSetup.finish()
26+
27+
expect:
28+
def trace = writer.firstTrace()
29+
trace.size() == 1
30+
def span = trace[0]
31+
32+
span.context().getSamplingPriority() == expected
33+
34+
tracer.close()
35+
where:
36+
partialFlushEnabled | latencyThreshold | tagname | duration | expected
37+
"true" | "102" | DDTags.MANUAL_KEEP | 100 | 2
38+
"true" | "102" | DDTags.MANUAL_DROP | 100 | -1
39+
"true" | "102" | DDTags.MANUAL_KEEP | 105 | 2
40+
"true" | "102" | DDTags.MANUAL_DROP | 105 | -1
41+
// "false" | "102" | DDTags.MANUAL_KEEP | 100 | 2
42+
// "false" | "102" | DDTags.MANUAL_DROP | 100 | -1
43+
// "false" | "102" | DDTags.MANUAL_KEEP | 105 | 2
44+
// "false" | "102" | DDTags.MANUAL_DROP | 105 | 2
45+
}
46+
}

internal-api/src/main/java/datadog/trace/api/Config.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ public static String getHostName() {
176176
private final boolean scopeStrictMode;
177177
private final int scopeIterationKeepAlive;
178178
private final int partialFlushMinSpans;
179+
private final int traceLatencyInterceptorValue;
180+
private final boolean traceLatencyInterceptorEnabled;
179181
private final boolean traceStrictWritesEnabled;
180182
private final boolean logExtractHeaderNames;
181183
private final Set<PropagationStyle> propagationStylesToExtract;
@@ -860,6 +862,12 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins
860862
? 0
861863
: configProvider.getInteger(PARTIAL_FLUSH_MIN_SPANS, DEFAULT_PARTIAL_FLUSH_MIN_SPANS);
862864

865+
traceLatencyInterceptorValue =
866+
configProvider.getInteger(
867+
TRACE_LATENCY_INTERCEPTOR_VALUE, DEFAULT_TRACE_LATENCY_INTERCEPTOR_VALUE);
868+
869+
traceLatencyInterceptorEnabled = !partialFlushEnabled && (traceLatencyInterceptorValue >= 0);
870+
863871
traceStrictWritesEnabled = configProvider.getBoolean(TRACE_STRICT_WRITES_ENABLED, false);
864872

865873
logExtractHeaderNames =
@@ -2075,6 +2083,14 @@ public int getPartialFlushMinSpans() {
20752083
return partialFlushMinSpans;
20762084
}
20772085

2086+
public int getTraceLatencyInterceptorValue() {
2087+
return traceLatencyInterceptorValue;
2088+
}
2089+
2090+
public boolean isTraceLatencyInterceptorEnabled() {
2091+
return traceLatencyInterceptorEnabled;
2092+
}
2093+
20782094
public boolean isTraceStrictWritesEnabled() {
20792095
return traceStrictWritesEnabled;
20802096
}
@@ -4158,6 +4174,10 @@ public String toString() {
41584174
+ scopeIterationKeepAlive
41594175
+ ", partialFlushMinSpans="
41604176
+ partialFlushMinSpans
4177+
+ ", traceLatencyInterceptorEnabled="
4178+
+ traceLatencyInterceptorEnabled
4179+
+ ", traceLatencyInterceptorValue="
4180+
+ traceLatencyInterceptorValue
41614181
+ ", traceStrictWritesEnabled="
41624182
+ traceStrictWritesEnabled
41634183
+ ", tracePropagationStylesToExtract="

0 commit comments

Comments
 (0)