Skip to content

Commit 8d60820

Browse files
committed
copy declarative config from agent
1 parent 364fc32 commit 8d60820

File tree

10 files changed

+580
-7
lines changed

10 files changed

+580
-7
lines changed

baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageProcessorCustomizerTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,9 @@ private static OpenTelemetrySdk getOpenTelemetrySdk(
103103
// We set the export interval of the spans to 10 ms. The default value is 5
104104
// seconds.
105105
"otel.bsp.schedule.delay", // span exporter
106-
"100",
106+
"10",
107107
"otel.blrp.schedule.delay", // log exporter
108-
"100",
108+
"10",
109109
"otel.traces.exporter",
110110
MEMORY_EXPORTER,
111111
"otel.metrics.exporter",
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
plugins {
2+
id("otel.java-conventions")
3+
id("otel.publish-conventions")
4+
}
5+
6+
description = "OpenTelemetry extension that provides a bridge for declarative configuration."
7+
otelJava.moduleName.set("io.opentelemetry.contrib.sdk.declarative.config.bridge")
8+
9+
dependencies {
10+
// We use `compileOnly` dependency because during runtime all necessary classes are provided by
11+
// javaagent itself.
12+
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
13+
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator")
14+
15+
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
16+
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")
17+
testImplementation("org.mockito:mockito-inline")
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.sdk.autoconfigure;
7+
8+
import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty;
9+
10+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
11+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
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.Objects;
18+
import java.util.function.BiFunction;
19+
import javax.annotation.Nullable;
20+
21+
/**
22+
* A {@link ConfigProperties} which resolves properties based on {@link
23+
* DeclarativeConfigProperties}.
24+
*
25+
* <p>Only properties starting with "otel.instrumentation." are resolved. Others return null (or
26+
* default value if provided).
27+
*
28+
* <p>To resolve:
29+
*
30+
* <ul>
31+
* <li>"otel.instrumentation" refers to the ".instrumentation.java" node
32+
* <li>The portion of the property after "otel.instrumentation." is split into segments based on
33+
* ".".
34+
* <li>For each N-1 segment, we walk down the tree to find the relevant leaf {@link
35+
* DeclarativeConfigProperties}.
36+
* <li>We extract the property from the resolved {@link DeclarativeConfigProperties} using the
37+
* last segment as the property key.
38+
* </ul>
39+
*
40+
* <p>For example, given the following YAML, asking for {@code
41+
* ConfigProperties#getString("otel.instrumentation.common.string_key")} yields "value":
42+
*
43+
* <pre>
44+
* instrumentation:
45+
* java:
46+
* common:
47+
* string_key: value
48+
* </pre>
49+
*/
50+
final class DeclarativeConfigPropertiesBridge implements ConfigProperties {
51+
52+
private static final String OTEL_INSTRUMENTATION_PREFIX = "otel.instrumentation.";
53+
54+
private final DeclarativeConfigProperties baseNode;
55+
56+
// lookup order matters - we choose the first match
57+
private final Map<String, String> mappings;
58+
private final Map<String, Object> overrideValues;
59+
60+
DeclarativeConfigPropertiesBridge(
61+
DeclarativeConfigProperties baseNode,
62+
Map<String, String> mappings,
63+
Map<String, Object> overrideValues) {
64+
this.baseNode = Objects.requireNonNull(baseNode);
65+
this.mappings = mappings;
66+
this.overrideValues = overrideValues;
67+
}
68+
69+
@Nullable
70+
@Override
71+
public String getString(String propertyName) {
72+
return getPropertyValue(propertyName, String.class, DeclarativeConfigProperties::getString);
73+
}
74+
75+
@Nullable
76+
@Override
77+
public Boolean getBoolean(String propertyName) {
78+
return getPropertyValue(propertyName, Boolean.class, DeclarativeConfigProperties::getBoolean);
79+
}
80+
81+
@Nullable
82+
@Override
83+
public Integer getInt(String propertyName) {
84+
return getPropertyValue(propertyName, Integer.class, DeclarativeConfigProperties::getInt);
85+
}
86+
87+
@Nullable
88+
@Override
89+
public Long getLong(String propertyName) {
90+
return getPropertyValue(propertyName, Long.class, DeclarativeConfigProperties::getLong);
91+
}
92+
93+
@Nullable
94+
@Override
95+
public Double getDouble(String propertyName) {
96+
return getPropertyValue(propertyName, Double.class, DeclarativeConfigProperties::getDouble);
97+
}
98+
99+
@Nullable
100+
@Override
101+
public Duration getDuration(String propertyName) {
102+
Long millis = getPropertyValue(propertyName, Long.class, DeclarativeConfigProperties::getLong);
103+
if (millis == null) {
104+
return null;
105+
}
106+
return Duration.ofMillis(millis);
107+
}
108+
109+
@SuppressWarnings("unchecked")
110+
@Override
111+
public List<String> getList(String propertyName) {
112+
List<String> propertyValue =
113+
getPropertyValue(
114+
propertyName,
115+
List.class,
116+
(properties, lastPart) -> properties.getScalarList(lastPart, String.class));
117+
return propertyValue == null ? Collections.emptyList() : propertyValue;
118+
}
119+
120+
@SuppressWarnings("unchecked")
121+
@Override
122+
public Map<String, String> getMap(String propertyName) {
123+
DeclarativeConfigProperties propertyValue =
124+
getPropertyValue(
125+
propertyName,
126+
DeclarativeConfigProperties.class,
127+
DeclarativeConfigProperties::getStructured);
128+
if (propertyValue == null) {
129+
return Collections.emptyMap();
130+
}
131+
Map<String, String> result = new HashMap<>();
132+
propertyValue
133+
.getPropertyKeys()
134+
.forEach(
135+
key -> {
136+
String value = propertyValue.getString(key);
137+
if (value == null) {
138+
return;
139+
}
140+
result.put(key, value);
141+
});
142+
return Collections.unmodifiableMap(result);
143+
}
144+
145+
@Nullable
146+
private <T> T getPropertyValue(
147+
String property,
148+
Class<T> clazz,
149+
BiFunction<DeclarativeConfigProperties, String, T> extractor) {
150+
T override = clazz.cast(overrideValues.get(property));
151+
if (override != null) {
152+
return override;
153+
}
154+
155+
String[] segments = getSegments(translateProperty(property));
156+
if (segments.length == 0) {
157+
return null;
158+
}
159+
160+
// Extract the value by walking to the N-1 entry
161+
DeclarativeConfigProperties target = baseNode;
162+
if (segments.length > 1) {
163+
for (int i = 0; i < segments.length - 1; i++) {
164+
target = target.getStructured(segments[i], empty());
165+
}
166+
}
167+
String lastPart = segments[segments.length - 1];
168+
169+
return extractor.apply(target, lastPart);
170+
}
171+
172+
static String[] getSegments(String property) {
173+
if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) {
174+
property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length());
175+
}
176+
// Split the remainder of the property on "."
177+
return property.replace('-', '_').split("\\.");
178+
}
179+
180+
private String translateProperty(String property) {
181+
for (Map.Entry<String, String> entry : mappings.entrySet()) {
182+
if (property.startsWith(entry.getKey())) {
183+
return entry.getValue() + property.substring(entry.getKey().length());
184+
}
185+
}
186+
return property;
187+
}
188+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.sdk.autoconfigure;
7+
8+
import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty;
9+
10+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
11+
import io.opentelemetry.api.incubator.config.ConfigProvider;
12+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
13+
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
14+
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
15+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
16+
import java.util.HashMap;
17+
import java.util.LinkedHashMap;
18+
import java.util.Map;
19+
import javax.annotation.Nullable;
20+
21+
/**
22+
* A builder for {@link DeclarativeConfigPropertiesBridge} that allows adding translations and fixed
23+
* values for properties.
24+
*/
25+
public class DeclarativeConfigPropertiesBridgeBuilder {
26+
/**
27+
* order is important here, so we use LinkedHashMap - see {@link #addMapping(String, String)} for
28+
* more details
29+
*/
30+
private final Map<String, String> mappings = new LinkedHashMap<>();
31+
32+
private final Map<String, Object> overrideValues = new HashMap<>();
33+
34+
public DeclarativeConfigPropertiesBridgeBuilder() {}
35+
36+
/**
37+
* Adds a mapping from a property prefix to a YAML path.
38+
*
39+
* <p>For example, if the property prefix is "otel.javaagent" and the YAML path is "agent", then
40+
* any property starting with "otel.javaagent." will be resolved against the "agent" node in the
41+
* instrumentation/java section of the YAML configuration.
42+
*
43+
* @param propertyPrefix the prefix of the property to translate
44+
* @param yamlPath the YAML path to resolve the property against
45+
*/
46+
@CanIgnoreReturnValue
47+
public DeclarativeConfigPropertiesBridgeBuilder addMapping(
48+
String propertyPrefix, String yamlPath) {
49+
mappings.put(propertyPrefix, yamlPath);
50+
return this;
51+
}
52+
53+
/**
54+
* Adds a fixed override value for a property.
55+
*
56+
* @param propertyName the name of the property to override
57+
* @param value the value to return when the property is requested
58+
*/
59+
@CanIgnoreReturnValue
60+
public DeclarativeConfigPropertiesBridgeBuilder addOverride(String propertyName, Object value) {
61+
overrideValues.put(propertyName, value);
62+
return this;
63+
}
64+
65+
/** Build {@link ConfigProperties} from the {@code autoConfiguredOpenTelemetrySdk}. */
66+
public ConfigProperties build(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
67+
ConfigProperties sdkConfigProperties =
68+
AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk);
69+
if (sdkConfigProperties != null) {
70+
return sdkConfigProperties;
71+
}
72+
ConfigProvider configProvider =
73+
AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk);
74+
if (configProvider != null) {
75+
return buildFromInstrumentationConfig(configProvider.getInstrumentationConfig());
76+
}
77+
// Should never happen
78+
throw new IllegalStateException(
79+
"AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or DeclarativeConfigProperties. This is likely a programming error in opentelemetry-java");
80+
}
81+
82+
/**
83+
* Build {@link DeclarativeConfigPropertiesBridge} from the {@link DeclarativeConfigProperties}.
84+
*
85+
* @param node the declarative config properties to build from
86+
* @return a new instance of {@link ConfigProperties}
87+
*/
88+
public ConfigProperties build(@Nullable DeclarativeConfigProperties node) {
89+
return new DeclarativeConfigPropertiesBridge(
90+
node == null ? empty() : node, mappings, overrideValues);
91+
}
92+
93+
/**
94+
* Build {@link ConfigProperties} from the {@link DeclarativeConfigProperties}.
95+
*
96+
* @param instrumentationConfig the instrumentation configuration to build from
97+
* @return a new instance of {@link ConfigProperties}
98+
*/
99+
public ConfigProperties buildFromInstrumentationConfig(
100+
@Nullable DeclarativeConfigProperties instrumentationConfig) {
101+
return build(
102+
instrumentationConfig == null ? null : instrumentationConfig.getStructured("java"));
103+
}
104+
}

0 commit comments

Comments
 (0)