Skip to content

Commit 2a04939

Browse files
authored
Update file probe format (#9047)
now it's only an array of probe with type as attribute. This is same format than stored in RemoteConfig. We need to parse it to look up using the type and reuse the moshi adapters to deserialize the probes. Add source as LOCAL_FILE
1 parent d20ab4d commit 2a04939

File tree

7 files changed

+282
-36
lines changed

7 files changed

+282
-36
lines changed

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ConfigurationAcceptor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
public interface ConfigurationAcceptor {
77
enum Source {
88
REMOTE_CONFIG,
9+
LOCAL_FILE,
910
CODE_ORIGIN,
1011
EXCEPTION
1112
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package com.datadog.debugger.agent;
2+
3+
import com.datadog.debugger.probe.LogProbe;
4+
import com.datadog.debugger.probe.MetricProbe;
5+
import com.datadog.debugger.probe.ProbeDefinition;
6+
import com.datadog.debugger.probe.SpanDecorationProbe;
7+
import com.datadog.debugger.probe.SpanProbe;
8+
import com.datadog.debugger.probe.TriggerProbe;
9+
import com.datadog.debugger.util.MoshiHelper;
10+
import com.squareup.moshi.JsonAdapter;
11+
import com.squareup.moshi.JsonReader;
12+
import com.squareup.moshi.JsonWriter;
13+
import com.squareup.moshi.Moshi;
14+
import com.squareup.moshi.Types;
15+
import datadog.trace.util.SizeCheckedInputStream;
16+
import java.io.ByteArrayInputStream;
17+
import java.io.ByteArrayOutputStream;
18+
import java.io.FileInputStream;
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
import java.lang.annotation.Annotation;
22+
import java.lang.reflect.ParameterizedType;
23+
import java.lang.reflect.Type;
24+
import java.nio.file.Path;
25+
import java.util.ArrayList;
26+
import java.util.List;
27+
import java.util.Set;
28+
import okio.Okio;
29+
import org.slf4j.Logger;
30+
import org.slf4j.LoggerFactory;
31+
32+
public class ConfigurationFileLoader {
33+
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationFileLoader.class);
34+
35+
public static Configuration from(Path probeFilePath, long maxPayloadSize) {
36+
LOGGER.debug("try to load from file...");
37+
try (InputStream inputStream =
38+
new SizeCheckedInputStream(new FileInputStream(probeFilePath.toFile()), maxPayloadSize)) {
39+
byte[] buffer = new byte[4096];
40+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(4096);
41+
int bytesRead;
42+
do {
43+
bytesRead = inputStream.read(buffer);
44+
if (bytesRead > -1) {
45+
outputStream.write(buffer, 0, bytesRead);
46+
}
47+
} while (bytesRead > -1);
48+
byte[] configContent = outputStream.toByteArray();
49+
Moshi moshi = MoshiHelper.createMoshiConfigBuilder().add(new ProbeFileFactory()).build();
50+
ParameterizedType type = Types.newParameterizedType(List.class, ProbeDefinition.class);
51+
JsonAdapter<List<ProbeDefinition>> adapter = moshi.adapter(type);
52+
List<ProbeDefinition> probeDefinitions =
53+
adapter.fromJson(
54+
JsonReader.of(Okio.buffer(Okio.source(new ByteArrayInputStream(configContent)))));
55+
return new Configuration(null, probeDefinitions);
56+
} catch (IOException ex) {
57+
LOGGER.error("Unable to load config file {}: {}", probeFilePath, ex);
58+
return null;
59+
}
60+
}
61+
62+
private static class ProbeFileFactory implements JsonAdapter.Factory {
63+
@Override
64+
public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi) {
65+
if (Types.equals(type, Types.newParameterizedType(List.class, ProbeDefinition.class))) {
66+
return new ProbeFileAdapter(
67+
moshi.adapter(LogProbe.class),
68+
moshi.adapter(MetricProbe.class),
69+
moshi.adapter(SpanProbe.class),
70+
moshi.adapter(SpanDecorationProbe.class),
71+
moshi.adapter(TriggerProbe.class));
72+
}
73+
return null;
74+
}
75+
}
76+
77+
private static class ProbeFileAdapter extends JsonAdapter<List<ProbeDefinition>> {
78+
private final JsonAdapter<LogProbe> logProbeAdapter;
79+
private final JsonAdapter<MetricProbe> metricProbeAdapter;
80+
private final JsonAdapter<SpanProbe> spanProbeAdapter;
81+
private final JsonAdapter<SpanDecorationProbe> spanDecorationProbeAdapter;
82+
private final JsonAdapter<TriggerProbe> triggerProbeAdapter;
83+
84+
public ProbeFileAdapter(
85+
JsonAdapter<LogProbe> logProbeAdapter,
86+
JsonAdapter<MetricProbe> metricProbeAdapter,
87+
JsonAdapter<SpanProbe> spanProbeAdapter,
88+
JsonAdapter<SpanDecorationProbe> spanDecorationProbeAdapter,
89+
JsonAdapter<TriggerProbe> triggerProbeAdapter) {
90+
this.logProbeAdapter = logProbeAdapter;
91+
this.metricProbeAdapter = metricProbeAdapter;
92+
this.spanProbeAdapter = spanProbeAdapter;
93+
this.spanDecorationProbeAdapter = spanDecorationProbeAdapter;
94+
this.triggerProbeAdapter = triggerProbeAdapter;
95+
}
96+
97+
@Override
98+
public List<ProbeDefinition> fromJson(JsonReader reader) throws IOException {
99+
List<ProbeDefinition> probeDefinitions = new ArrayList<>();
100+
reader.beginArray();
101+
while (reader.hasNext()) {
102+
if (reader.peek() == JsonReader.Token.END_ARRAY) {
103+
reader.endArray();
104+
break;
105+
}
106+
JsonReader jsonPeekReader = reader.peekJson();
107+
jsonPeekReader.beginObject();
108+
while (jsonPeekReader.hasNext()) {
109+
if (jsonPeekReader.selectName(JsonReader.Options.of("type")) == 0) {
110+
String type = jsonPeekReader.nextString();
111+
switch (type) {
112+
case "LOG_PROBE":
113+
probeDefinitions.add(logProbeAdapter.fromJson(reader));
114+
break;
115+
case "METRIC_PROBE":
116+
probeDefinitions.add(metricProbeAdapter.fromJson(reader));
117+
break;
118+
case "SPAN_PROBE":
119+
probeDefinitions.add(spanProbeAdapter.fromJson(reader));
120+
break;
121+
case "SPAN_DECORATION_PROBE":
122+
probeDefinitions.add(spanDecorationProbeAdapter.fromJson(reader));
123+
break;
124+
case "TRIGGER_PROBE":
125+
probeDefinitions.add(triggerProbeAdapter.fromJson(reader));
126+
break;
127+
default:
128+
throw new RuntimeException("Unknown type: " + type);
129+
}
130+
break;
131+
} else {
132+
jsonPeekReader.skipName();
133+
jsonPeekReader.skipValue();
134+
}
135+
}
136+
}
137+
return probeDefinitions;
138+
}
139+
140+
@Override
141+
public void toJson(JsonWriter writer, List<ProbeDefinition> value) throws IOException {
142+
// Implement the logic to write the list of ProbeDefinition to JSON
143+
}
144+
}
145+
}

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,8 @@
3333
import datadog.trace.bootstrap.debugger.util.Redaction;
3434
import datadog.trace.bootstrap.instrumentation.api.Tags;
3535
import datadog.trace.core.DDTraceCoreInfo;
36-
import datadog.trace.util.SizeCheckedInputStream;
3736
import datadog.trace.util.TagsHelper;
38-
import java.io.ByteArrayOutputStream;
39-
import java.io.FileInputStream;
4037
import java.io.IOException;
41-
import java.io.InputStream;
4238
import java.lang.instrument.ClassFileTransformer;
4339
import java.lang.instrument.Instrumentation;
4440
import java.lang.ref.WeakReference;
@@ -155,8 +151,14 @@ public static void startDynamicInstrumentation() {
155151
String probeFileLocation = config.getDynamicInstrumentationProbeFile();
156152
if (probeFileLocation != null) {
157153
Path probeFilePath = Paths.get(probeFileLocation);
158-
loadFromFile(
159-
probeFilePath, configurationUpdater, config.getDynamicInstrumentationMaxPayloadSize());
154+
Configuration configuration =
155+
ConfigurationFileLoader.from(
156+
probeFilePath, config.getDynamicInstrumentationMaxPayloadSize());
157+
if (configuration != null) {
158+
LOGGER.debug("Probe definitions loaded from file {}", probeFilePath);
159+
configurationUpdater.accept(
160+
ConfigurationAcceptor.Source.LOCAL_FILE, configuration.getDefinitions());
161+
}
160162
return;
161163
}
162164
if (configurationPoller != null) {
@@ -334,30 +336,6 @@ private static void setupSourceFileTracking(
334336
instrumentation.addTransformer(sourceFileTrackingTransformer);
335337
}
336338

337-
private static void loadFromFile(
338-
Path probeFilePath, ConfigurationUpdater configurationUpdater, long maxPayloadSize) {
339-
LOGGER.debug("try to load from file...");
340-
try (InputStream inputStream =
341-
new SizeCheckedInputStream(new FileInputStream(probeFilePath.toFile()), maxPayloadSize)) {
342-
byte[] buffer = new byte[4096];
343-
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(4096);
344-
int bytesRead;
345-
do {
346-
bytesRead = inputStream.read(buffer);
347-
if (bytesRead > -1) {
348-
outputStream.write(buffer, 0, bytesRead);
349-
}
350-
} while (bytesRead > -1);
351-
Configuration configuration =
352-
DebuggerProductChangesListener.Adapter.deserializeConfiguration(
353-
outputStream.toByteArray());
354-
LOGGER.debug("Probe definitions loaded from file {}", probeFilePath);
355-
configurationUpdater.accept(REMOTE_CONFIG, configuration.getDefinitions());
356-
} catch (IOException ex) {
357-
LOGGER.error("Unable to load config file {}: {}", probeFilePath, ex);
358-
}
359-
}
360-
361339
private static void subscribeConfigurationPoller(
362340
Config config, ConfigurationUpdater configurationUpdater, SymDBEnablement symDBEnablement) {
363341
LOGGER.debug("Subscribing to Live Debugging...");

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiHelper.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
public class MoshiHelper {
1818

1919
public static Moshi createMoshiConfig() {
20+
return createMoshiConfigBuilder().build();
21+
}
22+
23+
public static Moshi.Builder createMoshiConfigBuilder() {
2024
ProbeCondition.ProbeConditionJsonAdapter probeConditionJsonAdapter =
2125
new ProbeCondition.ProbeConditionJsonAdapter();
2226
return new Moshi.Builder()
@@ -25,8 +29,7 @@ public static Moshi createMoshiConfig() {
2529
.add(ValueScript.class, new ValueScript.ValueScriptAdapter())
2630
.add(LogProbe.Segment.class, new LogProbe.Segment.SegmentJsonAdapter())
2731
.add(Where.SourceLine[].class, new Where.SourceLineAdapter())
28-
.add(ProbeDefinition.Tag[].class, new ProbeDefinition.TagAdapter())
29-
.build();
32+
.add(ProbeDefinition.Tag[].class, new ProbeDefinition.TagAdapter());
3033
}
3134

3235
public static Moshi createMoshiSnapshot() {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.datadog.debugger.agent;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import com.datadog.debugger.probe.LogProbe;
6+
import com.datadog.debugger.probe.MetricProbe;
7+
import com.datadog.debugger.probe.ProbeDefinition;
8+
import com.datadog.debugger.probe.SpanDecorationProbe;
9+
import com.datadog.debugger.probe.SpanProbe;
10+
import java.nio.file.Path;
11+
import java.nio.file.Paths;
12+
import java.util.List;
13+
import org.junit.jupiter.api.Test;
14+
15+
class ConfigurationFileLoaderTest {
16+
17+
@Test
18+
public void load() throws Exception {
19+
Path probeFilePath =
20+
Paths.get(ConfigurationFileLoaderTest.class.getResource("/test_probe_file.json").toURI());
21+
Configuration configuration = ConfigurationFileLoader.from(probeFilePath, 1024 * 1024);
22+
assertNotNull(configuration);
23+
List<ProbeDefinition> definitions = configuration.getDefinitions();
24+
assertEquals(5, definitions.size());
25+
assertInstanceOf(MetricProbe.class, definitions.get(0));
26+
assertInstanceOf(LogProbe.class, definitions.get(1));
27+
assertInstanceOf(LogProbe.class, definitions.get(2));
28+
assertInstanceOf(SpanProbe.class, definitions.get(3));
29+
assertInstanceOf(SpanDecorationProbe.class, definitions.get(4));
30+
}
31+
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,8 @@ public void tearDown() throws IOException {
8383
@EnabledOnJre({JAVA_8, JAVA_11})
8484
public void runDisabled() {
8585
setFieldInConfig(Config.get(), "dynamicInstrumentationEnabled", false);
86-
URL probeDefinitionUrl = DebuggerAgentTest.class.getResource("/test_probe.json");
87-
System.setProperty("dd.dynamic.instrumentation.config-file", probeDefinitionUrl.getFile());
8886
DebuggerAgent.run(inst, new SharedCommunicationObjects());
8987
verify(inst, never()).addTransformer(any(), eq(true));
90-
System.clearProperty("dd.dynamic.instrumentation.config-file");
9188
}
9289

9390
@Test
@@ -148,7 +145,7 @@ public void runEnabledWithUnsupportedDatadogAgent() throws InterruptedException
148145
@Test
149146
@EnabledOnJre({JAVA_8, JAVA_11})
150147
public void readFromFile() throws URISyntaxException {
151-
URL res = getClass().getClassLoader().getResource("test_probe2.json");
148+
URL res = getClass().getClassLoader().getResource("test_probe_file.json");
152149
String probeDefinitionPath = Paths.get(res.toURI()).toFile().getAbsolutePath();
153150
setFieldInConfig(Config.get(), "serviceName", "petclinic");
154151
setFieldInConfig(Config.get(), "dynamicInstrumentationEnabled", true);
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
[
2+
{
3+
"id": "123356536",
4+
"version": 0,
5+
"language": "java",
6+
"type": "LOG_PROBE",
7+
"where": {
8+
"typeName": "java.lang.Object",
9+
"methodName": "toString",
10+
"signature": "java.lang.String ()"
11+
}
12+
},
13+
{
14+
"id": "100c9a5c-45ad-49dc-818b-c570d31e11d1",
15+
"version": 0,
16+
"type": "LOG_PROBE",
17+
"where": {
18+
"sourceFile": "index.js",
19+
"lines": ["25"]
20+
},
21+
"template": "Hello World",
22+
"segments": [{
23+
"str": "Hello World"
24+
}],
25+
"captureSnapshot": true,
26+
"capture": { "maxReferenceDepth": 3 },
27+
"sampling": { "snapshotsPerSecond": 100 }
28+
},
29+
{
30+
"id": "123356537",
31+
"language": "java",
32+
"type": "METRIC_PROBE",
33+
"where": {
34+
"typeName": "VetController",
35+
"methodName": "showVetList"
36+
},
37+
"tags": ["version:v123", "env:staging"],
38+
"kind": "COUNT",
39+
"metricName": "datadog.debugger.showVetList.calls",
40+
"value": {
41+
"dsl": "42",
42+
"json": 42
43+
}
44+
},
45+
{
46+
"id": "ad4cba6f-d476-4554-b5ed-80dd941a40d8",
47+
"version": 0,
48+
"type": "SPAN_DECORATION_PROBE",
49+
"language": "java",
50+
"where": {
51+
"typeName": "com.datadog.debugger.demomonitor.DemoRequestScheduledJob",
52+
"methodName": "fireRequests",
53+
"signature": "()"
54+
},
55+
"tags": [],
56+
"evaluateAt": "EXIT",
57+
"targetSpan": "ROOT",
58+
"decorations": [
59+
{
60+
"tags": [
61+
{
62+
"name": "client",
63+
"value": {
64+
"segments": [
65+
{
66+
"dsl": "client",
67+
"json": {
68+
"ref": "client"
69+
}
70+
}
71+
],
72+
"template": "{client}"
73+
}
74+
}
75+
]
76+
}
77+
]
78+
},
79+
{
80+
"id": "70b55d06-f9fa-403b-a329-4f2f960aed01",
81+
"version": 0,
82+
"type": "SPAN_PROBE",
83+
"language": "java",
84+
"where": {
85+
"typeName": "MetadataClientUtils",
86+
"methodName": "listTableWithContinuation"
87+
},
88+
"tags": [],
89+
"evaluateAt": "EXIT"
90+
}
91+
]

0 commit comments

Comments
 (0)