Skip to content

Commit f7cba3b

Browse files
zeitlingertrask
andauthored
fix spring declarative config with env var substitution (#15775)
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com> Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
1 parent f081e43 commit f7cba3b

8 files changed

Lines changed: 555 additions & 69 deletions

File tree

instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,8 @@ tasks {
302302

303303
// required on jdk17
304304
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
305+
// for @SetEnvironmentVariable
306+
jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
305307
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
306308
}
307309

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,20 @@ class EmbeddedConfigFile {
4646
private EmbeddedConfigFile() {}
4747

4848
static OpenTelemetryConfigurationModel extractModel(ConfigurableEnvironment environment) {
49-
Map<String, Object> props = extractSpringProperties(environment);
50-
Map<String, Object> nested = convertFlatPropsToNested(props);
51-
return MAPPER.convertValue(nested, OpenTelemetryConfigurationModel.class);
49+
Map<String, String> props = extractSpringProperties(environment);
50+
return convertToOpenTelemetryConfigurationModel(props);
5251
}
5352

54-
private static Map<String, Object> extractSpringProperties(ConfigurableEnvironment environment) {
53+
private static Map<String, String> extractSpringProperties(ConfigurableEnvironment environment) {
5554
MutablePropertySources propertySources = environment.getPropertySources();
5655

57-
Map<String, Object> props = new HashMap<>();
56+
Map<String, String> props = new HashMap<>();
5857
for (PropertySource<?> propertySource : propertySources) {
5958
if (propertySource instanceof EnumerablePropertySource<?>) {
6059
for (String propertyName :
6160
((EnumerablePropertySource<?>) propertySource).getPropertyNames()) {
6261
if (propertyName.startsWith("otel.")) {
63-
Object property = propertySource.getProperty(propertyName);
64-
// Resolve ${} placeholders in String values while preserving types for others
65-
if (property instanceof String) {
66-
property = environment.resolvePlaceholders((String) property);
67-
}
62+
String property = environment.getProperty(propertyName);
6863
if (Objects.equals(property, "")) {
6964
property = null; // spring returns empty string for yaml null
7065
}
@@ -100,18 +95,29 @@ private static Map<String, Object> extractSpringProperties(ConfigurableEnvironme
10095
return props;
10196
}
10297

98+
static OpenTelemetryConfigurationModel convertToOpenTelemetryConfigurationModel(
99+
Map<String, String> flatProps) {
100+
Map<String, Object> nested = convertFlatPropsToNested(flatProps);
101+
102+
return MAPPER.convertValue(nested, OpenTelemetryConfigurationModel.class);
103+
}
104+
105+
static ObjectMapper getObjectMapper() {
106+
return MAPPER;
107+
}
108+
103109
/**
104110
* Convert flat property map to nested structure. e.g. "otel.instrumentation.java.list[0]" = "one"
105111
* and "otel.instrumentation.java.list[1]" = "two" becomes: {otel: {instrumentation: {java: {list:
106112
* ["one", "two"]}}}}
107113
*/
108114
@SuppressWarnings("unchecked")
109-
static Map<String, Object> convertFlatPropsToNested(Map<String, Object> flatProps) {
115+
static Map<String, Object> convertFlatPropsToNested(Map<String, String> flatProps) {
110116
Map<String, Object> result = new HashMap<>();
111117

112-
for (Map.Entry<String, Object> entry : flatProps.entrySet()) {
118+
for (Map.Entry<String, String> entry : flatProps.entrySet()) {
113119
String key = entry.getKey();
114-
Object value = entry.getValue();
120+
String value = entry.getValue();
115121

116122
// Split the key by dots
117123
String[] parts = key.split("\\.");

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import static java.util.Collections.emptyMap;
99
import static java.util.Objects.requireNonNull;
10-
import static java.util.stream.Collectors.toList;
1110

1211
import io.opentelemetry.api.OpenTelemetry;
1312
import io.opentelemetry.api.incubator.ExtendedOpenTelemetry;
@@ -45,6 +44,7 @@
4544
import java.util.Optional;
4645
import java.util.function.Function;
4746
import java.util.function.Supplier;
47+
import java.util.stream.Collectors;
4848
import org.slf4j.Logger;
4949
import org.slf4j.LoggerFactory;
5050
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -164,12 +164,12 @@ public OpenTelemetryConfigurationModel openTelemetryConfigurationModel(
164164
@Bean
165165
public OpenTelemetry openTelemetry(
166166
OpenTelemetryConfigurationModel model, ApplicationContext applicationContext) {
167-
OpenTelemetrySdk sdk =
168-
DeclarativeConfiguration.create(
169-
model, new OpenTelemetrySdkComponentLoader(applicationContext));
167+
OpenTelemetrySdkComponentLoader componentLoader =
168+
new OpenTelemetrySdkComponentLoader(applicationContext);
169+
OpenTelemetrySdk sdk = DeclarativeConfiguration.create(model, componentLoader);
170170
Runtime.getRuntime().addShutdownHook(new Thread(sdk::close));
171171
logStart();
172-
return sdk;
172+
return new SpringOpenTelemetrySdk(sdk, SpringConfigProvider.create(model, componentLoader));
173173
}
174174

175175
/**
@@ -272,7 +272,7 @@ public OpenTelemetrySdkComponentLoader(ApplicationContext applicationContext) {
272272
public <T> Iterable<T> load(Class<T> spiClass) {
273273
List<T> spi = spiHelper.load(spiClass);
274274
List<T> beans =
275-
applicationContext.getBeanProvider(spiClass).orderedStream().collect(toList());
275+
applicationContext.getBeanProvider(spiClass).orderedStream().collect(Collectors.toList());
276276
spi.addAll(beans);
277277
return spi;
278278
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.autoconfigure;
7+
8+
import static java.util.Collections.emptyMap;
9+
10+
import com.fasterxml.jackson.core.type.TypeReference;
11+
import io.opentelemetry.api.incubator.config.ConfigProvider;
12+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
13+
import io.opentelemetry.common.ComponentLoader;
14+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
15+
import java.util.Map;
16+
17+
/**
18+
* Spring flavor of {@link io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider}
19+
* that tries to coerce types, because spring doesn't tell what the original type was.
20+
*
21+
* <p>The entire class is a copy of <a
22+
* href="https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java">SdkConfigProvider</a>
23+
* which uses {@link SpringDeclarativeConfigProperties} instead of {@link
24+
* io.opentelemetry.sdk.extension.incubator.fileconfig.YamlDeclarativeConfigProperties}.
25+
*/
26+
final class SpringConfigProvider implements ConfigProvider {
27+
28+
private final DeclarativeConfigProperties instrumentationConfig;
29+
30+
private SpringConfigProvider(
31+
OpenTelemetryConfigurationModel model, ComponentLoader componentLoader) {
32+
DeclarativeConfigProperties configProperties = toConfigProperties(model, componentLoader);
33+
this.instrumentationConfig = configProperties.get("instrumentation/development");
34+
}
35+
36+
private static DeclarativeConfigProperties toConfigProperties(
37+
OpenTelemetryConfigurationModel model, ComponentLoader componentLoader) {
38+
Map<String, Object> configurationMap =
39+
EmbeddedConfigFile.getObjectMapper()
40+
.convertValue(model, new TypeReference<Map<String, Object>>() {});
41+
if (configurationMap == null) {
42+
configurationMap = emptyMap();
43+
}
44+
return SpringDeclarativeConfigProperties.create(configurationMap, componentLoader);
45+
}
46+
47+
/**
48+
* Create a {@link SpringConfigProvider} from the {@code model}.
49+
*
50+
* @param model the configuration model
51+
* @param componentLoader the component loader
52+
* @return the {@link SpringConfigProvider}
53+
*/
54+
static SpringConfigProvider create(
55+
OpenTelemetryConfigurationModel model, ComponentLoader componentLoader) {
56+
return new SpringConfigProvider(model, componentLoader);
57+
}
58+
59+
@Override
60+
public DeclarativeConfigProperties getInstrumentationConfig() {
61+
return instrumentationConfig;
62+
}
63+
64+
@Override
65+
public String toString() {
66+
return "SpringConfigProvider{instrumentationConfig=" + instrumentationConfig + '}';
67+
}
68+
}

0 commit comments

Comments
 (0)