Skip to content

The Stable Config migration from snakeyaml to the snakeyaml engine to support GraalVM Native #8790

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
May 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9a3ba87
Migrate from snakeyaml to snakeyaml-engine
ygree May 9, 2025
68d5f1d
Clean up
ygree May 9, 2025
a4deaea
Clean up stableconfig stuff
ygree May 9, 2025
9305638
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 12, 2025
4ac352a
Properly exclude snakeyaml-engine
ygree May 12, 2025
92446dc
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 12, 2025
ae34b12
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 13, 2025
956c96b
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 13, 2025
4174012
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 13, 2025
a160ce9
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 13, 2025
c556fa3
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 14, 2025
38e5389
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 15, 2025
f38f8ce
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 16, 2025
ec92fee
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 16, 2025
2c56ffe
Clean up
ygree May 20, 2025
84da741
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 20, 2025
fe3e325
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 21, 2025
b7fd4e5
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 21, 2025
b572aa5
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 21, 2025
24f89bc
Fix broken tests
ygree May 21, 2025
c68d1b1
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 21, 2025
9bc2136
Merge branch 'master' into ygree/snakeyaml-engine-migration
ygree May 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions components/yaml/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ plugins {

apply(from = "$rootDir/gradle/java.gradle")

// https://repo1.maven.org/maven2/org/yaml/snakeyaml/2.4/snakeyaml-2.4.pom
dependencies {
implementation("org.yaml", "snakeyaml", "2.4")
implementation("org.snakeyaml", "snakeyaml-engine", "2.9")
}
21 changes: 12 additions & 9 deletions components/yaml/src/main/java/datadog/yaml/YamlParser.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package datadog.yaml;

import org.yaml.snakeyaml.Yaml;
import org.snakeyaml.engine.v2.api.Load;
import org.snakeyaml.engine.v2.api.LoadSettings;

public class YamlParser {
// Supports clazz == null for default yaml parsing
public static <T> T parse(String content, Class<T> clazz) {
Yaml yaml = new Yaml();
if (clazz == null) {
return yaml.load(content);
} else {
return yaml.loadAs(content, clazz);
}
/**
* Parses YAML content. Duplicate keys are not allowed and will result in a runtime exception..
*
* @param content - text context to be parsed as YAML
* @return - a parsed representation as a composition of map and list objects.
*/
public static Object parse(String content) {
LoadSettings settings = LoadSettings.builder().build();
Load yaml = new Load(settings);
return yaml.loadFromString(content);
}
}
4 changes: 2 additions & 2 deletions dd-java-agent/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ ext.generalShadowJarConfig = {
// used to report our own dependencies, but we should remove the top-level metadata
// of vendored packages because those could trigger unwanted framework checks.
exclude '/META-INF/maven/org.slf4j/**'
exclude '/META-INF/maven/org.yaml/**'
exclude '/META-INF/maven/org.snakeyaml/**'
exclude '**/META-INF/maven/**/pom.xml'
exclude '**/META-INF/proguard/'
exclude '**/META-INF/*.kotlin_module'
Expand Down Expand Up @@ -66,7 +66,7 @@ ext.generalShadowJarConfig = {

// Prevents conflict with other instances, but doesn't relocate instrumentation
if (!projectName.equals('instrumentation')) {
relocate 'org.yaml.snakeyaml', 'datadog.snakeyaml'
relocate 'org.snakeyaml.engine', 'datadog.snakeyaml.engine'
relocate 'okhttp3', 'datadog.okhttp3'
relocate 'okio', 'datadog.okio'
}
Expand Down
2 changes: 1 addition & 1 deletion dd-java-agent/instrumentation/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ subprojects { Project subProj ->
jdkCompile = "main_${name}Implementation"
}
configurations.muzzleBootstrap {
exclude group: 'org.yaml', module : 'snakeyaml' // we vendor this in the agent jar
exclude group: 'org.snakeyaml', module : 'snakeyaml-engine' // we vendor this in the agent jar
}
dependencies {
// Apply common dependencies for instrumentation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ public static void onExit() {
tracerResources.add("profiling/jfr/overrides/minimal.jfp");

// jmxfetch configs
tracerResources.add(
"metrics/project.properties"); // org.datadog.jmxfetch.AppConfig reads its version
tracerResources.add("metrics/project.properties");
tracerResources.add("metrics/org/datadog/jmxfetch/default-jmx-metrics.yaml");
tracerResources.add("metrics/org/datadog/jmxfetch/new-gc-default-jmx-metrics.yaml");
tracerResources.add("metrics/org/datadog/jmxfetch/old-gc-default-jmx-metrics.yaml");
Expand Down
2 changes: 1 addition & 1 deletion dd-java-agent/testing/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ excludedClassesCoverage += [
]

configurations.api {
exclude group: 'org.yaml', module: 'snakeyaml' // we vendor this in the agent jar
exclude group: 'org.snakeyaml', module: 'snakeyaml-engine' // we vendor this in the agent jar
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ if (hasProperty('agentPath')) {
if (withProfiler && property('profiler') == 'true') {
buildArgs.add("-J-Ddd.profiling.enabled=true")
}
buildArgs.add("--enable-monitoring=jmxserver")
jvmArgs.add("-Xmx3072M")
}
}
Expand Down
4 changes: 2 additions & 2 deletions gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ final class CachedData {
// cafe_crypto and its transitives
exclude(dependency('cafe.cryptography::'))

// snakeyaml and its transitives
exclude(dependency('org.yaml:snakeyaml'))
// snakeyaml-engine and its transitives
exclude(dependency('org.snakeyaml:snakeyaml-engine'))
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion internal-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ dependencies {
// it contains annotations that are also present in the instrumented application classes
api "com.datadoghq:dd-javac-plugin-client:0.2.2"

testImplementation("org.yaml:snakeyaml:2.4")
testImplementation("org.snakeyaml:snakeyaml-engine:2.9")
testImplementation project(":utils:test-utils")
testImplementation("org.assertj:assertj-core:3.20.2")
testImplementation libs.bundles.junit5
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package datadog.trace.bootstrap.config.provider;

import datadog.trace.bootstrap.config.provider.stableconfigyaml.ConfigurationMap;
import datadog.trace.bootstrap.config.provider.stableconfigyaml.Rule;
import datadog.trace.bootstrap.config.provider.stableconfigyaml.Selector;
import datadog.trace.bootstrap.config.provider.stableconfigyaml.StableConfigYaml;
import datadog.trace.bootstrap.config.provider.stableconfig.Rule;
import datadog.trace.bootstrap.config.provider.stableconfig.Selector;
import datadog.trace.bootstrap.config.provider.stableconfig.StableConfig;
import datadog.yaml.YamlParser;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -41,26 +41,28 @@ public static StableConfigSource.StableConfig parse(String filePath) throws IOEx
try {
String content = new String(Files.readAllBytes(Paths.get(filePath)), StandardCharsets.UTF_8);
String processedContent = processTemplate(content);
StableConfigYaml data = YamlParser.parse(processedContent, StableConfigYaml.class);
Object parsedYaml = YamlParser.parse(processedContent);
StableConfig data = new StableConfig(parsedYaml);

String configId = data.getConfig_id();
ConfigurationMap configMap = data.getApm_configuration_default();
List<Rule> rules = data.getApm_configuration_rules();
String configId = data.getConfigId();
Map<String, Object> configMap = data.getApmConfigurationDefault();
List<Rule> rules = data.getApmConfigurationRules();

if (!rules.isEmpty()) {
for (Rule rule : rules) {
// Use the first matching rule
if (doesRuleMatch(rule)) {
// Merge configs found in apm_configuration_rules with those found in
// apm_configuration_default
configMap.putAll(rule.getConfiguration());
return createStableConfig(configId, configMap);
Map<String, Object> mergedConfigMap = new LinkedHashMap<>(configMap);
mergedConfigMap.putAll(rule.getConfiguration());
return new StableConfigSource.StableConfig(configId, mergedConfigMap);
}
}
}
// If configs were found in apm_configuration_default, use them
if (!configMap.isEmpty()) {
return createStableConfig(configId, configMap);
return new StableConfigSource.StableConfig(configId, configMap);
}

// If there's a configId but no configMap, use configId but return an empty map
Expand All @@ -69,10 +71,7 @@ public static StableConfigSource.StableConfig parse(String filePath) throws IOEx
}

} catch (IOException e) {
log.debug(
"Stable configuration file either not found or not readable at filepath {}. Error: {}",
filePath,
e.getMessage());
log.debug("Failed to read the stable configuration file: {}", filePath, e);
}
return StableConfigSource.StableConfig.EMPTY;
}
Expand All @@ -91,12 +90,6 @@ private static boolean doesRuleMatch(Rule rule) {
return true; // Return true if all selectors match
}

/** Creates a StableConfig object from the provided configId and configMap. */
private static StableConfigSource.StableConfig createStableConfig(
String configId, ConfigurationMap configMap) {
return new StableConfigSource.StableConfig(configId, new HashMap<>(configMap));
}

private static boolean validOperatorForLanguageOrigin(String operator) {
operator = operator.toLowerCase();
// "exists" is not valid
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package datadog.trace.bootstrap.config.provider.stableconfig;

import static java.util.Collections.*;
import static java.util.stream.Collectors.toList;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
* Rule represents a set of selectors and their corresponding configurations found in stable
* configuration files
*/
public final class Rule {
private final List<Selector> selectors;
private final Map<String, Object> configuration;

public Rule() {
this.selectors = emptyList();
this.configuration = emptyMap();
}

public Rule(List<Selector> selectors, Map<String, Object> configuration) {
this.selectors = selectors;
this.configuration = configuration;
}

public Rule(Object yaml) {
Map map = (Map) yaml;
selectors =
unmodifiableList(
((List<Object>) map.get("selectors"))
.stream().filter(Objects::nonNull).map(Selector::new).collect(toList()));
configuration = unmodifiableMap((Map<String, Object>) map.get("configuration"));
}

public List<Selector> getSelectors() {
return selectors;
}

public Map<String, Object> getConfiguration() {
return configuration;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package datadog.trace.bootstrap.config.provider.stableconfig;

import java.util.Collections;
import java.util.List;
import java.util.Map;

public final class Selector {
private final String origin;
private final String key;
private final List<String> matches;
private final String operator;

public Selector(String origin, String key, List<String> matches, String operator) {
this.origin = origin;
this.key = key;
this.matches = matches;
this.operator = operator;
}

public Selector(Object yaml) {
Map map = (Map) yaml;
origin = (String) map.get("origin");
key = (String) map.get("key");
matches = Collections.unmodifiableList((List<String>) map.get("matches"));
operator = (String) map.get("operator");
}

public String getOrigin() {
return origin;
}

public String getKey() {
return key;
}

public List<String> getMatches() {
return matches;
}

public String getOperator() {
return operator;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package datadog.trace.bootstrap.config.provider.stableconfig;

import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static java.util.stream.Collectors.toList;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public final class StableConfig {
private final String configId;
private final Map<String, Object> apmConfigurationDefault;
private final List<Rule> apmConfigurationRules;

public StableConfig(Object yaml) {
Map<Object, Object> map = (Map<Object, Object>) yaml;
this.configId = String.valueOf(map.get("config_id"));
this.apmConfigurationDefault =
unmodifiableMap(
(Map<String, Object>) map.getOrDefault("apm_configuration_default", emptyMap()));
this.apmConfigurationRules =
unmodifiableList(
((List<Object>) map.getOrDefault("apm_configuration_rules", emptyList()))
.stream().map(Rule::new).collect(toList()));
}

// test only
private StableConfig(String configId, Map<String, Object> apmConfigurationDefault) {
this.configId = configId;
this.apmConfigurationDefault = apmConfigurationDefault;
this.apmConfigurationRules = new ArrayList<>();
}

public String getConfigId() {
return configId;
}

public Map<String, Object> getApmConfigurationDefault() {
return apmConfigurationDefault;
}

public List<Rule> getApmConfigurationRules() {
return apmConfigurationRules;
}
}

This file was deleted.

This file was deleted.

Loading
Loading