Skip to content

Commit 3f98bc4

Browse files
committed
Add an error sampler for Log probes
log template only probe are rate limited to a higher rate. if condition is set and returns an error, we don't want to return a snapshot at the same rate just for reporting the condition evaluation failure. So we are introducing a specific sampler at 1/s rate to report error in that case.
1 parent af8b844 commit 3f98bc4

2 files changed

Lines changed: 67 additions & 3 deletions

File tree

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ public String toString() {
325325
protected transient Map<DDTraceId, AtomicInteger> budget =
326326
Collections.synchronizedMap(new WeakIdentityHashMap<>());
327327
protected transient Sampler sampler;
328+
protected transient Sampler errorSampler;
328329

329330
// no-arg constructor is required by Moshi to avoid creating instance with unsafe and by-passing
330331
// constructors, including field initializers.
@@ -461,6 +462,7 @@ public void initSamplers() {
461462
? ProbeRateLimiter.DEFAULT_SNAPSHOT_RATE
462463
: ProbeRateLimiter.DEFAULT_LOG_RATE);
463464
sampler = ProbeRateLimiter.createSampler(rate);
465+
errorSampler = ProbeRateLimiter.createSampler(1.0); // errors are always sampled at 1/s rate
464466
}
465467

466468
public List<CaptureExpression> getCaptureExpressions() {
@@ -565,9 +567,13 @@ private void sample(LogStatus logStatus, MethodLocation methodLocation) {
565567
if (!MethodLocation.isSame(methodLocation, evaluateAt)) {
566568
return;
567569
}
570+
// if condition has error and no capture Snapshot, the error is reported using errorSampler
571+
// at 1/s rate instead of the log template one
572+
Sampler localSampler =
573+
logStatus.hasConditionErrors && !isCaptureSnapshot() ? errorSampler : sampler;
568574
boolean sampled =
569575
!logStatus.getDebugSessionStatus().isDisabled()
570-
&& ProbeRateLimiter.tryProbe(sampler, isCaptureSnapshot());
576+
&& ProbeRateLimiter.tryProbe(localSampler, isCaptureSnapshot());
571577
logStatus.setSampled(sampled);
572578
if (!sampled) {
573579
DebuggerAgent.getSink()

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

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import java.util.List;
7474
import java.util.Map;
7575
import java.util.Optional;
76+
import java.util.function.DoubleConsumer;
7677
import java.util.stream.Collectors;
7778
import org.jetbrains.kotlin.com.intellij.util.lang.JavaVersion;
7879
import org.joor.Reflect;
@@ -1296,6 +1297,35 @@ public void nullCondition() throws IOException, URISyntaxException {
12961297
assertEquals("Cannot dereference field: fld", snapshot.getMessage());
12971298
}
12981299

1300+
@Test
1301+
public void nullConditionTemplateOnly() throws IOException, URISyntaxException {
1302+
final String CLASS_NAME = "CapturedSnapshot08";
1303+
LogProbe logProbes =
1304+
createProbeBuilder(PROBE_ID, CLASS_NAME, "doit", "int (java.lang.String)")
1305+
.when(
1306+
new ProbeCondition(
1307+
DSL.when(
1308+
DSL.eq(
1309+
DSL.getMember(
1310+
DSL.getMember(DSL.getMember(DSL.ref("nullTyped"), "fld"), "fld"),
1311+
"msg"),
1312+
DSL.value("hello"))),
1313+
"nullTyped.fld.fld.msg == 'hello'"))
1314+
.captureSnapshot(false)
1315+
.template("plain log", Collections.emptyList())
1316+
.build();
1317+
TestSnapshotListener listener = installProbes(logProbes);
1318+
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
1319+
int result = Reflect.onClass(testClass).call("main", "1").get();
1320+
assertEquals(3, result);
1321+
Snapshot snapshot = assertOneSnapshot(listener);
1322+
assertEquals("Cannot dereference field: fld", snapshot.getMessage());
1323+
List<EvaluationError> evaluationErrors = snapshot.getEvaluationErrors();
1324+
assertEquals(1, evaluationErrors.size());
1325+
assertEquals("nullTyped.fld.fld", evaluationErrors.get(0).getExpr());
1326+
assertEquals("Cannot dereference field: fld", evaluationErrors.get(0).getMessage());
1327+
}
1328+
12991329
@Test
13001330
public void shortCircuitingCondition() throws IOException, URISyntaxException {
13011331
final String CLASS_NAME = "CapturedSnapshot08";
@@ -2676,19 +2706,47 @@ public void ensureCallingSamplingLineProbeCondition() throws IOException, URISyn
26762706
doSamplingTest(this::lineProbeCondition, 1, 1);
26772707
}
26782708

2709+
@Test
2710+
public void ensureCallingSamplingLogTemplateOnlyConditionError()
2711+
throws IOException, URISyntaxException {
2712+
doSamplingTest(this::nullConditionTemplateOnly, ProbeRateLimiter::setGlobalLogRate, 1, 0, 1);
2713+
}
2714+
26792715
private void doSamplingTest(TestMethod testRun, int expectedGlobalCount, int expectedProbeCount)
26802716
throws IOException, URISyntaxException {
2717+
doSamplingTest(
2718+
testRun,
2719+
ProbeRateLimiter::setGlobalSnapshotRate,
2720+
expectedGlobalCount,
2721+
expectedProbeCount,
2722+
0);
2723+
}
2724+
2725+
private void doSamplingTest(
2726+
TestMethod testRun,
2727+
DoubleConsumer globalRateSetter,
2728+
int expectedGlobalCount,
2729+
int expectedProbeCount,
2730+
int expectedErrorCount)
2731+
throws IOException, URISyntaxException {
26812732
MockSampler probeSampler = new MockSampler();
2733+
MockSampler errorSampler = new MockSampler();
26822734
MockSampler globalSampler = new MockSampler();
2683-
ProbeRateLimiter.setSamplerSupplier(rate -> rate < 101 ? probeSampler : globalSampler);
2684-
ProbeRateLimiter.setGlobalSnapshotRate(1000);
2735+
ProbeRateLimiter.setSamplerSupplier(
2736+
rate -> {
2737+
if (rate < 2) return errorSampler;
2738+
if (rate < 101) return probeSampler;
2739+
return globalSampler;
2740+
});
2741+
globalRateSetter.accept(1000);
26852742
try {
26862743
testRun.run();
26872744
} finally {
26882745
ProbeRateLimiter.setSamplerSupplier(null);
26892746
}
26902747
assertEquals(expectedGlobalCount, globalSampler.getCallCount());
26912748
assertEquals(expectedProbeCount, probeSampler.getCallCount());
2749+
assertEquals(expectedErrorCount, errorSampler.getCallCount());
26922750
}
26932751

26942752
@Test

0 commit comments

Comments
 (0)