Skip to content

Commit 780cdf4

Browse files
authored
Support declarative configuration (#12265)
1 parent d8eddfc commit 780cdf4

File tree

11 files changed

+329
-16
lines changed

11 files changed

+329
-16
lines changed

instrumentation/jmx-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstaller.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import io.opentelemetry.instrumentation.jmx.yaml.RuleParser;
1616
import io.opentelemetry.javaagent.extension.AgentListener;
1717
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
18-
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
1918
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
2019
import java.io.InputStream;
2120
import java.nio.file.Files;
@@ -29,7 +28,7 @@ public class JmxMetricInsightInstaller implements AgentListener {
2928

3029
@Override
3130
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
32-
ConfigProperties config = AutoConfigureUtil.getConfig(autoConfiguredSdk);
31+
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);
3332

3433
if (config.getBoolean("otel.jmx.enabled", true)) {
3534
JmxMetricInsight service =

instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/OshiMetricsInstaller.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import com.google.auto.service.AutoService;
99
import io.opentelemetry.javaagent.extension.AgentListener;
1010
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
11-
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
1211
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
1312
import java.lang.reflect.Method;
1413

@@ -21,7 +20,7 @@ public class OshiMetricsInstaller implements AgentListener {
2120

2221
@Override
2322
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
24-
ConfigProperties config = AutoConfigureUtil.getConfig(autoConfiguredSdk);
23+
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);
2524

2625
boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
2726
if (!config.getBoolean("otel.instrumentation.oshi.enabled", defaultEnabled)) {

instrumentation/runtime-telemetry/runtime-telemetry-java17/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java17/Java17RuntimeMetricsInstaller.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetricsBuilder;
1313
import io.opentelemetry.javaagent.extension.AgentListener;
1414
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
15-
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
1615
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
1716

1817
/** An {@link AgentListener} that enables runtime metrics during agent startup. */
@@ -21,7 +20,7 @@ public class Java17RuntimeMetricsInstaller implements AgentListener {
2120

2221
@Override
2322
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
24-
ConfigProperties config = AutoConfigureUtil.getConfig(autoConfiguredSdk);
23+
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);
2524

2625
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
2726
RuntimeMetricsBuilder builder = null;

instrumentation/runtime-telemetry/runtime-telemetry-java8/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/JarAnalyzerInstaller.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
import com.google.auto.service.AutoService;
99
import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder;
10+
import io.opentelemetry.javaagent.extension.AgentListener;
1011
import io.opentelemetry.javaagent.tooling.BeforeAgentListener;
1112
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
12-
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
1313
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
1414
import java.lang.instrument.Instrumentation;
1515

@@ -19,7 +19,8 @@ public class JarAnalyzerInstaller implements BeforeAgentListener {
1919

2020
@Override
2121
public void beforeAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
22-
ConfigProperties config = AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk);
22+
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredOpenTelemetrySdk);
23+
2324
boolean enabled =
2425
config.getBoolean("otel.instrumentation.runtime-telemetry.package-emitter.enabled", false);
2526
if (!enabled) {

instrumentation/runtime-telemetry/runtime-telemetry-java8/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/Java8RuntimeMetricsInstaller.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
2020
import io.opentelemetry.javaagent.extension.AgentListener;
2121
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
22-
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
2322
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
2423
import java.util.ArrayList;
2524
import java.util.List;
@@ -30,7 +29,7 @@ public class Java8RuntimeMetricsInstaller implements AgentListener {
3029

3130
@Override
3231
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
33-
ConfigProperties config = AutoConfigureUtil.getConfig(autoConfiguredSdk);
32+
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);
3433

3534
boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
3635
if (!config.getBoolean("otel.instrumentation.runtime-telemetry.enabled", defaultEnabled)

javaagent-extension-api/build.gradle.kts

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ dependencies {
1717
// autoconfigure is unstable, do not expose as api
1818
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
1919

20+
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")
21+
2022
// Used by byte-buddy but not brought in as a transitive dependency.
2123
compileOnly("com.google.code.findbugs:annotations")
2224
}

javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/AgentListener.java

+21
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
package io.opentelemetry.javaagent.extension;
77

88
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
9+
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
10+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
911
import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
12+
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
1013
import java.lang.instrument.Instrumentation;
1114
import net.bytebuddy.agent.builder.AgentBuilder;
1215

@@ -25,4 +28,22 @@ public interface AgentListener extends Ordered {
2528
* on an {@link Instrumentation}.
2629
*/
2730
void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk);
31+
32+
/** Resolve {@link ConfigProperties} from the {@code autoConfiguredOpenTelemetrySdk}. */
33+
static ConfigProperties resolveConfigProperties(
34+
AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
35+
ConfigProperties sdkConfigProperties =
36+
AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk);
37+
if (sdkConfigProperties != null) {
38+
return sdkConfigProperties;
39+
}
40+
StructuredConfigProperties structuredConfigProperties =
41+
AutoConfigureUtil.getStructuredConfig(autoConfiguredOpenTelemetrySdk);
42+
if (structuredConfigProperties != null) {
43+
return new StructuredConfigPropertiesBridge(structuredConfigProperties);
44+
}
45+
// Should never happen
46+
throw new IllegalStateException(
47+
"AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or StructuredConfigProperties. This is likely a programming error in opentelemetry-java");
48+
}
2849
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.extension;
7+
8+
import static io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties.empty;
9+
10+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
11+
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
12+
import java.time.Duration;
13+
import java.util.Collections;
14+
import java.util.HashMap;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.function.BiFunction;
18+
import javax.annotation.Nullable;
19+
20+
/**
21+
* A {@link ConfigProperties} which resolves properties based on {@link StructuredConfigProperties}.
22+
*
23+
* <p>Only properties starting with "otel.instrumentation." are resolved. Others return null (or
24+
* default value if provided).
25+
*
26+
* <p>To resolve:
27+
*
28+
* <ul>
29+
* <li>"otel.instrumentation" refers to the ".instrumentation.java" node
30+
* <li>The portion of the property after "otel.instrumentation." is split into segments based on
31+
* ".".
32+
* <li>For each N-1 segment, we walk down the tree to find the relevant leaf {@link
33+
* StructuredConfigProperties}.
34+
* <li>We extract the property from the resolved {@link StructuredConfigProperties} using the last
35+
* segment as the property key.
36+
* </ul>
37+
*
38+
* <p>For example, given the following YAML, asking for {@code
39+
* ConfigProperties#getString("otel.instrumentation.common.string_key")} yields "value":
40+
*
41+
* <pre>
42+
* instrumentation:
43+
* java:
44+
* common:
45+
* string_key: value
46+
* </pre>
47+
*/
48+
final class StructuredConfigPropertiesBridge implements ConfigProperties {
49+
50+
private static final String OTEL_INSTRUMENTATION_PREFIX = "otel.instrumentation.";
51+
52+
// The node at .instrumentation.java
53+
private final StructuredConfigProperties instrumentationJavaNode;
54+
55+
StructuredConfigPropertiesBridge(StructuredConfigProperties rootStructuredConfigProperties) {
56+
instrumentationJavaNode =
57+
rootStructuredConfigProperties
58+
.getStructured("instrumentation", empty())
59+
.getStructured("java", empty());
60+
}
61+
62+
@Nullable
63+
@Override
64+
public String getString(String propertyName) {
65+
return getPropertyValue(propertyName, StructuredConfigProperties::getString);
66+
}
67+
68+
@Nullable
69+
@Override
70+
public Boolean getBoolean(String propertyName) {
71+
return getPropertyValue(propertyName, StructuredConfigProperties::getBoolean);
72+
}
73+
74+
@Nullable
75+
@Override
76+
public Integer getInt(String propertyName) {
77+
return getPropertyValue(propertyName, StructuredConfigProperties::getInt);
78+
}
79+
80+
@Nullable
81+
@Override
82+
public Long getLong(String propertyName) {
83+
return getPropertyValue(propertyName, StructuredConfigProperties::getLong);
84+
}
85+
86+
@Nullable
87+
@Override
88+
public Double getDouble(String propertyName) {
89+
return getPropertyValue(propertyName, StructuredConfigProperties::getDouble);
90+
}
91+
92+
@Nullable
93+
@Override
94+
public Duration getDuration(String propertyName) {
95+
Long millis = getPropertyValue(propertyName, StructuredConfigProperties::getLong);
96+
if (millis == null) {
97+
return null;
98+
}
99+
return Duration.ofMillis(millis);
100+
}
101+
102+
@Override
103+
public List<String> getList(String propertyName) {
104+
List<String> propertyValue =
105+
getPropertyValue(
106+
propertyName,
107+
(properties, lastPart) -> properties.getScalarList(lastPart, String.class));
108+
return propertyValue == null ? Collections.emptyList() : propertyValue;
109+
}
110+
111+
@Override
112+
public Map<String, String> getMap(String propertyName) {
113+
StructuredConfigProperties propertyValue =
114+
getPropertyValue(propertyName, StructuredConfigProperties::getStructured);
115+
if (propertyValue == null) {
116+
return Collections.emptyMap();
117+
}
118+
Map<String, String> result = new HashMap<>();
119+
propertyValue
120+
.getPropertyKeys()
121+
.forEach(
122+
key -> {
123+
String value = propertyValue.getString(key);
124+
if (value == null) {
125+
return;
126+
}
127+
result.put(key, value);
128+
});
129+
return Collections.unmodifiableMap(result);
130+
}
131+
132+
@Nullable
133+
private <T> T getPropertyValue(
134+
String property, BiFunction<StructuredConfigProperties, String, T> extractor) {
135+
if (!property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) {
136+
return null;
137+
}
138+
String suffix = property.substring(OTEL_INSTRUMENTATION_PREFIX.length());
139+
// Split the remainder of the property on ".", and walk to the N-1 entry
140+
String[] segments = suffix.split("\\.");
141+
if (segments.length == 0) {
142+
return null;
143+
}
144+
StructuredConfigProperties target = instrumentationJavaNode;
145+
if (segments.length > 1) {
146+
for (int i = 0; i < segments.length - 1; i++) {
147+
target = target.getStructured(segments[i], empty());
148+
}
149+
}
150+
String lastPart = segments[segments.length - 1];
151+
return extractor.apply(target, lastPart);
152+
}
153+
}

0 commit comments

Comments
 (0)