Skip to content

Commit 5010e26

Browse files
authored
Enhance log probes to honor debug session tags (#8215)
Enhance log probes to honor debug session tags
1 parent fbb36f9 commit 5010e26

File tree

9 files changed

+278
-39
lines changed

9 files changed

+278
-39
lines changed

dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,26 @@ public class DebuggerContext {
2121
private static final ThreadLocal<Boolean> IN_PROBE = ThreadLocal.withInitial(() -> Boolean.FALSE);
2222

2323
public enum SkipCause {
24-
RATE,
25-
CONDITION
24+
RATE {
25+
@Override
26+
public String tag() {
27+
return "cause:rate";
28+
}
29+
},
30+
CONDITION {
31+
@Override
32+
public String tag() {
33+
return "cause:condition";
34+
}
35+
},
36+
DEBUG_SESSION_DISABLED {
37+
@Override
38+
public String tag() {
39+
return "cause:debug session disabled";
40+
}
41+
};
42+
43+
public abstract String tag();
2644
}
2745

2846
public interface ProbeResolver {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.datadog.debugger.probe;
2+
3+
public enum DebugSessionStatus {
4+
NONE,
5+
ACTIVE,
6+
DISABLED;
7+
8+
public boolean isDisabled() {
9+
return this == DISABLED;
10+
}
11+
}

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,21 @@
3232
import datadog.trace.bootstrap.debugger.ProbeImplementation;
3333
import datadog.trace.bootstrap.debugger.ProbeRateLimiter;
3434
import datadog.trace.bootstrap.debugger.util.TimeoutChecker;
35+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
36+
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
37+
import datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI;
38+
import datadog.trace.core.DDSpan;
39+
import datadog.trace.core.DDSpanContext;
3540
import java.io.IOException;
3641
import java.lang.reflect.ParameterizedType;
3742
import java.time.Duration;
3843
import java.time.temporal.ChronoUnit;
3944
import java.util.ArrayList;
4045
import java.util.Arrays;
4146
import java.util.Collections;
47+
import java.util.HashMap;
4248
import java.util.List;
49+
import java.util.Map;
4350
import java.util.Objects;
4451
import java.util.function.Consumer;
4552
import org.slf4j.Logger;
@@ -51,6 +58,30 @@ public class LogProbe extends ProbeDefinition implements Sampled {
5158
private static final Limits LIMITS = new Limits(1, 3, 8192, 5);
5259
private static final int LOG_MSG_LIMIT = 8192;
5360

61+
private static Map<String, String> getDebugSessions() {
62+
HashMap<String, String> sessions = new HashMap<>();
63+
TracerAPI tracer = AgentTracer.get();
64+
if (tracer != null) {
65+
AgentSpan span = tracer.activeSpan();
66+
if (span instanceof DDSpan) {
67+
DDSpanContext context = (DDSpanContext) span.context();
68+
String debug = context.getPropagationTags().getDebugPropagation();
69+
if (debug != null) {
70+
String[] entries = debug.split(",");
71+
for (String entry : entries) {
72+
if (!entry.contains(":")) {
73+
sessions.put("*", entry);
74+
} else {
75+
String[] values = entry.split(":");
76+
sessions.put(values[0], values[1]);
77+
}
78+
}
79+
}
80+
}
81+
}
82+
return sessions;
83+
}
84+
5485
/** Stores part of a templated message either a str or an expression */
5586
public static class Segment {
5687
private final String str;
@@ -503,10 +534,16 @@ private void sample(LogStatus logStatus, MethodLocation methodLocation) {
503534
if (!MethodLocation.isSame(methodLocation, evaluateAt)) {
504535
return;
505536
}
506-
boolean sampled = ProbeRateLimiter.tryProbe(id);
537+
boolean sampled =
538+
!logStatus.getDebugSessionStatus().isDisabled() && ProbeRateLimiter.tryProbe(id);
507539
logStatus.setSampled(sampled);
508540
if (!sampled) {
509-
DebuggerAgent.getSink().skipSnapshot(id, DebuggerContext.SkipCause.RATE);
541+
DebuggerAgent.getSink()
542+
.skipSnapshot(
543+
id,
544+
logStatus.getDebugSessionStatus().isDisabled()
545+
? DebuggerContext.SkipCause.DEBUG_SESSION_DISABLED
546+
: DebuggerContext.SkipCause.RATE);
510547
}
511548
}
512549

@@ -673,6 +710,10 @@ public boolean hasCondition() {
673710
return probeCondition != null;
674711
}
675712

713+
protected String getDebugSessionId() {
714+
return getTagMap().get("session_id");
715+
}
716+
676717
@Override
677718
public CapturedContext.Status createStatus() {
678719
return new LogStatus(this);
@@ -685,6 +726,7 @@ public static class LogStatus extends CapturedContext.Status {
685726
new LogStatus(ProbeImplementation.UNKNOWN, true);
686727

687728
private boolean condition = true;
729+
private final DebugSessionStatus debugSessionStatus;
688730
private boolean hasLogTemplateErrors;
689731
private boolean hasConditionErrors;
690732
private boolean sampled = true;
@@ -693,10 +735,11 @@ public static class LogStatus extends CapturedContext.Status {
693735

694736
public LogStatus(ProbeImplementation probeImplementation) {
695737
super(probeImplementation);
738+
debugSessionStatus = debugSessionStatus();
696739
}
697740

698741
private LogStatus(ProbeImplementation probeImplementation, boolean condition) {
699-
super(probeImplementation);
742+
this(probeImplementation);
700743
this.condition = condition;
701744
}
702745

@@ -711,7 +754,11 @@ public boolean isCapturing() {
711754
}
712755

713756
public boolean shouldSend() {
714-
return sampled && condition && !hasConditionErrors;
757+
DebugSessionStatus status = getDebugSessionStatus();
758+
// an ACTIVE status overrides the sampling as the sampling decision was made by the trigger
759+
// probe
760+
return status == DebugSessionStatus.ACTIVE
761+
|| !status.isDisabled() && sampled && condition && !hasConditionErrors;
715762
}
716763

717764
public boolean shouldReportError() {
@@ -722,6 +769,26 @@ public boolean getCondition() {
722769
return condition;
723770
}
724771

772+
public DebugSessionStatus getDebugSessionStatus() {
773+
return debugSessionStatus;
774+
}
775+
776+
private DebugSessionStatus debugSessionStatus() {
777+
if (probeImplementation instanceof LogProbe) {
778+
LogProbe definition = (LogProbe) probeImplementation;
779+
Map<String, String> sessions = getDebugSessions();
780+
String sessionId = definition.getDebugSessionId();
781+
if (sessionId == null) {
782+
return DebugSessionStatus.NONE;
783+
}
784+
return "1".equals(sessions.get(sessionId)) || "1".equals(sessions.get("*"))
785+
? DebugSessionStatus.ACTIVE
786+
: DebugSessionStatus.DISABLED;
787+
}
788+
789+
return DebugSessionStatus.NONE;
790+
}
791+
725792
public void setCondition(boolean value) {
726793
this.condition = value;
727794
}
@@ -765,6 +832,29 @@ public boolean isForceSampling() {
765832
public void setForceSampling(boolean forceSampling) {
766833
this.forceSampling = forceSampling;
767834
}
835+
836+
@Override
837+
public String toString() {
838+
return "LogStatus{"
839+
+ ", probeId="
840+
+ probeImplementation.getId()
841+
+ ", condition="
842+
+ condition
843+
+ ", debugSessionStatus="
844+
+ debugSessionStatus
845+
+ ", forceSampling="
846+
+ forceSampling
847+
+ ", hasConditionErrors="
848+
+ hasConditionErrors
849+
+ ", hasLogTemplateErrors="
850+
+ hasLogTemplateErrors
851+
+ ", message='"
852+
+ message
853+
+ '\''
854+
+ ", sampled="
855+
+ sampled
856+
+ '}';
857+
}
768858
}
769859

770860
@Generated

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinition.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ public String concatTags() {
9393
}
9494

9595
public Map<String, String> getTagMap() {
96+
if (tagMap.isEmpty() && tags != null) {
97+
initTagMap(tagMap, tags);
98+
}
9699
return Collections.unmodifiableMap(tagMap);
97100
}
98101

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/TriggerProbe.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ public InstrumentationResult.Status instrument(
5959
.instrument();
6060
}
6161

62+
public String getSessionId() {
63+
return sessionId;
64+
}
65+
66+
public TriggerProbe setSessionId(String sessionId) {
67+
this.sessionId = sessionId;
68+
return this;
69+
}
70+
6271
public Sampling getSampling() {
6372
return sampling;
6473
}
@@ -110,7 +119,7 @@ private void decorateTags() {
110119
AgentTracer.TracerAPI tracerAPI = AgentTracer.get();
111120

112121
AgentSpan agentSpan = tracerAPI.activeSpan().getLocalRootSpan();
113-
agentSpan.setTag(Tags.PROPAGATED_DEBUG, "1");
122+
agentSpan.setTag(Tags.PROPAGATED_DEBUG, sessionId + ":1");
114123
agentSpan.setTag(format("_dd.ld.probe_id.%s", probeId.getId()), true);
115124
}
116125

@@ -148,8 +157,20 @@ public int hashCode() {
148157

149158
@Override
150159
public String toString() {
151-
return format(
152-
"TriggerProbe{id='%s', where=%s, sampling=%s, probeCondition=%s}",
153-
id, where, sampling, probeCondition);
160+
return String.format(
161+
"TriggerProbe{id='%s', sessionId='%s', evaluateAt=%s, language='%s', location=%s, probeCondition=%s, probeId=%s,"
162+
+ " sampling=%s, tagMap=%s, tags=%s, version=%d, where=%s}",
163+
id,
164+
sessionId,
165+
evaluateAt,
166+
language,
167+
location,
168+
probeCondition,
169+
probeId,
170+
sampling,
171+
tagMap,
172+
Arrays.toString(tags),
173+
version,
174+
where);
154175
}
155176
}

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/DebuggerSink.java

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import com.datadog.debugger.uploader.BatchUploader;
55
import com.datadog.debugger.util.DebuggerMetrics;
66
import datadog.trace.api.Config;
7-
import datadog.trace.bootstrap.debugger.DebuggerContext;
7+
import datadog.trace.bootstrap.debugger.DebuggerContext.SkipCause;
88
import datadog.trace.bootstrap.debugger.ProbeId;
99
import datadog.trace.util.AgentTaskScheduler;
1010
import java.util.List;
@@ -223,20 +223,8 @@ private void reportError(ProbeId probeId, DiagnosticMessage msg) {
223223
}
224224

225225
/** Notifies the snapshot was skipped for one of the SkipCause reason */
226-
public void skipSnapshot(String probeId, DebuggerContext.SkipCause cause) {
227-
String causeTag;
228-
switch (cause) {
229-
case RATE:
230-
causeTag = "cause:rate";
231-
break;
232-
case CONDITION:
233-
causeTag = "cause:condition";
234-
break;
235-
default:
236-
throw new IllegalArgumentException("Unknown cause: " + cause);
237-
}
238-
String probeIdTag = "probe_id:" + probeId;
239-
debuggerMetrics.incrementCounter(PREFIX + "skip", causeTag, probeIdTag);
226+
public void skipSnapshot(String probeId, SkipCause cause) {
227+
debuggerMetrics.incrementCounter(PREFIX + "skip", cause.tag(), "probe_id:" + probeId);
240228
}
241229

242230
long getCurrentLowRateFlushInterval() {

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturingTestBase.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -352,14 +352,7 @@ public static LogProbe.Builder createProbeBuilder(
352352
protected TestSnapshotListener installProbes(
353353
Configuration configuration, ProbeDefinition... probes) {
354354

355-
config = mock(Config.class);
356-
when(config.isDebuggerEnabled()).thenReturn(true);
357-
when(config.isDebuggerClassFileDumpEnabled()).thenReturn(true);
358-
when(config.isDebuggerVerifyByteCode()).thenReturn(false);
359-
when(config.getFinalDebuggerSnapshotUrl())
360-
.thenReturn("http://localhost:8126/debugger/v1/input");
361-
when(config.getFinalDebuggerSymDBUrl()).thenReturn("http://localhost:8126/symdb/v1/input");
362-
when(config.getDebuggerCodeOriginMaxUserFrames()).thenReturn(20);
355+
config = mockConfig();
363356
instrumentationListener = new MockInstrumentationListener();
364357
probeStatusSink = mock(ProbeStatusSink.class);
365358

@@ -404,6 +397,19 @@ protected TestSnapshotListener installProbes(
404397
return listener;
405398
}
406399

400+
public static Config mockConfig() {
401+
Config config = mock(Config.class);
402+
when(config.isDebuggerEnabled()).thenReturn(true);
403+
when(config.isDebuggerClassFileDumpEnabled()).thenReturn(true);
404+
when(config.isDebuggerVerifyByteCode()).thenReturn(false);
405+
when(config.getFinalDebuggerSnapshotUrl())
406+
.thenReturn("http://localhost:8126/debugger/v1/input");
407+
when(config.getFinalDebuggerSymDBUrl()).thenReturn("http://localhost:8126/symdb/v1/input");
408+
when(config.getDebuggerCodeOriginMaxUserFrames()).thenReturn(20);
409+
410+
return config;
411+
}
412+
407413
public static ProbeImplementation resolver(
408414
String encodedId,
409415
Collection<LogProbe> logProbes,

0 commit comments

Comments
 (0)