Skip to content

Commit 77900be

Browse files
authored
Add an error sampler for Log probes (#10638)
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. Co-authored-by: jean-philippe.bempel <jean-philippe.bempel@datadoghq.com>
1 parent e894b34 commit 77900be

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
@@ -75,6 +75,7 @@
7575
import java.util.List;
7676
import java.util.Map;
7777
import java.util.Optional;
78+
import java.util.function.DoubleConsumer;
7879
import java.util.stream.Collectors;
7980
import org.jetbrains.kotlin.com.intellij.util.lang.JavaVersion;
8081
import org.joor.Reflect;
@@ -1299,6 +1300,35 @@ public void nullCondition() throws IOException, URISyntaxException {
12991300
assertEquals("Cannot dereference field: fld", snapshot.getMessage());
13001301
}
13011302

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

2712+
@Test
2713+
public void ensureCallingSamplingLogTemplateOnlyConditionError()
2714+
throws IOException, URISyntaxException {
2715+
doSamplingTest(this::nullConditionTemplateOnly, ProbeRateLimiter::setGlobalLogRate, 1, 0, 1);
2716+
}
2717+
26822718
private void doSamplingTest(TestMethod testRun, int expectedGlobalCount, int expectedProbeCount)
26832719
throws IOException, URISyntaxException {
2720+
doSamplingTest(
2721+
testRun,
2722+
ProbeRateLimiter::setGlobalSnapshotRate,
2723+
expectedGlobalCount,
2724+
expectedProbeCount,
2725+
0);
2726+
}
2727+
2728+
private void doSamplingTest(
2729+
TestMethod testRun,
2730+
DoubleConsumer globalRateSetter,
2731+
int expectedGlobalCount,
2732+
int expectedProbeCount,
2733+
int expectedErrorCount)
2734+
throws IOException, URISyntaxException {
26842735
MockSampler probeSampler = new MockSampler();
2736+
MockSampler errorSampler = new MockSampler();
26852737
MockSampler globalSampler = new MockSampler();
2686-
ProbeRateLimiter.setSamplerSupplier(rate -> rate < 101 ? probeSampler : globalSampler);
2687-
ProbeRateLimiter.setGlobalSnapshotRate(1000);
2738+
ProbeRateLimiter.setSamplerSupplier(
2739+
rate -> {
2740+
if (rate < 2) return errorSampler;
2741+
if (rate < 101) return probeSampler;
2742+
return globalSampler;
2743+
});
2744+
globalRateSetter.accept(1000);
26882745
try {
26892746
testRun.run();
26902747
} finally {
26912748
ProbeRateLimiter.setSamplerSupplier(null);
26922749
}
26932750
assertEquals(expectedGlobalCount, globalSampler.getCallCount());
26942751
assertEquals(expectedProbeCount, probeSampler.getCallCount());
2752+
assertEquals(expectedErrorCount, errorSampler.getCallCount());
26952753
}
26962754

26972755
@Test

0 commit comments

Comments
 (0)