Skip to content

Spring starter declarative config #14062

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 41 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
745a45f
fix NullPointerException with declarative config
zeitlinger Aug 15, 2025
8f2c268
move properties bridge to separate project
zeitlinger Jun 18, 2025
18568d8
declarative config
zeitlinger Jun 18, 2025
1809d38
isolate map converter
zeitlinger Jun 18, 2025
899a4c7
embedded config file
zeitlinger Jun 18, 2025
bfd9db0
embedded config file
zeitlinger Jun 18, 2025
51ace0c
config bridge
zeitlinger Jun 18, 2025
7c87bf1
config bridge
zeitlinger Jun 18, 2025
cdad174
use InstrumentationConfig for encapsulation
zeitlinger Jun 18, 2025
ead22ff
use InstrumentationConfig for encapsulation
zeitlinger Jun 18, 2025
5366437
use InstrumentationConfig for encapsulation
zeitlinger Jun 18, 2025
7a528e0
use InstrumentationConfig for encapsulation
zeitlinger Jun 18, 2025
6a8c6bd
create component loader directly (doesn't work otherwise)
zeitlinger Jun 19, 2025
07aa912
fix disabled config
zeitlinger Jun 19, 2025
8488268
add test
zeitlinger Jun 19, 2025
e72c6d6
add test
zeitlinger Jun 19, 2025
7713881
add test
zeitlinger Jun 19, 2025
eb33779
add test
zeitlinger Jun 19, 2025
f3d471c
add test
zeitlinger Jun 19, 2025
ed79818
add test
zeitlinger Jun 19, 2025
ac4cf0a
add test
zeitlinger Jun 19, 2025
9a71bbd
add test
zeitlinger Jun 19, 2025
4a534cc
add test
zeitlinger Jun 19, 2025
3a07917
add test
zeitlinger Jun 19, 2025
aa5592b
cleanup
zeitlinger Jun 19, 2025
11d397e
cleanup
zeitlinger Jun 19, 2025
a13beb4
format
zeitlinger Jun 19, 2025
e5036ca
fix classpath
zeitlinger Jun 20, 2025
17efe9e
fix classpath
zeitlinger Jun 20, 2025
de594ba
fix classpath
zeitlinger Jun 20, 2025
fa55c9b
fix rebase
zeitlinger Jun 30, 2025
3fa79c8
fix rebase
zeitlinger Jun 30, 2025
b3207ab
./gradlew spotlessApply
otelbot[bot] Jun 30, 2025
c4f4c72
fix rebase
zeitlinger Jul 7, 2025
abdba31
extract condition
zeitlinger Jul 7, 2025
0f6d460
fix rebase
zeitlinger Jul 7, 2025
376ee7f
use _
zeitlinger Jul 8, 2025
81398c4
use declarative config properties
zeitlinger Jul 8, 2025
6f45f71
use declarative config properties
zeitlinger Jul 8, 2025
8a5fb10
fix rebase
zeitlinger Aug 15, 2025
a6b132c
./gradlew spotlessApply
otelbot[bot] Aug 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .fossa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ targets:
- type: gradle
path: ./
target: ':sdk-autoconfigure-support'
- type: gradle
path: ./
target: ':sdk-bridge'
- type: gradle
path: ./
target: ':testing-common'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.19.0-SNAPSHOT.jar against opentelemetry-spring-boot-autoconfigure-2.18.1.jar
No changes.
=== UNCHANGED CLASS: PUBLIC io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
--- REMOVED ANNOTATION: org.springframework.boot.context.properties.EnableConfigurationProperties
--- REMOVED ELEMENT: value=io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtlpExporterProperties,io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelResourceProperties,io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelSpringProperties (-)
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package io.opentelemetry.instrumentation.api.incubator.config.internal;

import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;

import io.opentelemetry.api.incubator.config.ConfigProvider;
Expand Down Expand Up @@ -46,28 +47,33 @@ public CommonConfig(InstrumentationConfig config) {
getFromConfigProviderOrFallback(
config,
InstrumentationConfigUtil::peerServiceMapping,
emptyMap(),
() ->
config.getMap("otel.instrumentation.common.peer-service-mapping", emptyMap())));

clientRequestHeaders =
getFromConfigProviderOrFallback(
config,
InstrumentationConfigUtil::httpClientRequestCapturedHeaders,
emptyList(),
() -> config.getList("otel.instrumentation.http.client.capture-request-headers"));
clientResponseHeaders =
getFromConfigProviderOrFallback(
config,
InstrumentationConfigUtil::httpClientResponseCapturedHeaders,
emptyList(),
() -> config.getList("otel.instrumentation.http.client.capture-response-headers"));
serverRequestHeaders =
getFromConfigProviderOrFallback(
config,
InstrumentationConfigUtil::httpServerRequestCapturedHeaders,
emptyList(),
() -> config.getList("otel.instrumentation.http.server.capture-request-headers"));
serverResponseHeaders =
getFromConfigProviderOrFallback(
config,
InstrumentationConfigUtil::httpServerResponseCapturedHeaders,
emptyList(),
() -> config.getList("otel.instrumentation.http.server.capture-response-headers"));
knownHttpRequestMethods =
new HashSet<>(
Expand Down Expand Up @@ -154,11 +160,14 @@ public String getTraceFlagsKey() {
private static <T> T getFromConfigProviderOrFallback(
InstrumentationConfig config,
Function<ConfigProvider, T> getFromConfigProvider,
T defaultValue,
Supplier<T> fallback) {
ConfigProvider configProvider = config.getConfigProvider();
if (configProvider != null) {
return getFromConfigProvider.apply(configProvider);
T value = getFromConfigProvider.apply(configProvider);
return value != null ? value : defaultValue;
}
// fallback doesn't return null, so we can safely call it
return fallback.get();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ dependencies {
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor:$springBootVersion")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor:$springBootVersion")
implementation("javax.validation:validation-api")
// snake yaml is already used by "spring-boot-resources"
// and less likely to cause problems compared to jackson
implementation("org.snakeyaml:snakeyaml-engine")

implementation(project(":instrumentation-annotations-support"))
implementation(project(":instrumentation:kafka:kafka-clients:kafka-clients-2.6:library"))
Expand Down Expand Up @@ -66,7 +69,9 @@ dependencies {
library("org.springframework.boot:spring-boot-starter-data-jdbc:$springBootVersion")

implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
implementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")
implementation(project(":sdk-autoconfigure-support"))
implementation(project(":sdk-bridge"))
compileOnly("io.opentelemetry:opentelemetry-extension-trace-propagators")
compileOnly("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator")
compileOnly("io.opentelemetry:opentelemetry-exporter-logging")
Expand Down Expand Up @@ -174,6 +179,16 @@ testing {
}
}
}

val testDeclarativeConfig by registering(JvmTestSuite::class) {
dependencies {
implementation(project())
implementation("io.opentelemetry:opentelemetry-sdk")
implementation("org.springframework.boot:spring-boot-starter-test:$springBootVersion") {
exclude("org.junit.vintage", "junit-vintage-engine")
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure;

import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.snakeyaml.engine.v2.api.Dump;
import org.snakeyaml.engine.v2.api.DumpSettings;
import org.snakeyaml.engine.v2.api.Load;
import org.snakeyaml.engine.v2.api.LoadSettings;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;

class EmbeddedConfigFile {

private EmbeddedConfigFile() {
// Utility class
}

private static final Pattern PATTERN =
Pattern.compile(
"^Config resource 'class path resource \\[(.+)]' via location 'optional:classpath:/'$");

static OpenTelemetryConfigurationModel extractModel(ConfigurableEnvironment environment)
throws IOException {
for (PropertySource<?> propertySource : environment.getPropertySources()) {
if (propertySource instanceof OriginTrackedMapPropertySource) {
return getModel(environment, (OriginTrackedMapPropertySource) propertySource);
}
}
throw new IllegalStateException("No application.yaml file found.");
}

private static OpenTelemetryConfigurationModel getModel(
ConfigurableEnvironment environment, OriginTrackedMapPropertySource source)
throws IOException {
Matcher matcher = PATTERN.matcher(source.getName());
if (matcher.matches()) {
String file = matcher.group(1);

try (InputStream resourceAsStream =
environment.getClass().getClassLoader().getResourceAsStream(file)) {
if (resourceAsStream != null) {
return extractOtelConfigFile(resourceAsStream);
} else {
throw new IllegalStateException("Unable to load " + file + " in the classpath.");
}
}
} else {
throw new IllegalStateException(
"No OpenTelemetry configuration found in the application.yaml file.");
}
}

@Nullable
@SuppressWarnings("unchecked")
private static String parseOtelNode(InputStream in) {
Load load = new Load(LoadSettings.builder().build());
Dump dump = new Dump(DumpSettings.builder().build());
for (Object o : load.loadAllFromInputStream(in)) {
Map<String, Object> data = (Map<String, Object>) o;
Map<String, Map<String, Object>> otel = (Map<String, Map<String, Object>>) data.get("otel");
if (otel != null) {
return dump.dumpToString(otel);
}
}
throw new IllegalStateException("No 'otel' configuration found in the YAML file.");
}

private static OpenTelemetryConfigurationModel extractOtelConfigFile(InputStream content) {
String node = parseOtelNode(content);
if (node == null || node.isEmpty()) {
throw new IllegalStateException("otel node is empty or null in the YAML file.");
}

return DeclarativeConfiguration.parse(
new ByteArrayInputStream(node.getBytes(StandardCharsets.UTF_8)));
}
}
Loading
Loading