diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ConfigurationComparer.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ConfigurationComparer.java index 1407f84a193..c3f8749f2de 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ConfigurationComparer.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ConfigurationComparer.java @@ -92,8 +92,7 @@ public boolean hasProbeRelatedChanges() { public boolean hasRateLimitRelatedChanged() { return originalConfiguration != null - && originalConfiguration.getSampling() != incomingConfiguration.getSampling() - || hasProbeRelatedChanges(); + && originalConfiguration.getSampling() != incomingConfiguration.getSampling(); } List findChangesInBlockedTypes() { diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ConfigurationUpdater.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ConfigurationUpdater.java index 2ca6d2826ee..0f3aeba5eed 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ConfigurationUpdater.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ConfigurationUpdater.java @@ -9,7 +9,6 @@ import com.datadog.debugger.probe.ExceptionProbe; import com.datadog.debugger.probe.LogProbe; import com.datadog.debugger.probe.ProbeDefinition; -import com.datadog.debugger.probe.Sampled; import com.datadog.debugger.sink.DebuggerSink; import com.datadog.debugger.util.ExceptionHelper; import com.datadog.debugger.util.SpringHelper; @@ -170,7 +169,7 @@ private void applyNewConfiguration(Configuration newConfiguration) { if (changes.hasRateLimitRelatedChanged()) { // apply rate limit config first to avoid racing with execution/instrumentation // of probes requiring samplers - applyRateLimiter(changes, newConfiguration.getSampling()); + applyRateLimiter(newConfiguration.getSampling()); } currentConfiguration = newConfiguration; if (changes.hasProbeRelatedChanges()) { @@ -436,15 +435,7 @@ public ProbeImplementation resolve(int probeIndex) { return probeMetadata.getProbe(probeIndex); } - private static void applyRateLimiter( - ConfigurationComparer changes, LogProbe.Sampling globalSampling) { - // ensure rate is up-to-date for all new probes - for (ProbeDefinition added : changes.getAddedDefinitions()) { - if (added instanceof Sampled) { - Sampled probe = (Sampled) added; - probe.initSamplers(); - } - } + private static void applyRateLimiter(LogProbe.Sampling globalSampling) { // set global sampling if (globalSampling != null) { ProbeRateLimiter.setGlobalSnapshotRate(globalSampling.getSnapshotsPerSecond()); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerProductChangesListener.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerProductChangesListener.java index 0b2b80bb71d..82db0804ef1 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerProductChangesListener.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerProductChangesListener.java @@ -1,6 +1,12 @@ package com.datadog.debugger.agent; import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.REMOTE_CONFIG; +import static com.datadog.debugger.probe.ProbeDefinitionDeserializer.deserializeConfiguration; +import static com.datadog.debugger.probe.ProbeDefinitionDeserializer.deserializeLogProbe; +import static com.datadog.debugger.probe.ProbeDefinitionDeserializer.deserializeMetricProbe; +import static com.datadog.debugger.probe.ProbeDefinitionDeserializer.deserializeSpanDecorationProbe; +import static com.datadog.debugger.probe.ProbeDefinitionDeserializer.deserializeSpanProbe; +import static com.datadog.debugger.probe.ProbeDefinitionDeserializer.deserializeTriggerProbe; import com.datadog.debugger.probe.LogProbe; import com.datadog.debugger.probe.MetricProbe; @@ -8,14 +14,11 @@ import com.datadog.debugger.probe.SpanDecorationProbe; import com.datadog.debugger.probe.SpanProbe; import com.datadog.debugger.probe.TriggerProbe; -import com.datadog.debugger.util.MoshiHelper; -import com.squareup.moshi.JsonAdapter; import datadog.remoteconfig.PollingRateHinter; import datadog.remoteconfig.state.ConfigKey; import datadog.remoteconfig.state.ProductListener; import datadog.trace.api.Config; import datadog.trace.util.TagsHelper; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -24,7 +27,6 @@ import java.util.function.Consumer; import java.util.function.Predicate; import java.util.regex.Pattern; -import okio.Okio; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,54 +39,6 @@ public class DebuggerProductChangesListener implements ProductListener { private static final Logger LOGGER = LoggerFactory.getLogger(DebuggerProductChangesListener.class); - static class Adapter { - static final JsonAdapter CONFIGURATION_JSON_ADAPTER = - MoshiHelper.createMoshiConfig().adapter(Configuration.class); - - static final JsonAdapter METRIC_PROBE_JSON_ADAPTER = - MoshiHelper.createMoshiConfig().adapter(MetricProbe.class); - - static final JsonAdapter LOG_PROBE_JSON_ADAPTER = - MoshiHelper.createMoshiConfig().adapter(LogProbe.class); - - static final JsonAdapter SPAN_PROBE_JSON_ADAPTER = - MoshiHelper.createMoshiConfig().adapter(SpanProbe.class); - - static final JsonAdapter TRIGGER_PROBE_JSON_ADAPTER = - MoshiHelper.createMoshiConfig().adapter(TriggerProbe.class); - - static final JsonAdapter SPAN_DECORATION_PROBE_JSON_ADAPTER = - MoshiHelper.createMoshiConfig().adapter(SpanDecorationProbe.class); - - static Configuration deserializeConfiguration(byte[] content) throws IOException { - return deserialize(CONFIGURATION_JSON_ADAPTER, content); - } - - static MetricProbe deserializeMetricProbe(byte[] content) throws IOException { - return deserialize(METRIC_PROBE_JSON_ADAPTER, content); - } - - static LogProbe deserializeLogProbe(byte[] content) throws IOException { - return deserialize(LOG_PROBE_JSON_ADAPTER, content); - } - - static SpanProbe deserializeSpanProbe(byte[] content) throws IOException { - return deserialize(SPAN_PROBE_JSON_ADAPTER, content); - } - - static TriggerProbe deserializeTriggerProbe(byte[] content) throws IOException { - return deserialize(TRIGGER_PROBE_JSON_ADAPTER, content); - } - - static SpanDecorationProbe deserializeSpanDecorationProbe(byte[] content) throws IOException { - return deserialize(SPAN_DECORATION_PROBE_JSON_ADAPTER, content); - } - - private static T deserialize(JsonAdapter adapter, byte[] content) throws IOException { - return adapter.fromJson(Okio.buffer(Okio.source(new ByteArrayInputStream(content)))); - } - } - private static final Predicate IS_UUID = Pattern.compile( "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") @@ -105,22 +59,22 @@ public void accept(ConfigKey configKey, byte[] content, PollingRateHinter pollin String configId = configKey.getConfigId(); try { if (configId.startsWith(METRIC_PROBE_PREFIX)) { - MetricProbe metricProbe = Adapter.deserializeMetricProbe(content); + MetricProbe metricProbe = deserializeMetricProbe(content); configChunks.put(configId, definitions -> definitions.add(metricProbe)); } else if (configId.startsWith(LOG_PROBE_PREFIX)) { - LogProbe logProbe = Adapter.deserializeLogProbe(content); + LogProbe logProbe = deserializeLogProbe(content); configChunks.put(configId, definitions -> definitions.add(logProbe)); } else if (configId.startsWith(SPAN_PROBE_PREFIX)) { - SpanProbe spanProbe = Adapter.deserializeSpanProbe(content); + SpanProbe spanProbe = deserializeSpanProbe(content); configChunks.put(configId, definitions -> definitions.add(spanProbe)); } else if (configId.startsWith(TRIGGER_PROBE_PREFIX)) { - TriggerProbe triggerProbe = Adapter.deserializeTriggerProbe(content); + TriggerProbe triggerProbe = deserializeTriggerProbe(content); configChunks.put(configId, definitions -> definitions.add(triggerProbe)); } else if (configId.startsWith(SPAN_DECORATION_PROBE_PREFIX)) { - SpanDecorationProbe spanDecorationProbe = Adapter.deserializeSpanDecorationProbe(content); + SpanDecorationProbe spanDecorationProbe = deserializeSpanDecorationProbe(content); configChunks.put(configId, definitions -> definitions.add(spanDecorationProbe)); } else if (IS_UUID.test(configId)) { - Configuration newConfig = Adapter.deserializeConfiguration(content); + Configuration newConfig = deserializeConfiguration(content); if (newConfig.getService().equals(serviceName)) { configChunks.put( configId, diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinitionDeserializer.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinitionDeserializer.java new file mode 100644 index 00000000000..9501b1ad07b --- /dev/null +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinitionDeserializer.java @@ -0,0 +1,59 @@ +package com.datadog.debugger.probe; + +import com.datadog.debugger.agent.Configuration; +import com.datadog.debugger.util.MoshiHelper; +import com.squareup.moshi.JsonAdapter; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import okio.Okio; + +public class ProbeDefinitionDeserializer { + static final JsonAdapter CONFIGURATION_JSON_ADAPTER = + MoshiHelper.createMoshiConfig().adapter(Configuration.class); + static final JsonAdapter METRIC_PROBE_JSON_ADAPTER = + MoshiHelper.createMoshiConfig().adapter(MetricProbe.class); + static final JsonAdapter LOG_PROBE_JSON_ADAPTER = + MoshiHelper.createMoshiConfig().adapter(LogProbe.class); + static final JsonAdapter SPAN_PROBE_JSON_ADAPTER = + MoshiHelper.createMoshiConfig().adapter(SpanProbe.class); + static final JsonAdapter TRIGGER_PROBE_JSON_ADAPTER = + MoshiHelper.createMoshiConfig().adapter(TriggerProbe.class); + static final JsonAdapter SPAN_DECORATION_PROBE_JSON_ADAPTER = + MoshiHelper.createMoshiConfig().adapter(SpanDecorationProbe.class); + + public static Configuration deserializeConfiguration(byte[] content) throws IOException { + return deserialize(CONFIGURATION_JSON_ADAPTER, content); + } + + public static MetricProbe deserializeMetricProbe(byte[] content) throws IOException { + return deserialize(METRIC_PROBE_JSON_ADAPTER, content); + } + + public static LogProbe deserializeLogProbe(byte[] content) throws IOException { + LogProbe logProbe = deserialize(LOG_PROBE_JSON_ADAPTER, content); + logProbe.initSamplers(); + return logProbe; + } + + public static SpanProbe deserializeSpanProbe(byte[] content) throws IOException { + return deserialize(SPAN_PROBE_JSON_ADAPTER, content); + } + + public static TriggerProbe deserializeTriggerProbe(byte[] content) throws IOException { + TriggerProbe triggerProbe = deserialize(TRIGGER_PROBE_JSON_ADAPTER, content); + triggerProbe.initSamplers(); + return triggerProbe; + } + + public static SpanDecorationProbe deserializeSpanDecorationProbe(byte[] content) + throws IOException { + SpanDecorationProbe spanDecorationProbe = + deserialize(SPAN_DECORATION_PROBE_JSON_ADAPTER, content); + spanDecorationProbe.initSamplers(); + return spanDecorationProbe; + } + + private static T deserialize(JsonAdapter adapter, byte[] content) throws IOException { + return adapter.fromJson(Okio.buffer(Okio.source(new ByteArrayInputStream(content)))); + } +} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/SpanDecorationProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/SpanDecorationProbe.java index a4ed8a2fb12..af9354429d1 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/SpanDecorationProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/SpanDecorationProbe.java @@ -179,6 +179,18 @@ public SpanDecorationProbe( this.decorations = decorations; } + public SpanDecorationProbe(SpanDecorationProbe.Builder builder) { + this( + builder.language, + builder.probeId, + builder.tagStrs, + builder.where, + builder.evaluateAt, + builder.targetSpan, + builder.decorations); + initSamplers(); + } + @Override public InstrumentationResult.Status instrument( MethodInfo methodInfo, List diagnostics, List probeIndices) { @@ -399,26 +411,25 @@ public static SpanDecorationProbe.Builder builder() { public static class Builder extends ProbeDefinition.Builder { private TargetSpan targetSpan; - private List decorate; + private List decorations; public Builder targetSpan(TargetSpan targetSpan) { this.targetSpan = targetSpan; return this; } - public Builder decorate(List decorate) { - this.decorate = decorate; + public Builder decorations(List decorations) { + this.decorations = decorations; return this; } - public Builder decorate(Decoration decoration) { - this.decorate = Collections.singletonList(decoration); + public Builder decorations(Decoration decoration) { + this.decorations = Collections.singletonList(decoration); return this; } public SpanDecorationProbe build() { - return new SpanDecorationProbe( - LANGUAGE, probeId, tagStrs, where, evaluateAt, targetSpan, decorate); + return new SpanDecorationProbe(this); } } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/TriggerProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/TriggerProbe.java index 78eae9dd9e2..f890df1d8e9 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/TriggerProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/TriggerProbe.java @@ -53,6 +53,11 @@ public TriggerProbe(ProbeId probeId, Where where) { this(probeId, null, where, null, null); } + public TriggerProbe(TriggerProbe.Builder builder) { + this(builder.probeId, builder.tagStrs, builder.where, builder.probeCondition, builder.sampling); + initSamplers(); + } + @Override public InstrumentationResult.Status instrument( MethodInfo methodInfo, List diagnostics, List probeIndices) { @@ -144,7 +149,7 @@ private void decorateTags() { AgentSpan agentSpan = tracerAPI.activeSpan().getLocalRootSpan(); agentSpan.setTag(Tags.PROPAGATED_DEBUG, sessionId + ":1"); - agentSpan.setTag(format("_dd.ld.probe_id.%s", probeId.getId()), true); + agentSpan.setTag(format("_dd.ld.probe_id.%s", getProbeId().getId()), true); } @Override @@ -197,4 +202,27 @@ public String toString() { version, where); } + + public static TriggerProbe.Builder builder() { + return new TriggerProbe.Builder(); + } + + public static class Builder extends ProbeDefinition.Builder { + private ProbeCondition probeCondition; + private Sampling sampling; + + public TriggerProbe.Builder when(ProbeCondition probeCondition) { + this.probeCondition = probeCondition; + return this; + } + + public TriggerProbe.Builder sampling(Sampling sampling) { + this.sampling = sampling; + return this; + } + + public TriggerProbe build() { + return new TriggerProbe(this); + } + } } 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 71b883ba9a9..d40fe6659d1 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 @@ -2824,7 +2824,7 @@ public void allProbesSameMethod() throws IOException, URISyntaxException { .probeId(PROBE_ID) .where(where) .targetSpan(SpanDecorationProbe.TargetSpan.ACTIVE) - .decorate( + .decorations( new SpanDecorationProbe.Decoration( null, Arrays.asList( @@ -2842,7 +2842,7 @@ public void allProbesSameMethod() throws IOException, URISyntaxException { .where(where) .build()) .add(LogProbe.builder().probeId(PROBE_ID3).where(where).build()) - .add(new TriggerProbe(PROBE_ID4, where)) + .add(TriggerProbe.builder().probeId(PROBE_ID4).where(where).build()) .build(); CoreTracer tracer = CoreTracer.builder().build(); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java index a28184370d9..b0413b63a08 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java @@ -18,7 +18,6 @@ import com.datadog.debugger.probe.SpanDecorationProbe; import com.datadog.debugger.probe.SpanProbe; import com.datadog.debugger.probe.TriggerProbe; -import com.datadog.debugger.probe.Where; import com.datadog.debugger.util.MoshiHelper; import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.Types; @@ -410,7 +409,10 @@ private static LogProbe createLog( private static TriggerProbe createTriggerProbe( String id, String typeName, String methodName, String signature) { - return new TriggerProbe(new ProbeId(id, 0), Where.of(typeName, methodName, signature)); + return TriggerProbe.builder() + .probeId(new ProbeId(id, 0)) + .where(typeName, methodName, signature) + .build(); } private static SpanProbe createSpan( @@ -438,7 +440,7 @@ private static SpanDecorationProbe createDecorationSpan( .evaluateAt(MethodLocation.ENTRY) .tags("tag1:value1", "tag2:value2") .targetSpan(targetSpan) - .decorate(decoration) + .decorations(decoration) .build(); } diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationUpdaterTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationUpdaterTest.java index 9fcc54b6140..0ec8e1622e1 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationUpdaterTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationUpdaterTest.java @@ -2,12 +2,20 @@ import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.REMOTE_CONFIG; import static com.datadog.debugger.agent.DebuggerProductChangesListener.LOG_PROBE_PREFIX; +import static com.datadog.debugger.probe.ProbeDefinitionDeserializer.deserializeLogProbe; +import static com.datadog.debugger.probe.ProbeDefinitionDeserializer.deserializeSpanDecorationProbe; +import static com.datadog.debugger.probe.ProbeDefinitionDeserializer.deserializeTriggerProbe; +import static com.datadog.debugger.probe.ProbeDefinitionSerializer.serializeLogProbe; +import static com.datadog.debugger.probe.ProbeDefinitionSerializer.serializeSpanDecorationProbe; +import static com.datadog.debugger.probe.ProbeDefinitionSerializer.serializeTriggerProbe; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -23,12 +31,19 @@ import com.datadog.debugger.probe.ProbeDefinition; import com.datadog.debugger.probe.SpanDecorationProbe; import com.datadog.debugger.probe.SpanProbe; +import com.datadog.debugger.probe.TriggerProbe; import com.datadog.debugger.sink.DebuggerSink; import com.datadog.debugger.sink.ProbeStatusSink; import datadog.environment.JavaVirtualMachine; import datadog.trace.api.Config; +import datadog.trace.bootstrap.debugger.CapturedContext; +import datadog.trace.bootstrap.debugger.EvaluationError; +import datadog.trace.bootstrap.debugger.MethodLocation; import datadog.trace.bootstrap.debugger.ProbeId; import datadog.trace.bootstrap.debugger.ProbeImplementation; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; +import datadog.trace.bootstrap.instrumentation.api.Tags; import java.io.IOException; import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException; @@ -709,6 +724,100 @@ public void recordWithTypeAnnotation() verify(inst, times(0)).retransformClasses(any()); } + @Test + public void logProbeSamplers() throws IOException { + when(inst.getAllLoadedClasses()).thenReturn(new Class[] {String.class}); + ConfigurationUpdater configurationUpdater = createConfigUpdater(debuggerSinkWithMockStatusSink); + LogProbe probe1 = + LogProbe.builder().probeId(PROBE_ID).where("java.lang.String", "concat").build(); + configurationUpdater.accept(REMOTE_CONFIG, singletonList(probe1)); + assertTrue(probe1.isReadyToCapture()); + + // Simulate JSON round-trip: in production, each remote config delivery deserializes fresh + // LogProbe objects. + // Moshi skips transient fields, so sampler need to be initialized with initSamplers. + LogProbe probe1Deserialized = deserializeLogProbe(serializeLogProbe(probe1).getBytes()); + LogProbe probe2 = + LogProbe.builder().probeId(PROBE_ID2).where("java.lang.String", "concat").build(); + configurationUpdater.accept(REMOTE_CONFIG, Arrays.asList(probe1Deserialized, probe2)); + assertTrue(probe1Deserialized.isReadyToCapture()); + assertTrue(probe2.isReadyToCapture()); + } + + @Test + public void spanDecorationProbeSamplers() throws IOException { + when(inst.getAllLoadedClasses()).thenReturn(new Class[] {String.class}); + ConfigurationUpdater configurationUpdater = createConfigUpdater(debuggerSinkWithMockStatusSink); + SpanDecorationProbe probe1 = + SpanDecorationProbe.builder() + .probeId(PROBE_ID) + .where("java.lang.String", "concat") + .evaluateAt(MethodLocation.EXIT) + .build(); + configurationUpdater.accept(REMOTE_CONFIG, singletonList(probe1)); + + assertCommitSpanDecorationProbe(probe1); + + // Simulate JSON round-trip: in production, each remote config delivery deserializes fresh + // SpanDecorationProbe objects. + // Moshi skips transient fields, so sampler need to be initialized with initSamplers. + SpanDecorationProbe probe1Deserialized = + deserializeSpanDecorationProbe(serializeSpanDecorationProbe(probe1).getBytes()); + SpanDecorationProbe probe2 = + SpanDecorationProbe.builder() + .probeId(PROBE_ID2) + .where("java.lang.String", "concat") + .evaluateAt(MethodLocation.EXIT) + .build(); + configurationUpdater.accept(REMOTE_CONFIG, Arrays.asList(probe1Deserialized, probe2)); + assertCommitSpanDecorationProbe(probe1Deserialized); + assertCommitSpanDecorationProbe(probe2); + } + + private static void assertCommitSpanDecorationProbe(SpanDecorationProbe spanDecorationProbe) { + DebuggerSink sinkMock = mock(DebuggerSink.class); + DebuggerAgent.initSink(sinkMock); + CapturedContext capturedContext = mock(CapturedContext.class); + CapturedContext.Status status = spanDecorationProbe.createStatus(); + status.addError(new EvaluationError(null, null)); + when(capturedContext.getStatus(anyString())).thenReturn(status); + spanDecorationProbe.commit(null, capturedContext, null); + verify(sinkMock).addSnapshot(any()); + } + + @Test + public void triggerProbeSamplers() throws IOException { + when(inst.getAllLoadedClasses()).thenReturn(new Class[] {String.class}); + ConfigurationUpdater configurationUpdater = createConfigUpdater(debuggerSinkWithMockStatusSink); + TriggerProbe probe1 = + TriggerProbe.builder().probeId(PROBE_ID).where("java.lang.String", "concat").build(); + configurationUpdater.accept(REMOTE_CONFIG, singletonList(probe1)); + + assertEvaluateTriggerProbe(probe1); + + // Simulate JSON round-trip: in production, each remote config delivery deserializes fresh + // LogProbe objects. + // Moshi skips transient fields, so sampler need to be initialized with initSamplers. + TriggerProbe probe1Deserialized = + deserializeTriggerProbe(serializeTriggerProbe(probe1).getBytes()); + TriggerProbe probe2 = + TriggerProbe.builder().probeId(PROBE_ID2).where("java.lang.String", "concat").build(); + configurationUpdater.accept(REMOTE_CONFIG, Arrays.asList(probe1Deserialized, probe2)); + assertEvaluateTriggerProbe(probe1Deserialized); + assertEvaluateTriggerProbe(probe2); + } + + private static void assertEvaluateTriggerProbe(TriggerProbe triggerProbe) { + AgentTracer.TracerAPI tracerAPIMock = mock(AgentTracer.TracerAPI.class); + AgentSpan spanMock = mock(AgentSpan.class); + when(tracerAPIMock.activeSpan()).thenReturn(spanMock); + when(spanMock.getLocalRootSpan()).thenReturn(spanMock); + AgentTracer.forceRegister(tracerAPIMock); + triggerProbe.evaluate( + new CapturedContext(), triggerProbe.createStatus(), MethodLocation.ENTRY, true); + verify(spanMock).setTag(eq(Tags.PROPAGATED_DEBUG), anyString()); + } + private DebuggerTransformer createTransformer( Config tracerConfig, Configuration configuration, diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DebuggerProductChangesListenerTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DebuggerProductChangesListenerTest.java index 859b25c436d..6b769c8b32d 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DebuggerProductChangesListenerTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DebuggerProductChangesListenerTest.java @@ -1,5 +1,11 @@ package com.datadog.debugger.agent; +import static com.datadog.debugger.probe.ProbeDefinitionSerializer.serializeConfiguration; +import static com.datadog.debugger.probe.ProbeDefinitionSerializer.serializeLogProbe; +import static com.datadog.debugger.probe.ProbeDefinitionSerializer.serializeMetricProbe; +import static com.datadog.debugger.probe.ProbeDefinitionSerializer.serializeSpanDecorationProbe; +import static com.datadog.debugger.probe.ProbeDefinitionSerializer.serializeSpanProbe; +import static com.datadog.debugger.probe.ProbeDefinitionSerializer.serializeTriggerProbe; import static com.datadog.debugger.util.LogProbeTestHelper.parseTemplate; import static datadog.remoteconfig.PollingHinterNoop.NOOP; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -16,7 +22,6 @@ import com.datadog.debugger.probe.SpanDecorationProbe; import com.datadog.debugger.probe.SpanProbe; import com.datadog.debugger.probe.TriggerProbe; -import com.datadog.debugger.probe.Where; import datadog.remoteconfig.state.ParsedConfigKey; import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.ProbeId; @@ -259,39 +264,27 @@ private void assertDefinitions( } byte[] toContent(Configuration configuration) { - return DebuggerProductChangesListener.Adapter.CONFIGURATION_JSON_ADAPTER - .toJson(configuration) - .getBytes(StandardCharsets.UTF_8); + return serializeConfiguration(configuration).getBytes(StandardCharsets.UTF_8); } byte[] toContent(MetricProbe probe) { - return DebuggerProductChangesListener.Adapter.METRIC_PROBE_JSON_ADAPTER - .toJson(probe) - .getBytes(StandardCharsets.UTF_8); + return serializeMetricProbe(probe).getBytes(StandardCharsets.UTF_8); } byte[] toContent(LogProbe probe) { - return DebuggerProductChangesListener.Adapter.LOG_PROBE_JSON_ADAPTER - .toJson(probe) - .getBytes(StandardCharsets.UTF_8); + return serializeLogProbe(probe).getBytes(StandardCharsets.UTF_8); } byte[] toContent(SpanProbe probe) { - return DebuggerProductChangesListener.Adapter.SPAN_PROBE_JSON_ADAPTER - .toJson(probe) - .getBytes(StandardCharsets.UTF_8); + return serializeSpanProbe(probe).getBytes(StandardCharsets.UTF_8); } byte[] toContent(SpanDecorationProbe probe) { - return DebuggerProductChangesListener.Adapter.SPAN_DECORATION_PROBE_JSON_ADAPTER - .toJson(probe) - .getBytes(StandardCharsets.UTF_8); + return serializeSpanDecorationProbe(probe).getBytes(StandardCharsets.UTF_8); } byte[] toContent(TriggerProbe probe) { - return DebuggerProductChangesListener.Adapter.TRIGGER_PROBE_JSON_ADAPTER - .toJson(probe) - .getBytes(StandardCharsets.UTF_8); + return serializeTriggerProbe(probe).getBytes(StandardCharsets.UTF_8); } void acceptConfig( @@ -398,7 +391,10 @@ SpanDecorationProbe createSpanDecorationProbe(String id) { } TriggerProbe createTriggerProbe(String id) { - return new TriggerProbe(new ProbeId(id, 0), Where.of("java.lang.String", "indexOf", null)); + return TriggerProbe.builder() + .probeId(new ProbeId(id, 0)) + .where("java.lang.String", "indexOf", null) + .build(); } Configuration.FilterList createFilteredList() { diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanDecorationProbeInstrumentationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanDecorationProbeInstrumentationTest.java index d70d8d4c1d3..5bb18c36d7e 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanDecorationProbeInstrumentationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanDecorationProbeInstrumentationTest.java @@ -728,7 +728,7 @@ private static SpanDecorationProbe.Builder createProbeBuilder( .where(typeName, methodName, signature) .evaluateAt(MethodLocation.EXIT) .targetSpan(targetSpan) - .decorate(decorationList); + .decorations(decorationList); } private static SpanDecorationProbe.Builder createProbeBuilder( @@ -743,7 +743,7 @@ private static SpanDecorationProbe.Builder createProbeBuilder( .where(sourceFile, line) .evaluateAt(MethodLocation.EXIT) .targetSpan(targetSpan) - .decorate(decorationList); + .decorations(decorationList); } private void installSpanProbes(String expectedClassName, SpanDecorationProbe... probes) { diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/ProbeDefinitionSerializer.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/ProbeDefinitionSerializer.java new file mode 100644 index 00000000000..4867db10149 --- /dev/null +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/ProbeDefinitionSerializer.java @@ -0,0 +1,31 @@ +package com.datadog.debugger.probe; + +import com.datadog.debugger.agent.Configuration; + +public class ProbeDefinitionSerializer { + + public static String serializeMetricProbe(MetricProbe metricProbe) { + return ProbeDefinitionDeserializer.METRIC_PROBE_JSON_ADAPTER.toJson(metricProbe); + } + + public static String serializeLogProbe(LogProbe logProbe) { + return ProbeDefinitionDeserializer.LOG_PROBE_JSON_ADAPTER.toJson(logProbe); + } + + public static String serializeSpanProbe(SpanProbe spanProbe) { + return ProbeDefinitionDeserializer.SPAN_PROBE_JSON_ADAPTER.toJson(spanProbe); + } + + public static String serializeSpanDecorationProbe(SpanDecorationProbe spanDecorationProbe) { + return ProbeDefinitionDeserializer.SPAN_DECORATION_PROBE_JSON_ADAPTER.toJson( + spanDecorationProbe); + } + + public static String serializeTriggerProbe(TriggerProbe triggerProbe) { + return ProbeDefinitionDeserializer.TRIGGER_PROBE_JSON_ADAPTER.toJson(triggerProbe); + } + + public static String serializeConfiguration(Configuration configuration) { + return ProbeDefinitionDeserializer.CONFIGURATION_JSON_ADAPTER.toJson(configuration); + } +} diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/trigger/TriggerProbeTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/trigger/TriggerProbeTest.java index 79ad1e25e22..6b895a9dc13 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/trigger/TriggerProbeTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/trigger/TriggerProbeTest.java @@ -16,7 +16,6 @@ import com.datadog.debugger.el.ProbeCondition; import com.datadog.debugger.probe.Sampling; import com.datadog.debugger.probe.TriggerProbe; -import com.datadog.debugger.probe.Where; import com.datadog.debugger.util.TestTraceInterceptor; import datadog.trace.agent.tooling.TracerInstaller; import datadog.trace.api.Config; @@ -96,10 +95,13 @@ private static TriggerProbe createTriggerProbe( String signature, ProbeCondition probeCondition, Sampling sampling) { - return new TriggerProbe(id, Where.of(typeName, methodName, signature)) - .setSessionId(sessionId) - .setProbeCondition(probeCondition) - .setSampling(sampling); + return TriggerProbe.builder() + .probeId(id) + .where(typeName, methodName, signature) + .when(probeCondition) + .sampling(sampling) + .build() + .setSessionId(sessionId); } @Test diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java index 931cca819d1..36eed37f458 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java @@ -9,8 +9,10 @@ import com.datadog.debugger.agent.ProbeStatus; import com.datadog.debugger.probe.LogProbe; import com.datadog.debugger.probe.MetricProbe; +import com.datadog.debugger.probe.ProbeDefinition; import com.datadog.debugger.probe.SpanDecorationProbe; import com.datadog.debugger.probe.SpanProbe; +import com.datadog.debugger.probe.TriggerProbe; import com.datadog.debugger.sink.Snapshot; import com.datadog.debugger.util.MoshiHelper; import com.datadog.debugger.util.MoshiSnapshotTestHelper; @@ -89,7 +91,6 @@ public abstract class BaseIntegrationTest { protected static final MockResponse EMPTY_200_RESPONSE = new MockResponse().setResponseCode(200); private static final ByteString DIAGNOSTICS_STR = ByteString.encodeUtf8("{\"diagnostics\":"); - private static final String LD_CONFIG_ID = UUID.randomUUID().toString(); private static final String APM_CONFIG_ID = UUID.randomUUID().toString(); public static final String LIVE_DEBUGGING_PRODUCT = "LIVE_DEBUGGING"; public static final String APM_TRACING_PRODUCT = "APM_TRACING"; @@ -162,6 +163,7 @@ protected List getDebuggerCommandParams() { "-Ddd.profiling.enabled=false", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=info", "-Ddatadog.slf4j.simpleLogger.log.com.datadog.debugger=debug", + "-Ddatadog.slf4j.simpleLogger.log.datadog.trace.bootstrap.debugger=debug", "-Ddatadog.slf4j.simpleLogger.log.datadog.remoteconfig=debug", "-Ddd.jmxfetch.start-delay=0", "-Ddd.jmxfetch.enabled=false", @@ -441,14 +443,23 @@ protected MockResponse handleConfigRequests() { configuration = createConfig(Collections.emptyList()); } try { - JsonAdapter adapter = - MoshiConfigTestHelper.createMoshiConfig().adapter(Configuration.class); - String liveDebuggingJson = adapter.toJson(configuration); - LOG.info("Sending Live Debugging json: {}", liveDebuggingJson); + JsonAdapter logAdapter = + MoshiConfigTestHelper.createMoshiConfig().adapter(LogProbe.class); + JsonAdapter metricAdapter = + MoshiConfigTestHelper.createMoshiConfig().adapter(MetricProbe.class); + JsonAdapter spanAdapter = + MoshiConfigTestHelper.createMoshiConfig().adapter(SpanProbe.class); + JsonAdapter spanDecorationAdapter = + MoshiConfigTestHelper.createMoshiConfig().adapter(SpanDecorationProbe.class); + JsonAdapter triggerAdapter = + MoshiConfigTestHelper.createMoshiConfig().adapter(TriggerProbe.class); List remoteConfigs = new ArrayList<>(); - remoteConfigs.add( - new RemoteConfigHelper.RemoteConfig( - LIVE_DEBUGGING_PRODUCT, liveDebuggingJson, LD_CONFIG_ID)); + addToRemoteConfig(configuration.getLogProbes(), logAdapter, remoteConfigs); + addToRemoteConfig(configuration.getMetricProbes(), metricAdapter, remoteConfigs); + addToRemoteConfig(configuration.getSpanProbes(), spanAdapter, remoteConfigs); + addToRemoteConfig( + configuration.getSpanDecorationProbes(), spanDecorationAdapter, remoteConfigs); + addToRemoteConfig(configuration.getTriggerProbes(), triggerAdapter, remoteConfigs); if (configOverrides != null) { JsonAdapter configAdapter = new Moshi.Builder().build().adapter(ConfigOverrides.class); @@ -465,6 +476,38 @@ protected MockResponse handleConfigRequests() { } } + private static void addToRemoteConfig( + Collection probes, + JsonAdapter probeAdapter, + List remoteConfigs) { + for (T probe : probes) { + String json = probeAdapter.toJson(probe); + LOG.info("Sending {} json: {}", probe.getClass().getSimpleName(), json); + remoteConfigs.add( + new RemoteConfigHelper.RemoteConfig( + LIVE_DEBUGGING_PRODUCT, json, getProbePrefix(probe) + UUID.randomUUID())); + } + } + + private static String getProbePrefix(ProbeDefinition probeDefinition) { + if (probeDefinition instanceof LogProbe) { + return "logProbe_"; + } + if (probeDefinition instanceof MetricProbe) { + return "metricProbe_"; + } + if (probeDefinition instanceof SpanProbe) { + return "spanProbe_"; + } + if (probeDefinition instanceof SpanDecorationProbe) { + return "spanDecorationProbe_"; + } + if (probeDefinition instanceof TriggerProbe) { + return "triggerProbe_"; + } + return ""; + } + private Configuration getCurrentConfiguration() { synchronized (configLock) { return currentConfiguration; diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java index feee2c42fdd..93009c1a929 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java @@ -58,7 +58,7 @@ void testMethodSimpleTagNoCondition() throws Exception { SpanDecorationProbe.builder() .probeId(PROBE_ID) .where(TEST_APP_CLASS_NAME, TRACED_METHOD_NAME) - .decorate(createDecoration("tag1", "{argStr}")) + .decorations(createDecoration("tag1", "{argStr}")) .targetSpan(SpanDecorationProbe.TargetSpan.ACTIVE) .build(); addProbe(spanDecorationProbe); @@ -104,7 +104,7 @@ void testMethodMultiTagsMultiConditions() throws Exception { SpanDecorationProbe.builder() .probeId(PROBE_ID) .where(TEST_APP_CLASS_NAME, TRACED_METHOD_NAME) - .decorate(decorations) + .decorations(decorations) .targetSpan(SpanDecorationProbe.TargetSpan.ACTIVE) .build(); addProbe(spanDecorationProbe); @@ -137,7 +137,7 @@ void testMethodSimpleTagValueError() throws Exception { SpanDecorationProbe.builder() .probeId(PROBE_ID) .where(TEST_APP_CLASS_NAME, TRACED_METHOD_NAME) - .decorate(createDecoration("tag1", "{invalidArg}")) + .decorations(createDecoration("tag1", "{invalidArg}")) .targetSpan(SpanDecorationProbe.TargetSpan.ACTIVE) .build(); addProbe(spanDecorationProbe); @@ -180,7 +180,7 @@ void testMethodSimpleTagConditionError() throws Exception { SpanDecorationProbe.builder() .probeId(PROBE_ID) .where(TEST_APP_CLASS_NAME, TRACED_METHOD_NAME) - .decorate( + .decorations( createDecoration( not(eq(ref("invalidArg"), nullValue())), "invalidArg != null", @@ -228,7 +228,7 @@ void testMethodMultiTagValueError() throws Exception { SpanDecorationProbe.builder() .probeId(PROBE_ID) .where(TEST_APP_CLASS_NAME, TRACED_METHOD_NAME) - .decorate(decorations) + .decorations(decorations) .targetSpan(SpanDecorationProbe.TargetSpan.ACTIVE) .build(); addProbe(spanDecorationProbe); @@ -276,7 +276,7 @@ void testSamplingSpanDecoration() throws Exception { SpanDecorationProbe.builder() .probeId(PROBE_ID) .where(TEST_APP_CLASS_NAME, TRACED_METHOD_NAME) - .decorate(createDecoration("tag1", "staticText")) + .decorations(createDecoration("tag1", "staticText")) .targetSpan(SpanDecorationProbe.TargetSpan.ACTIVE) .build(); addProbe(spanDecorationProbe);