Skip to content

Commit 92f18c1

Browse files
committed
The one true config bridge
1 parent d844002 commit 92f18c1

File tree

10 files changed

+537
-87
lines changed

10 files changed

+537
-87
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.config.bridge;
7+
8+
import io.opentelemetry.api.incubator.config.ConfigProvider;
9+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
10+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
11+
import javax.annotation.Nullable;
12+
13+
/**
14+
* A {@link ConfigProvider} implementation backed by {@link ConfigProperties}.
15+
*
16+
* <p>This allows instrumentations to always use {@code ExtendedOpenTelemetry.getConfigProvider()}
17+
* regardless of whether the user started with system properties or YAML.
18+
*/
19+
public final class ConfigPropertiesBackedConfigProvider implements ConfigProvider {
20+
21+
private final DeclarativeConfigProperties instrumentationConfig;
22+
23+
public static ConfigProvider create(ConfigProperties configProperties) {
24+
return new ConfigPropertiesBackedConfigProvider(configProperties);
25+
}
26+
27+
private ConfigPropertiesBackedConfigProvider(ConfigProperties configProperties) {
28+
this.instrumentationConfig =
29+
ConfigPropertiesBackedDeclarativeConfigProperties.createInstrumentationConfig(
30+
configProperties);
31+
}
32+
33+
@Nullable
34+
@Override
35+
public DeclarativeConfigProperties getInstrumentationConfig() {
36+
return instrumentationConfig;
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.config.bridge;
7+
8+
import static java.util.Collections.emptySet;
9+
10+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
11+
import io.opentelemetry.common.ComponentLoader;
12+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
13+
import java.util.ArrayList;
14+
import java.util.Collections;
15+
import java.util.HashMap;
16+
import java.util.List;
17+
import java.util.Map;
18+
import java.util.Set;
19+
import javax.annotation.Nullable;
20+
21+
/**
22+
* Implementation of {@link DeclarativeConfigProperties} backed by {@link ConfigProperties}.
23+
*
24+
* <p>It tracks the navigation path and only resolves to system properties at the leaf node when a
25+
* value is actually requested.
26+
*/
27+
public final class ConfigPropertiesBackedDeclarativeConfigProperties
28+
implements DeclarativeConfigProperties {
29+
30+
private static final String GENERAL_PEER_SERVICE_MAPPING = "general.peer.service_mapping";
31+
32+
private static final Map<String, String> LIST_MAPPINGS;
33+
34+
static {
35+
LIST_MAPPINGS = new HashMap<>();
36+
LIST_MAPPINGS.put(
37+
"general.http.client.request_captured_headers",
38+
"otel.instrumentation.http.client.capture-request-headers");
39+
LIST_MAPPINGS.put(
40+
"general.http.client.response_captured_headers",
41+
"otel.instrumentation.http.client.capture-response-headers");
42+
LIST_MAPPINGS.put(
43+
"general.http.server.request_captured_headers",
44+
"otel.instrumentation.http.server.capture-request-headers");
45+
LIST_MAPPINGS.put(
46+
"general.http.server.response_captured_headers",
47+
"otel.instrumentation.http.server.capture-response-headers");
48+
}
49+
50+
private final ConfigProperties configProperties;
51+
private final List<String> path;
52+
53+
public static DeclarativeConfigProperties createInstrumentationConfig(
54+
ConfigProperties configProperties) {
55+
return new ConfigPropertiesBackedDeclarativeConfigProperties(
56+
configProperties, Collections.emptyList());
57+
}
58+
59+
private ConfigPropertiesBackedDeclarativeConfigProperties(
60+
ConfigProperties configProperties, List<String> path) {
61+
this.configProperties = configProperties;
62+
this.path = path;
63+
}
64+
65+
@Nullable
66+
@Override
67+
public String getString(String name) {
68+
String fullPath = pathWithName(name);
69+
return configProperties.getString(toPropertyKey(fullPath));
70+
}
71+
72+
@Nullable
73+
@Override
74+
public Boolean getBoolean(String name) {
75+
String fullPath = pathWithName(name);
76+
return configProperties.getBoolean(toPropertyKey(fullPath));
77+
}
78+
79+
@Nullable
80+
@Override
81+
public Integer getInt(String name) {
82+
String fullPath = pathWithName(name);
83+
return configProperties.getInt(toPropertyKey(fullPath));
84+
}
85+
86+
@Nullable
87+
@Override
88+
public Long getLong(String name) {
89+
String fullPath = pathWithName(name);
90+
return configProperties.getLong(toPropertyKey(fullPath));
91+
}
92+
93+
@Nullable
94+
@Override
95+
public Double getDouble(String name) {
96+
String fullPath = pathWithName(name);
97+
return configProperties.getDouble(toPropertyKey(fullPath));
98+
}
99+
100+
/**
101+
* Important: this method should return null if there is no structured child with the given name,
102+
* but unfortunately that is not implementable on top of ConfigProperties.
103+
*
104+
* <p>This will be misleading if anyone is comparing the return value to null.
105+
*/
106+
@Override
107+
public DeclarativeConfigProperties getStructured(String name) {
108+
List<String> newPath = new ArrayList<>(path);
109+
newPath.add(name);
110+
return new ConfigPropertiesBackedDeclarativeConfigProperties(configProperties, newPath);
111+
}
112+
113+
@Nullable
114+
@Override
115+
@SuppressWarnings("unchecked") // Safe because T is known to be String via scalarType check
116+
public <T> List<T> getScalarList(String name, Class<T> scalarType) {
117+
if (scalarType != String.class) {
118+
return null;
119+
}
120+
String fullPath = pathWithName(name);
121+
122+
// Check explicit list mappings first
123+
String mappedKey = LIST_MAPPINGS.get(fullPath);
124+
if (mappedKey != null) {
125+
List<String> list = configProperties.getList(mappedKey);
126+
if (!list.isEmpty()) {
127+
return (List<T>) list;
128+
}
129+
return null;
130+
}
131+
132+
// Standard mapping
133+
List<String> list = configProperties.getList(toPropertyKey(fullPath));
134+
if (list.isEmpty()) {
135+
return null;
136+
}
137+
return (List<T>) list;
138+
}
139+
140+
@Nullable
141+
@Override
142+
public List<DeclarativeConfigProperties> getStructuredList(String name) {
143+
String fullPath = pathWithName(name);
144+
if (GENERAL_PEER_SERVICE_MAPPING.equals(fullPath)) {
145+
return PeerServiceMapping.getList(configProperties);
146+
}
147+
return null;
148+
}
149+
150+
@Override
151+
public Set<String> getPropertyKeys() {
152+
// this is not supported when using system properties based configuration
153+
return emptySet();
154+
}
155+
156+
@Override
157+
public ComponentLoader getComponentLoader() {
158+
return configProperties.getComponentLoader();
159+
}
160+
161+
private String pathWithName(String name) {
162+
if (path.isEmpty()) {
163+
return name;
164+
}
165+
return String.join(".", path) + "." + name;
166+
}
167+
168+
private static String toPropertyKey(String fullPath) {
169+
String translatedPath = translatePath(fullPath);
170+
171+
// Handle agent prefix: java.agent.* → otel.javaagent.*
172+
if (translatedPath.startsWith("agent.")) {
173+
return "otel.java" + translatedPath;
174+
}
175+
176+
// Handle jmx prefix: java.jmx.* → otel.jmx.*
177+
if (translatedPath.startsWith("jmx.")) {
178+
return "otel." + translatedPath;
179+
}
180+
181+
// Standard mapping
182+
return "otel.instrumentation." + translatedPath;
183+
}
184+
185+
private static String translatePath(String path) {
186+
StringBuilder result = new StringBuilder();
187+
for (String segment : path.split("\\.")) {
188+
// Skip "java" segment - it doesn't exist in system properties
189+
if ("java".equals(segment)) {
190+
continue;
191+
}
192+
if (result.length() > 0) {
193+
result.append(".");
194+
}
195+
result.append(translateName(segment));
196+
}
197+
return result.toString();
198+
}
199+
200+
private static String translateName(String name) {
201+
if (name.endsWith("/development")) {
202+
return "experimental."
203+
+ name.substring(0, name.length() - "/development".length()).replace('_', '-');
204+
}
205+
return name.replace('_', '-');
206+
}
207+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.config.bridge;
7+
8+
import static java.util.Collections.emptySet;
9+
10+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
11+
import io.opentelemetry.common.ComponentLoader;
12+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
13+
import java.util.ArrayList;
14+
import java.util.HashMap;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.Set;
18+
import javax.annotation.Nullable;
19+
20+
final class PeerServiceMapping implements DeclarativeConfigProperties {
21+
22+
private final Map<String, String> fields;
23+
private final ComponentLoader componentLoader;
24+
25+
@Nullable
26+
static List<DeclarativeConfigProperties> getList(ConfigProperties configProperties) {
27+
Map<String, String> map =
28+
configProperties.getMap("otel.instrumentation.common.peer-service-mapping");
29+
if (map.isEmpty()) {
30+
return null;
31+
}
32+
List<DeclarativeConfigProperties> result = new ArrayList<>();
33+
for (Map.Entry<String, String> entry : map.entrySet()) {
34+
Map<String, String> fields = new HashMap<>();
35+
fields.put("peer", entry.getKey());
36+
fields.put("service", entry.getValue());
37+
result.add(new PeerServiceMapping(fields, configProperties.getComponentLoader()));
38+
}
39+
return result;
40+
}
41+
42+
private PeerServiceMapping(Map<String, String> fields, ComponentLoader componentLoader) {
43+
this.fields = fields;
44+
this.componentLoader = componentLoader;
45+
}
46+
47+
@Nullable
48+
@Override
49+
public String getString(String name) {
50+
return fields.get(name);
51+
}
52+
53+
@Nullable
54+
@Override
55+
public Boolean getBoolean(String name) {
56+
return null;
57+
}
58+
59+
@Nullable
60+
@Override
61+
public Integer getInt(String name) {
62+
return null;
63+
}
64+
65+
@Nullable
66+
@Override
67+
public Long getLong(String name) {
68+
return null;
69+
}
70+
71+
@Nullable
72+
@Override
73+
public Double getDouble(String name) {
74+
return null;
75+
}
76+
77+
@Nullable
78+
@Override
79+
public DeclarativeConfigProperties getStructured(String name) {
80+
return null;
81+
}
82+
83+
@Nullable
84+
@Override
85+
public <T> List<T> getScalarList(String name, Class<T> scalarType) {
86+
return null;
87+
}
88+
89+
@Nullable
90+
@Override
91+
public List<DeclarativeConfigProperties> getStructuredList(String name) {
92+
return null;
93+
}
94+
95+
@Override
96+
public Set<String> getPropertyKeys() {
97+
// this is not supported when using system properties based configuration
98+
return emptySet();
99+
}
100+
101+
@Override
102+
public ComponentLoader getComponentLoader() {
103+
return componentLoader;
104+
}
105+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.config.internal;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.api.incubator.ExtendedOpenTelemetry;
10+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
11+
12+
/**
13+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
14+
* any time.
15+
*/
16+
public final class DeclarativeConfigUtil {
17+
18+
// this is a temporary convenience until getConfigProvider is available on OpenTelemetry
19+
public static DeclarativeConfigProperties getStructured(
20+
OpenTelemetry openTelemetry, String name, DeclarativeConfigProperties defaultValue) {
21+
22+
if (!(openTelemetry instanceof ExtendedOpenTelemetry)) {
23+
return defaultValue;
24+
}
25+
ExtendedOpenTelemetry extendedOpenTelemetry = (ExtendedOpenTelemetry) openTelemetry;
26+
DeclarativeConfigProperties instrumentationConfig =
27+
extendedOpenTelemetry.getConfigProvider().getInstrumentationConfig();
28+
if (instrumentationConfig == null) {
29+
return defaultValue;
30+
}
31+
return instrumentationConfig.getStructured(name, defaultValue);
32+
}
33+
34+
private DeclarativeConfigUtil() {}
35+
}

0 commit comments

Comments
 (0)