From 0d477d6a3e5ba70ed475eeb2a8545b1e92d9840f Mon Sep 17 00:00:00 2001 From: jean-philippe bempel Date: Thu, 4 Jun 2026 21:29:42 +0200 Subject: [PATCH 1/2] Fix sampling log probe with capture expressions A log probe with CaptureExpressions has captureSnapshot=false but this is not a log template so the sampling must be 1/s like a regular snapshot --- .../com/datadog/debugger/probe/LogProbe.java | 6 +- .../debugger/agent/CapturedSnapshotTest.java | 235 ++++++++++++------ 2 files changed, 160 insertions(+), 81 deletions(-) diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java index 5647cb371b1..bb72a718e12 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java @@ -502,7 +502,7 @@ public InstrumentationResult.Status instrument( public boolean isReadyToCapture() { if (!hasCondition()) { // we are sampling here to avoid creating CapturedContext when the sampling result is negative - return ProbeRateLimiter.tryProbe(sampler, isCaptureSnapshot()); + return ProbeRateLimiter.tryProbe(sampler, isFullSnapshot()); } return true; } @@ -570,10 +570,10 @@ private void sample(LogStatus logStatus, MethodLocation methodLocation) { // if condition has error and no capture Snapshot, the error is reported using errorSampler // at 1/s rate instead of the log template one Sampler localSampler = - logStatus.hasConditionErrors && !isCaptureSnapshot() ? errorSampler : sampler; + logStatus.hasConditionErrors && !isFullSnapshot() ? errorSampler : sampler; boolean sampled = !logStatus.getDebugSessionStatus().isDisabled() - && ProbeRateLimiter.tryProbe(localSampler, isCaptureSnapshot()); + && ProbeRateLimiter.tryProbe(localSampler, isFullSnapshot()); logStatus.setSampled(sampled); if (!sampled) { DebuggerAgent.getSink() diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java index d40fe6659d1..ae2500c3fa2 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java @@ -1,5 +1,8 @@ package com.datadog.debugger.agent; +import static com.datadog.debugger.el.DSL.not; +import static com.datadog.debugger.el.DSL.nullValue; +import static com.datadog.debugger.el.DSL.ref; import static com.datadog.debugger.util.LogProbeTestHelper.parseTemplate; import static com.datadog.debugger.util.MoshiSnapshotHelper.DEPTH_REASON; import static com.datadog.debugger.util.MoshiSnapshotHelper.FIELD_COUNT_REASON; @@ -172,8 +175,7 @@ public void methodProbeAtExitWithCondition() throws IOException, URISyntaxExcept final String CLASS_NAME = "CapturedSnapshot01"; LogProbe probe = createProbeBuilder(PROBE_ID, CLASS_NAME, "main", "int (java.lang.String)") - .when( - new ProbeCondition(DSL.when(DSL.eq(DSL.ref("arg"), DSL.value("1"))), "arg == '1'")) + .when(new ProbeCondition(DSL.when(DSL.eq(ref("arg"), DSL.value("1"))), "arg == '1'")) .evaluateAt(MethodLocation.EXIT) .build(); TestSnapshotListener listener = installProbes(probe); @@ -1029,13 +1031,12 @@ public void uncaughtExceptionCondition() throws IOException, URISyntaxException DSL.when( DSL.and( DSL.instanceOf( - DSL.ref("@exception"), - DSL.value("CapturedSnapshot05$CustomException")), + ref("@exception"), DSL.value("CapturedSnapshot05$CustomException")), DSL.eq( - DSL.getMember(DSL.ref("@exception"), "detailMessage"), + DSL.getMember(ref("@exception"), "detailMessage"), DSL.value("oops")), DSL.eq( - DSL.getMember(DSL.ref("@exception"), "additionalMsg"), + DSL.getMember(ref("@exception"), "additionalMsg"), DSL.value("I did it again")))), "@exception instanceof \"CapturedSnapshot05$CustomException\" and @exception.detailMessage == 'oops' and @exception.additionalMsg == 'I did it again'")) .template(LOG_TEMPLATE, parseTemplate(LOG_TEMPLATE)) @@ -1125,8 +1126,7 @@ public void noUncaughtExceptionCondition() throws IOException, URISyntaxExceptio .evaluateAt(MethodLocation.EXIT) .when( new ProbeCondition( - DSL.when(DSL.not(DSL.isDefined(DSL.ref("@exception")))), - "not(isDefined(@exception))")) + DSL.when(not(DSL.isDefined(ref("@exception")))), "not(isDefined(@exception))")) .template(LOG_TEMPLATE, parseTemplate(LOG_TEMPLATE)) .build(); TestSnapshotListener listener = installProbes(probe); @@ -1188,17 +1188,16 @@ public void simpleConditionTest() throws IOException, URISyntaxException { // this is always true DSL.and( // this reference is resolved directly from the snapshot - DSL.eq(DSL.ref("fld"), DSL.value(11)), + DSL.eq(ref("fld"), DSL.value(11)), // this reference chain needs to use reflection DSL.eq( DSL.getMember( - DSL.getMember( - DSL.getMember(DSL.ref("typed"), "fld"), "fld"), + DSL.getMember(DSL.getMember(ref("typed"), "fld"), "fld"), "msg"), DSL.value("hello"))), DSL.and( - DSL.eq(DSL.ref("arg"), DSL.value("5")), - DSL.ge(DSL.ref(ValueReferences.DURATION_REF), DSL.value(0L))))), + DSL.eq(ref("arg"), DSL.value("5")), + DSL.ge(ref(ValueReferences.DURATION_REF), DSL.value(0L))))), "(fld == 11 && typed.fld.fld.msg == \"hello\") && (arg == '5' && @duration >= 0)")) .evaluateAt(MethodLocation.EXIT) .build(); @@ -1226,15 +1225,14 @@ public void lineProbeCondition() throws IOException, URISyntaxException { // this is always true DSL.and( // this reference is resolved directly from the snapshot - DSL.eq(DSL.ref("fld"), DSL.value(11)), + DSL.eq(ref("fld"), DSL.value(11)), // this reference chain needs to use reflection DSL.eq( DSL.getMember( - DSL.getMember( - DSL.getMember(DSL.ref("typed"), "fld"), "fld"), + DSL.getMember(DSL.getMember(ref("typed"), "fld"), "fld"), "msg"), DSL.value("hello"))), - DSL.eq(DSL.ref("arg"), DSL.value("5")))), + DSL.eq(ref("arg"), DSL.value("5")))), "(fld == 11 && typed.fld.fld.msg == \"hello\") && arg == '5'")) .build(); TestSnapshotListener listener = installProbes(logProbe); @@ -1258,7 +1256,7 @@ public void staticFieldCondition() throws IOException, URISyntaxException { createProbeBuilder(PROBE_ID, CLASS_NAME, "process", "int (java.lang.String)") .when( new ProbeCondition( - DSL.when(DSL.eq(DSL.ref("strField"), DSL.value("foo"))), "strField == 'foo'")) + DSL.when(DSL.eq(ref("strField"), DSL.value("foo"))), "strField == 'foo'")) .evaluateAt(MethodLocation.EXIT) .build(); TestSnapshotListener listener = installProbes(logProbe); @@ -1282,8 +1280,7 @@ public void simpleFalseConditionTest() throws IOException, URISyntaxException { int line = getLineForLineProbe(CLASS_NAME, LINE_PROBE_ID2); LogProbe logProbe = createProbeBuilder(LINE_PROBE_ID2, CLASS_NAME, line) - .when( - new ProbeCondition(DSL.when(DSL.eq(DSL.ref("arg"), DSL.value("5"))), "arg == '5'")) + .when(new ProbeCondition(DSL.when(DSL.eq(ref("arg"), DSL.value("5"))), "arg == '5'")) .evaluateAt(MethodLocation.EXIT) .build(); TestSnapshotListener listener = installProbes(logProbe); @@ -1303,7 +1300,7 @@ public void nullCondition() throws IOException, URISyntaxException { DSL.when( DSL.eq( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("nullTyped"), "fld"), "fld"), + DSL.getMember(DSL.getMember(ref("nullTyped"), "fld"), "fld"), "msg"), DSL.value("hello"))), "nullTyped.fld.fld.msg == 'hello'")) @@ -1329,7 +1326,7 @@ public void nullConditionTemplateOnly() throws IOException, URISyntaxException { DSL.when( DSL.eq( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("nullTyped"), "fld"), "fld"), + DSL.getMember(DSL.getMember(ref("nullTyped"), "fld"), "fld"), "msg"), DSL.value("hello"))), "nullTyped.fld.fld.msg == 'hello'")) @@ -1357,9 +1354,9 @@ public void shortCircuitingCondition() throws IOException, URISyntaxException { new ProbeCondition( DSL.when( DSL.and( - DSL.isDefined(DSL.ref("@exception")), + DSL.isDefined(ref("@exception")), DSL.contains( - DSL.getMember(DSL.ref("@exception"), "detailMessage"), + DSL.getMember(ref("@exception"), "detailMessage"), new StringValue("closed")))), "isDefined(@exception) && contains(@exception.detailMessage, 'closed')")) .build(); @@ -1379,8 +1376,7 @@ public void wellKnownClassesCondition() throws IOException, URISyntaxException { .when( new ProbeCondition( DSL.when( - DSL.eq( - DSL.getMember(DSL.ref("maybeStr"), "value"), DSL.value("maybe foo"))), + DSL.eq(DSL.getMember(ref("maybeStr"), "value"), DSL.value("maybe foo"))), "maybeStr.value == 'maybe foo'")) .build(); TestSnapshotListener listener = installProbes(logProbes); @@ -1463,7 +1459,7 @@ public void mergedProbesConditionMainErrorAdditionalFalse() DSL.when( DSL.eq( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("nullTyped"), "fld"), "fld"), "msg"), + DSL.getMember(DSL.getMember(ref("nullTyped"), "fld"), "fld"), "msg"), DSL.value("hello"))), "nullTyped.fld.fld.msg == 'hello'"); ProbeCondition condition2 = new ProbeCondition(DSL.when(DSL.FALSE), "false"); @@ -1482,7 +1478,7 @@ public void mergedProbesConditionMainErrorAdditionalTrue() DSL.when( DSL.eq( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("nullTyped"), "fld"), "fld"), "msg"), + DSL.getMember(DSL.getMember(ref("nullTyped"), "fld"), "fld"), "msg"), DSL.value("hello"))), "nullTyped.fld.fld.msg == 'hello'"); ProbeCondition condition2 = new ProbeCondition(DSL.when(DSL.TRUE), "true"); @@ -1503,7 +1499,7 @@ public void mergedProbesConditionMainFalseAdditionalError() DSL.when( DSL.eq( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("nullTyped"), "fld"), "fld"), "msg"), + DSL.getMember(DSL.getMember(ref("nullTyped"), "fld"), "fld"), "msg"), DSL.value("hello"))), "nullTyped.fld.fld.msg == 'hello'"); List snapshots = doMergedProbeConditions(condition1, condition2, 1); @@ -1522,7 +1518,7 @@ public void mergedProbesConditionMainTrueAdditionalError() DSL.when( DSL.eq( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("nullTyped"), "fld"), "fld"), "msg"), + DSL.getMember(DSL.getMember(ref("nullTyped"), "fld"), "fld"), "msg"), DSL.value("hello"))), "nullTyped.fld.fld.msg == 'hello'"); List snapshots = doMergedProbeConditions(condition1, condition2, 2); @@ -1545,7 +1541,7 @@ public void mergedProbesConditionMixedLocation() throws IOException, URISyntaxEx createProbeBuilder(PROBE_ID2, CLASS_NAME, "doit", "int (java.lang.String)") .when( new ProbeCondition( - DSL.when(DSL.ge(DSL.ref("@duration"), DSL.value(0))), "@duration >= 0")) + DSL.when(DSL.ge(ref("@duration"), DSL.value(0))), "@duration >= 0")) .evaluateAt(MethodLocation.EXIT) .build(); TestSnapshotListener listener = installProbes(probe1, probe2); @@ -1587,14 +1583,12 @@ public void mergedProbesWithCaptureExpressionsMixed() throws IOException, URISyn "typed_fld_fld_msg", new ValueScript( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("typed"), "fld"), "fld"), - "msg"), + DSL.getMember(DSL.getMember(ref("typed"), "fld"), "fld"), "msg"), "typed.fld.fld.msg"), null), new LogProbe.CaptureExpression( "nullTyped_fld", - new ValueScript( - DSL.getMember(DSL.ref("nullTyped"), "fld"), "nullTyped.fld"), + new ValueScript(DSL.getMember(ref("nullTyped"), "fld"), "nullTyped.fld"), null))) .build(); LogProbe probe2 = createMethodProbeAtExit(PROBE_ID2, CLASS_NAME, "doit", null); @@ -1638,14 +1632,12 @@ public void mergedProbesWithDifferentCaptureExpressions() throws IOException, UR "typed_fld_fld_msg", new ValueScript( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("typed"), "fld"), "fld"), - "msg"), + DSL.getMember(DSL.getMember(ref("typed"), "fld"), "fld"), "msg"), "typed.fld.fld.msg"), null), new LogProbe.CaptureExpression( "nullTyped_fld", - new ValueScript( - DSL.getMember(DSL.ref("nullTyped"), "fld"), "nullTyped.fld"), + new ValueScript(DSL.getMember(ref("nullTyped"), "fld"), "nullTyped.fld"), null))) .build(); LogProbe probe2 = @@ -1655,10 +1647,10 @@ public void mergedProbesWithDifferentCaptureExpressions() throws IOException, UR .captureExpressions( Arrays.asList( new LogProbe.CaptureExpression( - "var1", new ValueScript(DSL.ref("var1"), "var1"), null), + "var1", new ValueScript(ref("var1"), "var1"), null), new LogProbe.CaptureExpression( "this_fld", - new ValueScript(DSL.getMember(DSL.ref("this"), "fld"), "this.fld"), + new ValueScript(DSL.getMember(ref("this"), "fld"), "this.fld"), null))) .build(); TestSnapshotListener listener = installProbes(probe1, probe2); @@ -1729,7 +1721,7 @@ public void inheritedFields() throws IOException, URISyntaxException { createProbeBuilder(PROBE_ID, INHERITED_CLASS_NAME, "f", "()") .when( new ProbeCondition( - DSL.when(DSL.eq(DSL.ref("intValue"), DSL.value(24))), "intValue == 24")) + DSL.when(DSL.eq(ref("intValue"), DSL.value(24))), "intValue == 24")) .evaluateAt(MethodLocation.ENTRY) .build(); TestSnapshotListener listener = installProbes(probe); @@ -1772,7 +1764,7 @@ public void staticInheritedFields() throws IOException, URISyntaxException { createProbeBuilder(PROBE_ID, INHERITED_CLASS_NAME, "f", "()") .when( new ProbeCondition( - DSL.when(DSL.eq(DSL.ref("intValue"), DSL.value(48))), "intValue == 48")) + DSL.when(DSL.eq(ref("intValue"), DSL.value(48))), "intValue == 48")) .evaluateAt(MethodLocation.EXIT) .build(); TestSnapshotListener listener = installProbes(logProbe); @@ -1997,8 +1989,7 @@ public void evaluateAtEntry() throws IOException, URISyntaxException { final String CLASS_NAME = "CapturedSnapshot01"; LogProbe logProbes = createProbeBuilder(PROBE_ID, CLASS_NAME, "main", "int (java.lang.String)") - .when( - new ProbeCondition(DSL.when(DSL.eq(DSL.ref("arg"), DSL.value("1"))), "arg == '1'")) + .when(new ProbeCondition(DSL.when(DSL.eq(ref("arg"), DSL.value("1"))), "arg == '1'")) .evaluateAt(MethodLocation.ENTRY) .build(); TestSnapshotListener listener = installProbes(logProbes); @@ -2014,8 +2005,7 @@ public void evaluateAtExit() throws IOException, URISyntaxException { LogProbe logProbes = createProbeBuilder(PROBE_ID, CLASS_NAME, "main", "int (java.lang.String)") .when( - new ProbeCondition( - DSL.when(DSL.eq(DSL.ref("@return"), DSL.value(3))), "@return == 3")) + new ProbeCondition(DSL.when(DSL.eq(ref("@return"), DSL.value(3))), "@return == 3")) .evaluateAt(MethodLocation.EXIT) .build(); TestSnapshotListener listener = installProbes(logProbes); @@ -2031,8 +2021,7 @@ public void evaluateAtExitFalse() throws IOException, URISyntaxException { LogProbe logProbes = createProbeBuilder(PROBE_ID, CLASS_NAME, "main", "int (java.lang.String)") .when( - new ProbeCondition( - DSL.when(DSL.eq(DSL.ref("@return"), DSL.value(0))), "@return == 0")) + new ProbeCondition(DSL.when(DSL.eq(ref("@return"), DSL.value(0))), "@return == 0")) .evaluateAt(MethodLocation.EXIT) .build(); TestSnapshotListener listener = installProbes(logProbes); @@ -2057,8 +2046,7 @@ public void uncaughtExceptionConditionLocalVar() throws IOException, URISyntaxEx final String CLASS_NAME = "CapturedSnapshot05"; LogProbe probe = createProbeBuilder(PROBE_ID, CLASS_NAME, "main", "(String)") - .when( - new ProbeCondition(DSL.when(DSL.ge(DSL.ref("after"), DSL.value(0))), "after >= 0")) + .when(new ProbeCondition(DSL.when(DSL.ge(ref("after"), DSL.value(0))), "after >= 0")) .evaluateAt(MethodLocation.EXIT) .build(); TestSnapshotListener listener = installProbes(probe); @@ -2370,10 +2358,10 @@ public void enumCondition() throws IOException, URISyntaxException { new ProbeCondition( DSL.when( DSL.and( - DSL.eq(DSL.ref("@return"), DSL.value("TWO")), - DSL.eq(DSL.ref("@return"), DSL.value("MyEnum.TWO")), + DSL.eq(ref("@return"), DSL.value("TWO")), + DSL.eq(ref("@return"), DSL.value("MyEnum.TWO")), DSL.eq( - DSL.ref("@return"), + ref("@return"), DSL.value("com.datadog.debugger.CapturedSnapshot23$MyEnum.TWO")))), "@return == 'TWO' && @return == 'MyEnum.TWO' && @return == 'com.datadog.debugger.CapturedSnapshot23$MyEnum.TWO'")) .build(); @@ -2437,7 +2425,7 @@ private Snapshot doUnknownCount(String CLASS_NAME) throws IOException, URISyntax createProbeBuilder(PROBE_ID, CLASS_NAME, "doit", null) .when( new ProbeCondition( - DSL.when(DSL.ge(DSL.len(DSL.ref("holder")), DSL.value(0))), "len(holder) >= 0")) + DSL.when(DSL.ge(DSL.len(ref("holder")), DSL.value(0))), "len(holder) >= 0")) .build(); TestSnapshotListener listener = installProbes(logProbe); Class testClass = compileAndLoadClass(CLASS_NAME); @@ -2535,7 +2523,7 @@ public void keywordRedactionConditions() throws IOException, URISyntaxException new ProbeCondition( DSL.when( DSL.contains( - DSL.getMember(DSL.ref("this"), "password"), new StringValue("123"))), + DSL.getMember(ref("this"), "password"), new StringValue("123"))), "contains(this.password, '123')")) .captureSnapshot(true) .evaluateAt(MethodLocation.EXIT) @@ -2544,7 +2532,7 @@ public void keywordRedactionConditions() throws IOException, URISyntaxException createProbeBuilder(PROBE_ID2, CLASS_NAME, "doit", null) .when( new ProbeCondition( - DSL.when(DSL.eq(DSL.ref("password"), DSL.value("123"))), "password == '123'")) + DSL.when(DSL.eq(ref("password"), DSL.value("123"))), "password == '123'")) .captureSnapshot(true) .evaluateAt(MethodLocation.EXIT) .build(); @@ -2553,8 +2541,7 @@ public void keywordRedactionConditions() throws IOException, URISyntaxException .when( new ProbeCondition( DSL.when( - DSL.eq( - DSL.index(DSL.ref("strMap"), DSL.value("password")), DSL.value("123"))), + DSL.eq(DSL.index(ref("strMap"), DSL.value("password")), DSL.value("123"))), "strMap['password'] == '123'")) .captureSnapshot(true) .evaluateAt(MethodLocation.EXIT) @@ -2652,7 +2639,7 @@ public void typeRedactionCondition() throws IOException, URISyntaxException { new ProbeCondition( DSL.when( DSL.contains( - DSL.getMember(DSL.getMember(DSL.ref("this"), "creds"), "secretCode"), + DSL.getMember(DSL.getMember(ref("this"), "creds"), "secretCode"), new StringValue("123"))), "contains(this.creds.secretCode, '123')")) .captureSnapshot(true) @@ -2662,8 +2649,7 @@ public void typeRedactionCondition() throws IOException, URISyntaxException { createProbeBuilder(PROBE_ID2, CLASS_NAME, "doit", null) .when( new ProbeCondition( - DSL.when( - DSL.eq(DSL.getMember(DSL.ref("creds"), "secretCode"), DSL.value("123"))), + DSL.when(DSL.eq(DSL.getMember(ref("creds"), "secretCode"), DSL.value("123"))), "creds.secretCode == '123'")) .captureSnapshot(true) .evaluateAt(MethodLocation.EXIT) @@ -2673,7 +2659,7 @@ public void typeRedactionCondition() throws IOException, URISyntaxException { .when( new ProbeCondition( DSL.when( - DSL.eq(DSL.index(DSL.ref("credMap"), DSL.value("dave")), DSL.value("123"))), + DSL.eq(DSL.index(ref("credMap"), DSL.value("dave")), DSL.value("123"))), "credMap['dave'] == '123'")) .captureSnapshot(true) .evaluateAt(MethodLocation.EXIT) @@ -2734,6 +2720,28 @@ public void ensureCallingSamplingLogTemplateOnlyConditionError() doSamplingTest(this::nullConditionTemplateOnly, ProbeRateLimiter::setGlobalLogRate, 1, 0, 1); } + @Test + public void ensureCallingSamplingCaptureExpression() throws IOException, URISyntaxException { + doSamplingTest(this::captureExpressions, 1, 1); + } + + @Test + public void ensureCallingSamplingCaptureExpressionWithCondition() + throws IOException, URISyntaxException { + doSamplingTest(this::captureExpressionsWithCondition, 1, 1); + } + + @Test + public void ensureCallingSamplingCaptureExpressionWithNullCondition() + throws IOException, URISyntaxException { + doSamplingTest( + this::captureExpressionsWithNullCondition, + ProbeRateLimiter::setGlobalSnapshotRate, + 1, + 1, + 0); + } + private void doSamplingTest(TestMethod testRun, int expectedGlobalCount, int expectedProbeCount) throws IOException, URISyntaxException { doSamplingTest( @@ -2900,15 +2908,52 @@ public void captureExpressions() throws IOException, URISyntaxException { "typed_fld_fld_msg", new ValueScript( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("typed"), "fld"), "fld"), - "msg"), + DSL.getMember(DSL.getMember(ref("typed"), "fld"), "fld"), "msg"), "typed.fld.fld.msg"), null), new LogProbe.CaptureExpression( "nullTyped_fld", + new ValueScript(DSL.getMember(ref("nullTyped"), "fld"), "nullTyped.fld"), + null))) + .build(); + TestSnapshotListener listener = installProbes(probe); + Class testClass = compileAndLoadClass(CLASS_NAME); + int result = Reflect.onClass(testClass).call("main", "1").get(); + assertEquals(3, result); + Snapshot snapshot = assertOneSnapshot(listener); + assertEquals(2, snapshot.getCaptures().getReturn().getCaptureExpressions().size()); + assertCaptureExpressions( + snapshot.getCaptures().getReturn(), + "typed_fld_fld_msg", + String.class.getTypeName(), + "hello"); + assertCaptureExpressions( + snapshot.getCaptures().getReturn(), "nullTyped_fld", Object.class.getTypeName(), null); + } + + @Test + public void captureExpressionsWithCondition() throws IOException, URISyntaxException { + final String CLASS_NAME = "CapturedSnapshot08"; + LogProbe probe = + createProbeBuilder(PROBE_ID, CLASS_NAME, "doit", null) + .evaluateAt(MethodLocation.EXIT) + .captureSnapshot(false) + .captureExpressions( + Arrays.asList( + new LogProbe.CaptureExpression( + "typed_fld_fld_msg", new ValueScript( - DSL.getMember(DSL.ref("nullTyped"), "fld"), "nullTyped.fld"), + DSL.getMember( + DSL.getMember(DSL.getMember(ref("typed"), "fld"), "fld"), "msg"), + "typed.fld.fld.msg"), + null), + new LogProbe.CaptureExpression( + "nullTyped_fld", + new ValueScript(DSL.getMember(ref("nullTyped"), "fld"), "nullTyped.fld"), null))) + .when( + new ProbeCondition( + DSL.when(not(DSL.eq(ref("typed"), nullValue()))), "typed != null")) .build(); TestSnapshotListener listener = installProbes(probe); Class testClass = compileAndLoadClass(CLASS_NAME); @@ -2925,6 +2970,44 @@ public void captureExpressions() throws IOException, URISyntaxException { snapshot.getCaptures().getReturn(), "nullTyped_fld", Object.class.getTypeName(), null); } + @Test + public void captureExpressionsWithNullCondition() throws IOException, URISyntaxException { + final String CLASS_NAME = "CapturedSnapshot08"; + LogProbe logProbes = + createProbeBuilder(PROBE_ID, CLASS_NAME, "doit", "int (java.lang.String)") + .when( + new ProbeCondition( + DSL.when( + DSL.eq( + DSL.getMember( + DSL.getMember(DSL.getMember(ref("nullTyped"), "fld"), "fld"), + "msg"), + DSL.value("hello"))), + "nullTyped.fld.fld.msg == 'hello'")) + .captureSnapshot(false) + .template("plain log", Collections.emptyList()) + .captureExpressions( + Arrays.asList( + new LogProbe.CaptureExpression( + "typed_fld_fld_msg", + new ValueScript( + DSL.getMember( + DSL.getMember(DSL.getMember(ref("typed"), "fld"), "fld"), "msg"), + "typed.fld.fld.msg"), + null))) + .build(); + TestSnapshotListener listener = installProbes(logProbes); + Class testClass = compileAndLoadClass(CLASS_NAME); + int result = Reflect.onClass(testClass).call("main", "1").get(); + assertEquals(3, result); + Snapshot snapshot = assertOneSnapshot(listener); + assertEquals("Cannot dereference field: fld", snapshot.getMessage()); + List evaluationErrors = snapshot.getEvaluationErrors(); + assertEquals(1, evaluationErrors.size()); + assertEquals("nullTyped.fld.fld", evaluationErrors.get(0).getExpr()); + assertEquals("Cannot dereference field: fld", evaluationErrors.get(0).getMessage()); + } + @Test public void captureExpressionsPrimitives() throws IOException, URISyntaxException { final String CLASS_NAME = "CapturedSnapshot08"; @@ -2936,10 +3019,10 @@ public void captureExpressionsPrimitives() throws IOException, URISyntaxExceptio Arrays.asList( new LogProbe.CaptureExpression( "this_fld", - new ValueScript(DSL.getMember(DSL.ref("this"), "fld"), "this.fld"), + new ValueScript(DSL.getMember(ref("this"), "fld"), "this.fld"), null), new LogProbe.CaptureExpression( - "var1", new ValueScript(DSL.ref("var1"), "var1"), null))) + "var1", new ValueScript(ref("var1"), "var1"), null))) .build(); TestSnapshotListener listener = installProbes(probe); Class testClass = compileAndLoadClass(CLASS_NAME); @@ -2965,14 +3048,12 @@ public void captureExpressionsLineProbe() throws IOException, URISyntaxException "typed_fld_fld_msg", new ValueScript( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("typed"), "fld"), "fld"), - "msg"), + DSL.getMember(DSL.getMember(ref("typed"), "fld"), "fld"), "msg"), "typed.fld.fld.msg"), null), new LogProbe.CaptureExpression( "nullTyped_fld", - new ValueScript( - DSL.getMember(DSL.ref("nullTyped"), "fld"), "nullTyped.fld"), + new ValueScript(DSL.getMember(ref("nullTyped"), "fld"), "nullTyped.fld"), null))) .build(); TestSnapshotListener listener = installProbes(probe); @@ -3000,13 +3081,12 @@ public void captureExpressionsWithCaptureLimits() throws IOException, URISyntaxE "typed_fld_fld_msg", new ValueScript( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("typed"), "fld"), "fld"), - "msg"), + DSL.getMember(DSL.getMember(ref("typed"), "fld"), "fld"), "msg"), "typed.fld.fld.msg"), new LogProbe.Capture(3, 100, 3, 20)), new LogProbe.CaptureExpression( "typed", - new ValueScript(DSL.ref("typed"), "typed"), + new ValueScript(ref("typed"), "typed"), new LogProbe.Capture(1, 1, 3, 20)))) .build(); TestSnapshotListener listener = installProbes(probe); @@ -3043,12 +3123,11 @@ public void captureExpressionsWithInheritedCaptureLimits() "typed_fld_fld_msg", new ValueScript( DSL.getMember( - DSL.getMember(DSL.getMember(DSL.ref("typed"), "fld"), "fld"), - "msg"), + DSL.getMember(DSL.getMember(ref("typed"), "fld"), "fld"), "msg"), "typed.fld.fld.msg"), null), new LogProbe.CaptureExpression( - "typed", new ValueScript(DSL.ref("typed"), "typed"), null))) + "typed", new ValueScript(ref("typed"), "typed"), null))) .build(); TestSnapshotListener listener = installProbes(probe); Class testClass = compileAndLoadClass(CLASS_NAME); From a1304b2e082222ac31909b1382e7ac150423c2d6 Mon Sep 17 00:00:00 2001 From: jean-philippe bempel Date: Thu, 4 Jun 2026 22:02:58 +0200 Subject: [PATCH 2/2] fix initSamplers --- .../src/main/java/com/datadog/debugger/probe/LogProbe.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java index bb72a718e12..5cdc2354409 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java @@ -458,7 +458,7 @@ public void initSamplers() { double rate = sampling != null ? sampling.getEventsPerSecond() - : (isCaptureSnapshot() + : (isFullSnapshot() ? ProbeRateLimiter.DEFAULT_SNAPSHOT_RATE : ProbeRateLimiter.DEFAULT_LOG_RATE); sampler = ProbeRateLimiter.createSampler(rate);