Skip to content

Commit 87351d8

Browse files
authored
add support for org level APM_TRACING configs (#9360)
1 parent 7cf5888 commit 87351d8

File tree

3 files changed

+453
-8
lines changed

3 files changed

+453
-8
lines changed

dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java

Lines changed: 169 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_ENABLE_DYNAMIC_INSTRUMENTATION;
99
import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_ENABLE_EXCEPTION_REPLAY;
1010
import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_ENABLE_LIVE_DEBUGGING;
11+
import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_MULTICONFIG;
1112
import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_SAMPLE_RATE;
1213
import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_SAMPLE_RULES;
1314
import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_TRACING_ENABLED;
@@ -34,6 +35,7 @@
3435
import java.io.ByteArrayInputStream;
3536
import java.io.IOException;
3637
import java.util.Collections;
38+
import java.util.Comparator;
3739
import java.util.HashMap;
3840
import java.util.Iterator;
3941
import java.util.List;
@@ -74,7 +76,8 @@ public void start(Config config, SharedCommunicationObjects sco) {
7476
| CAPABILITY_APM_TRACING_ENABLE_DYNAMIC_INSTRUMENTATION
7577
| CAPABILITY_APM_TRACING_ENABLE_EXCEPTION_REPLAY
7678
| CAPABILITY_APM_TRACING_ENABLE_CODE_ORIGIN
77-
| CAPABILITY_APM_TRACING_ENABLE_LIVE_DEBUGGING);
79+
| CAPABILITY_APM_TRACING_ENABLE_LIVE_DEBUGGING
80+
| CAPABILITY_APM_TRACING_MULTICONFIG);
7881
}
7982
stopPolling = new Updater().register(config, configPoller);
8083
}
@@ -87,14 +90,17 @@ public void stop() {
8790

8891
final class Updater implements ProductListener {
8992
private final JsonAdapter<ConfigOverrides> CONFIG_OVERRIDES_ADAPTER;
93+
private final JsonAdapter<LibConfig> LIB_CONFIG_ADAPTER;
9094
private final JsonAdapter<TracingSamplingRule> TRACE_SAMPLING_RULE;
9195

9296
{
9397
Moshi MOSHI = new Moshi.Builder().add(new TracingSamplingRulesAdapter()).build();
9498
CONFIG_OVERRIDES_ADAPTER = MOSHI.adapter(ConfigOverrides.class);
99+
LIB_CONFIG_ADAPTER = MOSHI.adapter(LibConfig.class);
95100
TRACE_SAMPLING_RULE = MOSHI.adapter(TracingSamplingRule.class);
96101
}
97102

103+
private final Map<String, ConfigOverrides> configs = new HashMap<>();
98104
private boolean receivedOverrides = false;
99105

100106
public Runnable register(Config config, ConfigurationPoller poller) {
@@ -115,27 +121,46 @@ public void accept(ConfigKey configKey, byte[] content, PollingRateHinter hinter
115121
Okio.buffer(Okio.source(new ByteArrayInputStream(content))));
116122

117123
if (null != overrides && null != overrides.libConfig) {
118-
receivedOverrides = true;
119-
applyConfigOverrides(checkConfig(overrides.libConfig));
124+
configs.put(configKey.getConfigId(), overrides);
120125
if (log.isDebugEnabled()) {
121126
log.debug(
122-
"Applied APM_TRACING overrides: {}", CONFIG_OVERRIDES_ADAPTER.toJson(overrides));
127+
"Applied APM_TRACING overrides: {} - priority: {}",
128+
CONFIG_OVERRIDES_ADAPTER.toJson(overrides),
129+
overrides.getOverridePriority());
123130
}
124131
} else {
125132
log.debug("No APM_TRACING overrides");
126133
}
127134
}
128135

129136
@Override
130-
public void remove(ConfigKey configKey, PollingRateHinter hinter) {}
137+
public void remove(ConfigKey configKey, PollingRateHinter hinter) {
138+
configs.remove(configKey.getConfigId());
139+
}
131140

132141
@Override
133142
public void commit(PollingRateHinter hinter) {
134-
if (!receivedOverrides) {
143+
// sort configs by override priority
144+
List<LibConfig> sortedConfigs =
145+
configs.values().stream()
146+
.sorted(Comparator.comparingInt(ConfigOverrides::getOverridePriority).reversed())
147+
.map(config -> config.libConfig)
148+
.collect(Collectors.toList());
149+
150+
LibConfig mergedConfig = LibConfig.mergeLibConfigs(sortedConfigs);
151+
152+
if (mergedConfig != null) {
153+
// apply merged config
154+
if (log.isDebugEnabled()) {
155+
log.debug(
156+
"Applying merged APM_TRACING config: {}", LIB_CONFIG_ADAPTER.toJson(mergedConfig));
157+
}
158+
applyConfigOverrides(checkConfig(mergedConfig));
159+
}
160+
161+
if (sortedConfigs.isEmpty()) {
135162
removeConfigOverrides();
136163
log.debug("Removed APM_TRACING overrides");
137-
} else {
138-
receivedOverrides = false;
139164
}
140165
}
141166

@@ -263,6 +288,77 @@ private Map<String, String> parseTagListToMap(List<String> input) {
263288
static final class ConfigOverrides {
264289
@Json(name = "lib_config")
265290
public LibConfig libConfig;
291+
292+
@Json(name = "service_target")
293+
public ServiceTarget serviceTarget;
294+
295+
@Json(name = "k8s_target_v2")
296+
public K8sTargetV2 k8sTargetV2;
297+
298+
public int getOverridePriority() {
299+
boolean isSingleEnvironment = isSingleEnvironment();
300+
boolean isSingleService = isSingleService();
301+
boolean isClusterTarget = isClusterTarget();
302+
303+
// Service+ Environment level override - highest priority
304+
if (isSingleEnvironment && isSingleService) {
305+
return 5;
306+
}
307+
308+
if (isSingleService) {
309+
return 4;
310+
}
311+
312+
if (isSingleEnvironment) {
313+
return 3;
314+
}
315+
316+
if (isClusterTarget) {
317+
return 2;
318+
}
319+
320+
// Org level override - lowest priority
321+
return 1;
322+
}
323+
324+
// allEnvironments = serviceTarget is null or serviceTarget.env is null or '*'
325+
public boolean isSingleEnvironment() {
326+
return serviceTarget != null && serviceTarget.env != null && !"*".equals(serviceTarget.env);
327+
}
328+
329+
public boolean isSingleService() {
330+
return serviceTarget != null
331+
&& serviceTarget.service != null
332+
&& !"*".equals(serviceTarget.service);
333+
}
334+
335+
public boolean isClusterTarget() {
336+
return k8sTargetV2 != null;
337+
}
338+
}
339+
340+
static final class ServiceTarget {
341+
@Json(name = "service")
342+
public String service;
343+
344+
@Json(name = "env")
345+
public String env;
346+
}
347+
348+
static final class K8sTargetV2 {
349+
@Json(name = "cluster_targets")
350+
public List<ClusterTarget> clusterTargets;
351+
}
352+
353+
static final class ClusterTarget {
354+
@Json(name = "cluster_name")
355+
public String clusterName;
356+
357+
@Json(name = "enabled")
358+
public Boolean enabled;
359+
360+
@Json(name = "enabled_namespaces")
361+
public List<String> enabledNamespaces;
266362
}
267363

268364
static final class LibConfig {
@@ -307,6 +403,71 @@ static final class LibConfig {
307403

308404
@Json(name = "live_debugging_enabled")
309405
public Boolean liveDebuggingEnabled;
406+
407+
/**
408+
* Merges a list of LibConfig objects by taking the first non-null value for each field.
409+
*
410+
* @param configs the list of LibConfig objects to merge
411+
* @return a merged LibConfig object, or null if the input list is null or empty
412+
*/
413+
public static LibConfig mergeLibConfigs(List<LibConfig> configs) {
414+
if (configs == null || configs.isEmpty()) {
415+
return null;
416+
}
417+
418+
LibConfig merged = new LibConfig();
419+
420+
for (LibConfig config : configs) {
421+
if (config == null) {
422+
continue;
423+
}
424+
425+
if (merged.tracingEnabled == null) {
426+
merged.tracingEnabled = config.tracingEnabled;
427+
}
428+
if (merged.debugEnabled == null) {
429+
merged.debugEnabled = config.debugEnabled;
430+
}
431+
if (merged.runtimeMetricsEnabled == null) {
432+
merged.runtimeMetricsEnabled = config.runtimeMetricsEnabled;
433+
}
434+
if (merged.logsInjectionEnabled == null) {
435+
merged.logsInjectionEnabled = config.logsInjectionEnabled;
436+
}
437+
if (merged.dataStreamsEnabled == null) {
438+
merged.dataStreamsEnabled = config.dataStreamsEnabled;
439+
}
440+
if (merged.serviceMapping == null) {
441+
merged.serviceMapping = config.serviceMapping;
442+
}
443+
if (merged.headerTags == null) {
444+
merged.headerTags = config.headerTags;
445+
}
446+
if (merged.traceSampleRate == null) {
447+
merged.traceSampleRate = config.traceSampleRate;
448+
}
449+
if (merged.tracingTags == null) {
450+
merged.tracingTags = config.tracingTags;
451+
}
452+
if (merged.tracingSamplingRules == null) {
453+
merged.tracingSamplingRules = config.tracingSamplingRules;
454+
}
455+
if (merged.dynamicInstrumentationEnabled == null) {
456+
merged.dynamicInstrumentationEnabled = config.dynamicInstrumentationEnabled;
457+
}
458+
if (merged.exceptionReplayEnabled == null) {
459+
merged.exceptionReplayEnabled = config.exceptionReplayEnabled;
460+
}
461+
if (merged.codeOriginEnabled == null) {
462+
merged.codeOriginEnabled = config.codeOriginEnabled;
463+
}
464+
if (merged.liveDebuggingEnabled == null) {
465+
merged.liveDebuggingEnabled = config.liveDebuggingEnabled;
466+
}
467+
}
468+
469+
return merged;
470+
}
310471
}
311472

312473
/** Holds the raw JSON string and the parsed rule data. */

0 commit comments

Comments
 (0)