Skip to content

Commit b708841

Browse files
committed
Support for nocode rules embedded in declarative config YAML
1 parent c984279 commit b708841

File tree

5 files changed

+125
-37
lines changed

5 files changed

+125
-37
lines changed

custom/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfigureUtil.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import io.opentelemetry.api.OpenTelemetry;
2222
import io.opentelemetry.api.incubator.ExtendedOpenTelemetry;
23+
import io.opentelemetry.api.incubator.config.ConfigProvider;
2324
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
2425
import io.opentelemetry.common.ComponentLoader;
2526
import io.opentelemetry.instrumentation.config.bridge.ConfigPropertiesBackedConfigProvider;
@@ -54,12 +55,17 @@ public static ConfigProperties getConfig(AutoConfiguredOpenTelemetrySdk sdk) {
5455
}
5556

5657
public static boolean isDeclarativeConfig(AutoConfiguredOpenTelemetrySdk sdk) {
58+
ConfigProvider configProvider = getConfigProvider(sdk);
59+
return configProvider != null
60+
&& !(configProvider instanceof ConfigPropertiesBackedConfigProvider);
61+
}
62+
63+
public static ConfigProvider getConfigProvider(AutoConfiguredOpenTelemetrySdk sdk) {
5764
OpenTelemetry openTelemetry = sdk.getOpenTelemetrySdk();
5865
if (openTelemetry instanceof ExtendedOpenTelemetry) {
59-
return !(((ExtendedOpenTelemetry) openTelemetry).getConfigProvider()
60-
instanceof ConfigPropertiesBackedConfigProvider);
66+
return ((ExtendedOpenTelemetry) openTelemetry).getConfigProvider();
6167
}
62-
return false;
68+
return null;
6369
}
6470

6571
public static DeclarativeConfigProperties getDistributionConfig(

instrumentation/nocode-testing/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
plugins {
2+
id("splunk.instrumentation-conventions")
3+
}
4+
15
dependencies {
26
testImplementation(project(":bootstrap"))
7+
testImplementation(project(":custom"))
38
testImplementation(project(":instrumentation:nocode"))
49
testImplementation("org.snakeyaml:snakeyaml-engine")
510
}

instrumentation/nocode-testing/src/test/java/com/splunk/opentelemetry/instrumentation/nocode/YamlParserTest.java

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,17 @@
1616

1717
package com.splunk.opentelemetry.instrumentation.nocode;
1818

19+
import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty;
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
1922
import static org.junit.jupiter.api.Assertions.assertEquals;
20-
import static org.junit.jupiter.api.Assertions.fail;
2123

24+
import com.splunk.opentelemetry.javaagent.bootstrap.nocode.NocodeRules;
25+
import com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil;
26+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
27+
import io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil;
28+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
29+
import java.util.List;
2230
import org.junit.jupiter.api.Test;
2331
import org.junit.jupiter.params.ParameterizedTest;
2432
import org.junit.jupiter.params.provider.ValueSource;
@@ -93,11 +101,33 @@ void testBasicRuleParsesOK() throws Exception {
93101
})
94102
// spotless:on
95103
void invalidYamlIsInvalid(String yaml) {
96-
try {
97-
YamlParser.parseFromString(yaml);
98-
fail("Expected an exception parsing broken yaml");
99-
} catch (Exception expected) {
100-
// ok
101-
}
104+
assertThatThrownBy(() -> YamlParser.parseFromString(yaml)).isInstanceOf(RuntimeException.class);
105+
}
106+
107+
@Test
108+
void shouldParseFromDeclarativeConfigYaml() {
109+
// given
110+
var yaml =
111+
"""
112+
file_format: "1.0-rc.3"
113+
instrumentation/development:
114+
java:
115+
splunk:
116+
nocode:
117+
- class: foo.Foo
118+
method: foo
119+
- class: foo.Foo
120+
method: throwSomething
121+
""";
122+
OpenTelemetryConfigurationModel model = DeclarativeConfigTestUtil.parse(yaml);
123+
DeclarativeConfigProperties splunkRoot =
124+
AutoConfigureUtil.getInstrumentationConfig(model).getStructured("splunk", empty());
125+
List<DeclarativeConfigProperties> ruleNodes = splunkRoot.getStructuredList("nocode");
126+
127+
// when
128+
List<NocodeRules.Rule> rules = YamlParser.parseFromDeclarativeConfig(ruleNodes);
129+
130+
// then
131+
assertThat(rules).hasSize(2);
102132
}
103133
}

instrumentation/nocode/src/main/java/com/splunk/opentelemetry/instrumentation/nocode/NocodeInitializer.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,19 @@
1717
package com.splunk.opentelemetry.instrumentation.nocode;
1818

1919
import static io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil.getConfig;
20+
import static io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil.getConfigProvider;
21+
import static io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil.isDeclarativeConfig;
2022

2123
import com.google.auto.service.AutoService;
2224
import com.splunk.opentelemetry.javaagent.bootstrap.nocode.NocodeRules;
25+
import io.opentelemetry.api.incubator.config.ConfigProvider;
26+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
2327
import io.opentelemetry.javaagent.tooling.BeforeAgentListener;
2428
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
2529
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
2630
import java.util.Collections;
2731
import java.util.List;
32+
import java.util.Objects;
2833
import java.util.logging.Level;
2934
import java.util.logging.Logger;
3035

@@ -35,7 +40,25 @@ public class NocodeInitializer implements BeforeAgentListener {
3540

3641
@Override
3742
public void beforeAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
38-
ConfigProperties config = getConfig(autoConfiguredOpenTelemetrySdk);
43+
if (isDeclarativeConfig(autoConfiguredOpenTelemetrySdk)) {
44+
processEmbeddedRules(getConfigProvider(autoConfiguredOpenTelemetrySdk));
45+
}
46+
processRulesFromExternalFile(getConfig(autoConfiguredOpenTelemetrySdk));
47+
}
48+
49+
private static void processEmbeddedRules(ConfigProvider configProvider) {
50+
Objects.requireNonNull(configProvider);
51+
DeclarativeConfigProperties config = configProvider.getInstrumentationConfig("splunk");
52+
53+
if (config != null) {
54+
List<DeclarativeConfigProperties> nocodeRules = config.getStructuredList("nocode");
55+
if (nocodeRules != null) {
56+
NocodeRules.setGlobalRules(YamlParser.parseFromDeclarativeConfig(nocodeRules));
57+
}
58+
}
59+
}
60+
61+
private static void processRulesFromExternalFile(ConfigProperties config) {
3962
String yamlFileName = config.getString(NOCODE_YMLFILE);
4063
if (yamlFileName == null || yamlFileName.trim().isEmpty()) {
4164
return;

instrumentation/nocode/src/main/java/com/splunk/opentelemetry/instrumentation/nocode/YamlParser.java

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.splunk.opentelemetry.javaagent.bootstrap.nocode.NocodeExpression;
2020
import com.splunk.opentelemetry.javaagent.bootstrap.nocode.NocodeRules;
21+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
2122
import io.opentelemetry.api.trace.SpanKind;
2223
import io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers;
2324
import java.io.IOException;
@@ -33,6 +34,7 @@
3334
import java.util.Locale;
3435
import java.util.Map;
3536
import java.util.logging.Logger;
37+
import java.util.stream.Collectors;
3638
import net.bytebuddy.description.NamedElement;
3739
import net.bytebuddy.description.method.MethodDescription;
3840
import net.bytebuddy.description.type.TypeDescription;
@@ -58,47 +60,69 @@ static List<NocodeRules.Rule> parseFromString(String yaml) throws IOException {
5860
return new YamlParser(new StringReader(yaml)).getInstrumentationRules();
5961
}
6062

63+
static List<NocodeRules.Rule> parseFromDeclarativeConfig(
64+
List<DeclarativeConfigProperties> ruleNodes) {
65+
List<Map<String, Object>> rulesList =
66+
ruleNodes.stream().map(DeclarativeConfigProperties::toMap).collect(Collectors.toList());
67+
68+
return new YamlParser(rulesList).getInstrumentationRules();
69+
}
70+
6171
private YamlParser(Reader reader) throws IOException {
6272
instrumentationRules = Collections.unmodifiableList(new ArrayList<>(loadUnsafe(reader)));
6373
}
6474

75+
private YamlParser(List<Map<String, Object>> ruleListNode) {
76+
instrumentationRules = Collections.unmodifiableList(loadUnsafe(ruleListNode));
77+
}
78+
6579
private List<NocodeRules.Rule> getInstrumentationRules() {
6680
return instrumentationRules;
6781
}
6882

83+
@SuppressWarnings("unchecked")
84+
private List<NocodeRules.Rule> loadUnsafe(List<Map<String, Object>> ruleListNode) {
85+
List<NocodeRules.Rule> answer = new ArrayList<>();
86+
87+
for (Map<String, Object> yamlRule : ruleListNode) {
88+
ElementMatcher<TypeDescription> classMatcher = classMatcher(yamlRule.get("class"));
89+
ElementMatcher<MethodDescription> methodMatcher = methodMatcher(yamlRule.get("method"));
90+
NocodeExpression spanName = toExpression(yamlRule.get("span_name"));
91+
SpanKind spanKind = null;
92+
if (yamlRule.get("span_kind") != null) {
93+
String spanKindString = yamlRule.get("span_kind").toString();
94+
try {
95+
spanKind = SpanKind.valueOf(spanKindString.toUpperCase(Locale.ROOT));
96+
} catch (IllegalArgumentException exception) {
97+
logger.warning("Invalid span kind " + spanKindString);
98+
}
99+
}
100+
NocodeExpression spanStatus = toExpression(yamlRule.get("span_status"));
101+
102+
Map<String, NocodeExpression> ruleAttributes = new HashMap<>();
103+
List<Map<String, Object>> attrs = (List<Map<String, Object>>) yamlRule.get("attributes");
104+
if (attrs != null) {
105+
for (Map<String, Object> attr : attrs) {
106+
ruleAttributes.put(attr.get("key").toString(), toExpression(attr.get("value")));
107+
}
108+
}
109+
answer.add(
110+
new RuleImpl(
111+
classMatcher, methodMatcher, spanName, spanKind, spanStatus, ruleAttributes));
112+
}
113+
114+
return answer;
115+
}
116+
69117
@SuppressWarnings("unchecked")
70118
private List<NocodeRules.Rule> loadUnsafe(Reader reader) throws IOException {
71119
List<NocodeRules.Rule> answer = new ArrayList<>();
72120
Load load = new Load(LoadSettings.builder().build());
73121
Iterable<Object> parsedYaml = load.loadAllFromReader(reader);
122+
74123
for (Object yamlBit : parsedYaml) {
75124
List<Map<String, Object>> rulesMap = (List<Map<String, Object>>) yamlBit;
76-
for (Map<String, Object> yamlRule : rulesMap) {
77-
ElementMatcher<TypeDescription> classMatcher = classMatcher(yamlRule.get("class"));
78-
ElementMatcher<MethodDescription> methodMatcher = methodMatcher(yamlRule.get("method"));
79-
NocodeExpression spanName = toExpression(yamlRule.get("span_name"));
80-
SpanKind spanKind = null;
81-
if (yamlRule.get("span_kind") != null) {
82-
String spanKindString = yamlRule.get("span_kind").toString();
83-
try {
84-
spanKind = SpanKind.valueOf(spanKindString.toUpperCase(Locale.ROOT));
85-
} catch (IllegalArgumentException exception) {
86-
logger.warning("Invalid span kind " + spanKindString);
87-
}
88-
}
89-
NocodeExpression spanStatus = toExpression(yamlRule.get("span_status"));
90-
91-
Map<String, NocodeExpression> ruleAttributes = new HashMap<>();
92-
List<Map<String, Object>> attrs = (List<Map<String, Object>>) yamlRule.get("attributes");
93-
if (attrs != null) {
94-
for (Map<String, Object> attr : attrs) {
95-
ruleAttributes.put(attr.get("key").toString(), toExpression(attr.get("value")));
96-
}
97-
}
98-
answer.add(
99-
new RuleImpl(
100-
classMatcher, methodMatcher, spanName, spanKind, spanStatus, ruleAttributes));
101-
}
125+
answer.addAll(loadUnsafe(rulesMap));
102126
}
103127

104128
return answer;

0 commit comments

Comments
 (0)