-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathConfigPropertiesBackedDeclarativeConfigProperties.java
More file actions
250 lines (220 loc) · 9 KB
/
Copy pathConfigPropertiesBackedDeclarativeConfigProperties.java
File metadata and controls
250 lines (220 loc) · 9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.config.bridge;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Implementation of {@link DeclarativeConfigProperties} backed by {@link ConfigProperties}.
*
* <p>It tracks the navigation path and only resolves to system properties at the leaf node when a
* value is actually requested.
*/
public final class ConfigPropertiesBackedDeclarativeConfigProperties
implements DeclarativeConfigProperties {
private static final String JAVA_COMMON_SERVICE_PEER_MAPPING = "java.common.service_peer_mapping";
private static final Map<String, String> SPECIAL_MAPPINGS;
static {
SPECIAL_MAPPINGS = new HashMap<>();
// mapping of general configs to old property names
SPECIAL_MAPPINGS.put(
"general.http.client.request_captured_headers",
"otel.instrumentation.http.client.capture-request-headers");
SPECIAL_MAPPINGS.put(
"general.http.client.response_captured_headers",
"otel.instrumentation.http.client.capture-response-headers");
SPECIAL_MAPPINGS.put(
"general.http.server.request_captured_headers",
"otel.instrumentation.http.server.capture-request-headers");
SPECIAL_MAPPINGS.put(
"general.http.server.response_captured_headers",
"otel.instrumentation.http.server.capture-response-headers");
SPECIAL_MAPPINGS.put(
"general.sanitization.url.sensitive_query_parameters/development",
"otel.instrumentation.sanitization.url.experimental.sensitive-query-parameters");
SPECIAL_MAPPINGS.put("general.semconv_stability.opt_in", "otel.semconv-stability.opt-in");
SPECIAL_MAPPINGS.put(
"general.semconv_exception.signal.preview", "otel.semconv.exception.signal.preview");
// moving common http, database, messaging, and gen_ai configs under common
SPECIAL_MAPPINGS.put(
"java.common.http.known_methods", "otel.instrumentation.http.known-methods");
SPECIAL_MAPPINGS.put(
"java.common.http.client.emit_experimental_telemetry/development",
"otel.instrumentation.http.client.emit-experimental-telemetry");
SPECIAL_MAPPINGS.put(
"java.common.http.server.emit_experimental_telemetry/development",
"otel.instrumentation.http.server.emit-experimental-telemetry");
SPECIAL_MAPPINGS.put(
"java.common.messaging.receive_telemetry/development.enabled",
"otel.instrumentation.messaging.experimental.receive-telemetry.enabled");
SPECIAL_MAPPINGS.put(
"java.common.messaging.capture_headers/development",
"otel.instrumentation.messaging.experimental.capture-headers");
SPECIAL_MAPPINGS.put(
"java.common.gen_ai.capture_message_content",
"otel.instrumentation.genai.capture-message-content");
// top-level common configs
SPECIAL_MAPPINGS.put(
"java.common.span_suppression_strategy/development",
"otel.instrumentation.experimental.span-suppression-strategy");
// renaming to match instrumentation module name
SPECIAL_MAPPINGS.put(
"java.opentelemetry_extension_annotations.exclude_methods",
"otel.instrumentation.opentelemetry-annotations.exclude-methods");
// renaming to avoid top level config
SPECIAL_MAPPINGS.put(
"java.servlet.javascript_snippet/development", "otel.experimental.javascript-snippet");
// jmx properties don't have an "instrumentation" segment
SPECIAL_MAPPINGS.put("java.jmx.enabled", "otel.jmx.enabled");
SPECIAL_MAPPINGS.put("java.jmx.config", "otel.jmx.config");
// otel.jmx.discovery.delay also has a dedicated branch in getLong() that reads it as a
// Duration and falls back to otel.metric.export.interval; this mapping is here only to keep
// it consistent with the rest of the jmx.* properties.
SPECIAL_MAPPINGS.put("java.jmx.discovery.delay", "otel.jmx.discovery.delay");
SPECIAL_MAPPINGS.put("java.jmx.target.system", "otel.jmx.target.system");
}
private final ConfigProperties configProperties;
private final List<String> path;
public static DeclarativeConfigProperties createInstrumentationConfig(
ConfigProperties configProperties) {
return new ConfigPropertiesBackedDeclarativeConfigProperties(configProperties, emptyList());
}
private ConfigPropertiesBackedDeclarativeConfigProperties(
ConfigProperties configProperties, List<String> path) {
this.configProperties = configProperties;
this.path = path;
}
@Nullable
@Override
public String getString(String name) {
return configProperties.getString(resolvePropertyKey(name));
}
@Nullable
@Override
public Boolean getBoolean(String name) {
return configProperties.getBoolean(resolvePropertyKey(name));
}
@Nullable
@Override
public Integer getInt(String name) {
return configProperties.getInt(resolvePropertyKey(name));
}
@Nullable
@Override
public Long getLong(String name) {
String fullPath = pathWithName(name);
if (fullPath.equals("java.jmx.discovery.delay")) {
Duration duration = configProperties.getDuration("otel.jmx.discovery.delay");
if (duration != null) {
return duration.toMillis();
}
// If discovery delay has not been configured, have a peek at the metric export interval.
// It makes sense for both of these values to be similar.
Duration fallback = configProperties.getDuration("otel.metric.export.interval");
if (fallback != null) {
return fallback.toMillis();
}
return null;
}
return configProperties.getLong(resolvePropertyKey(name));
}
@Nullable
@Override
public Double getDouble(String name) {
return configProperties.getDouble(resolvePropertyKey(name));
}
/**
* Important: this method should return null if there is no structured child with the given name,
* but unfortunately that is not implementable on top of ConfigProperties.
*
* <p>This will be misleading if anyone is comparing the return value to null.
*/
@Override
public DeclarativeConfigProperties getStructured(String name) {
List<String> newPath = new ArrayList<>(path);
newPath.add(name);
return new ConfigPropertiesBackedDeclarativeConfigProperties(configProperties, newPath);
}
@Nullable
@Override
@SuppressWarnings("unchecked") // Safe because T is known to be String via scalarType check
public <T> List<T> getScalarList(String name, Class<T> scalarType) {
if (scalarType != String.class) {
return null;
}
List<String> list = configProperties.getList(resolvePropertyKey(name));
if (list.isEmpty()) {
// returning null instead of empty list here has some implications,
// such as that there's no way to explicitly set an empty list using env var / sys props,
// e.g. for known_methods or sensitive_query_parameters,
// but it seems safer to return null and use the default value in these cases
return null;
}
return (List<T>) list;
}
@Nullable
@Override
public List<DeclarativeConfigProperties> getStructuredList(String name) {
String fullPath = pathWithName(name);
if (JAVA_COMMON_SERVICE_PEER_MAPPING.equals(fullPath)) {
return ServicePeerMapping.getList(configProperties);
}
return null;
}
@Override
public Set<String> getPropertyKeys() {
// this is not supported when using system properties based configuration
return emptySet();
}
@Override
public ComponentLoader getComponentLoader() {
return configProperties.getComponentLoader();
}
private String resolvePropertyKey(String name) {
String fullPath = pathWithName(name);
// Check explicit property mappings first
String mappedKey = SPECIAL_MAPPINGS.get(fullPath);
if (mappedKey != null) {
return mappedKey;
}
if (!fullPath.startsWith("java.")) {
return "";
}
// Remove "java." prefix and translate the remaining path
String[] segments = fullPath.substring(5).split("\\.");
StringBuilder translatedPath = new StringBuilder();
for (int i = 0; i < segments.length; i++) {
if (i > 0) {
translatedPath.append(".");
}
translatedPath.append(translateName(segments[i]));
}
return "otel.instrumentation." + translatedPath;
}
private String pathWithName(String name) {
if (path.isEmpty()) {
return name;
}
return String.join(".", path) + "." + name;
}
private static String translateName(String name) {
if (name.endsWith("/development")) {
name = name.substring(0, name.length() - "/development".length());
if (!name.contains("experimental")) {
name = "experimental." + name;
}
}
return name.replace('_', '-');
}
}