Skip to content
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
5b1236d
autoconfigure and smoke
jaydeluca Nov 25, 2025
38d6dc4
progress
jaydeluca Nov 25, 2025
ba32a8d
kafka
jaydeluca Nov 26, 2025
6c13ecb
kafka working
jaydeluca Nov 26, 2025
330cd89
most things working
jaydeluca Nov 26, 2025
f100658
fix more tests, exclude tests from latestdeps
jaydeluca Nov 26, 2025
9c6f987
micrometer fix
jaydeluca Nov 26, 2025
79629d2
add kafka test
jaydeluca Nov 26, 2025
30d0e2c
native and another micrometer issue
jaydeluca Nov 26, 2025
be14d94
fix codeql
jaydeluca Nov 26, 2025
6757a63
fix test on java 25
jaydeluca Nov 27, 2025
25b980b
Merge branch 'main' into spring-boot-4-autoconfigure
jaydeluca Nov 27, 2025
6562745
ignore transient kafka startup errors
jaydeluca Nov 27, 2025
7e75966
Merge branch 'spring-boot-4-autoconfigure' of github.com:jaydeluca/op…
jaydeluca Nov 27, 2025
f37f691
Merge branch 'main' into spring-boot-4-autoconfigure
jaydeluca Nov 27, 2025
a37c8a5
register both mongo configs
jaydeluca Nov 27, 2025
a406382
more smoke test changes
jaydeluca Nov 27, 2025
3113936
test fix
jaydeluca Nov 27, 2025
f86e06e
missing mongo
jaydeluca Nov 27, 2025
bc3519d
avoid using common to fix dependency issues
jaydeluca Nov 27, 2025
fcf6bce
add propagators and app file
jaydeluca Nov 27, 2025
cbc8b1e
only run native tests on java 25+
jaydeluca Nov 28, 2025
1a83d20
cleanup
jaydeluca Nov 28, 2025
c2d5230
cleanup
jaydeluca Nov 28, 2025
9ce1a80
Merge branch 'main' into spring-boot-4-autoconfigure
jaydeluca Nov 28, 2025
d17fed1
fix comment line breaks
jaydeluca Nov 28, 2025
36c991a
switch to using conditionalOnClass
jaydeluca Nov 29, 2025
10fef8c
abstract incompatible class
jaydeluca Nov 29, 2025
103cecb
start refactoring common test module
jaydeluca Dec 2, 2025
153195e
finish common testing module
jaydeluca Dec 2, 2025
e6a8ae4
create spring 2 suite to avoid exlusions
jaydeluca Dec 2, 2025
7ac8f91
refactor smoke tests to avoid duplication for kafka and mongo, jfr as…
jaydeluca Dec 2, 2025
59994b7
further reduce test duplication
jaydeluca Dec 2, 2025
c7d5de8
fix
jaydeluca Dec 2, 2025
0a5806f
add dependency
jaydeluca Dec 3, 2025
c8516d7
japi cmp
jaydeluca Dec 3, 2025
d88ceef
fix kafka separation of configs
jaydeluca Dec 3, 2025
e375143
Merge branch 'main' into spring-boot-4-autoconfigure
jaydeluca Dec 3, 2025
8f75f72
revert jApiCmd
jaydeluca Dec 3, 2025
fc726fe
fix conditionals
jaydeluca Dec 3, 2025
0c6e263
extending wrong test classes
jaydeluca Dec 4, 2025
088b920
Merge branch 'main' into spring-boot-4-autoconfigure
laurit Dec 5, 2025
3b18b94
fix jApiCmp
laurit Dec 5, 2025
24aac9a
update fossa configuration
laurit Dec 5, 2025
879cdde
review
laurit Dec 5, 2025
41879d3
fix graal tests
jaydeluca Dec 5, 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
1 change: 1 addition & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ jobs:
./gradlew assemble -x javadoc
-x :smoke-tests-otel-starter:spring-boot-3:collectReachabilityMetadata
-x :smoke-tests-otel-starter:spring-boot-3.2:collectReachabilityMetadata
-x :smoke-tests-otel-starter:spring-boot-4:collectReachabilityMetadata
-x :smoke-tests-otel-starter:spring-boot-reactive-3:collectReachabilityMetadata
--no-build-cache --no-daemon

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,20 @@ sourceSets {
setSrcDirs(listOf("src/main/javaSpring3"))
}
}
create("javaSpring4") {
java {
setSrcDirs(listOf("src/main/javaSpring4"))
}
}
}

configurations {
named("javaSpring3CompileOnly") {
extendsFrom(configurations["compileOnly"])
}
named("javaSpring4CompileOnly") {
extendsFrom(configurations["compileOnly"])
}
}

dependencies {
Expand Down Expand Up @@ -100,6 +108,8 @@ dependencies {
testImplementation("io.opentelemetry:opentelemetry-exporter-zipkin")
testImplementation(project(":instrumentation-annotations"))

latestDepTestLibrary("org.springframework.boot:spring-boot-starter-micrometer-metrics:latest.release")

// needed for the Spring Boot 3 support
implementation(project(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:library"))

Expand All @@ -110,15 +120,19 @@ dependencies {
add("javaSpring3CompileOnly", project(":instrumentation:spring:spring-web:spring-web-3.1:library"))
add("javaSpring3CompileOnly", project(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:library"))

// tests don't work with spring boot 4 yet
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-test:3.+") // documented limitation
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-actuator:3.+") // documented limitation
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-aop:3.+") // documented limitation
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-web:3.+") // documented limitation
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-webflux:3.+") // documented limitation
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-data-mongodb:3.+") // documented limitation
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-data-r2dbc:3.+") // documented limitation
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-data-jdbc:3.+") // documented limitation
// Spring Boot 4
add("javaSpring4CompileOnly", files(sourceSets.main.get().output.classesDirs))
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-starter-kafka:4.0.0")
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-autoconfigure:4.0.0")
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-jdbc:4.0.0")
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-starter-jdbc:4.0.0")
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-restclient:4.0.0")
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-starter-data-mongodb:4.0.0")
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.0")
add("javaSpring4CompileOnly", project(":instrumentation:kafka:kafka-clients:kafka-clients-2.6:library"))
add("javaSpring4CompileOnly", project(":instrumentation:spring:spring-kafka-2.7:library"))
add("javaSpring4CompileOnly", project(":instrumentation:mongo:mongo-3.1:library"))
add("javaSpring4CompileOnly", project(":instrumentation:micrometer:micrometer-1.5:library"))
}

val latestDepTest = findProperty("testLatestDeps") as Boolean
Expand Down Expand Up @@ -186,6 +200,28 @@ testing {
}
}

val testSpring4 by registering(JvmTestSuite::class) {
dependencies {
implementation(project())
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
implementation("org.springframework.boot:spring-boot-starter-jdbc:4.0.0")
implementation("org.springframework.boot:spring-boot-restclient:4.0.0")
implementation("org.springframework.boot:spring-boot-starter-kafka:4.0.0")
implementation("org.springframework.boot:spring-boot-starter-actuator:4.0.0")
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc:4.0.0")
implementation("org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.0")
implementation("io.opentelemetry:opentelemetry-sdk")
implementation("io.opentelemetry:opentelemetry-sdk-testing")
implementation(project(":instrumentation-api"))
implementation(project(":instrumentation:micrometer:micrometer-1.5:library"))
implementation("org.springframework.boot:spring-boot-starter-test:4.0.0") {
exclude("org.junit.vintage", "junit-vintage-engine")
}
runtimeOnly("com.h2database:h2:1.4.197")
runtimeOnly("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")
}
}

val testDeclarativeConfig by registering(JvmTestSuite::class) {
dependencies {
implementation(project())
Expand All @@ -208,6 +244,15 @@ configurations.configureEach {
tasks {
compileTestJava {
options.compilerArgs.add("-parameters")

// Exclude Spring Boot specific tests from compilation when testLatestDeps is true
// These tests are covered by testSpring4 suite
if (latestDepTest) {
exclude("**/micrometer/MicrometerBridgeAutoConfigurationTest.java")
exclude("**/r2dbc/R2DbcInstrumentationAutoConfigurationTest.java")
exclude("**/jdbc/JdbcInstrumentationAutoConfigurationTest.java")
exclude("**/web/SpringWebInstrumentationAutoConfigurationTest.java")
}
}

withType<Test>().configureEach {
Expand All @@ -218,6 +263,17 @@ tasks {
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
}

test {
// Exclude Spring Boot specific tests when testLatestDeps is true
// These tests are covered by testSpring4 suite
if (latestDepTest) {
exclude("**/micrometer/MicrometerBridgeAutoConfigurationTest.class")
exclude("**/r2dbc/R2DbcInstrumentationAutoConfigurationTest.class")
exclude("**/jdbc/JdbcInstrumentationAutoConfigurationTest.class")
exclude("**/web/SpringWebInstrumentationAutoConfigurationTest.class")
}
}

named<JavaCompile>("compileJavaSpring3Java") {
sourceCompatibility = "17"
targetCompatibility = "17"
Expand All @@ -234,12 +290,30 @@ tasks {
isEnabled = testSpring3
}

named<JavaCompile>("compileJavaSpring4Java") {
sourceCompatibility = "17"
targetCompatibility = "17"
options.release.set(17)
}

named<JavaCompile>("compileTestSpring4Java") {
sourceCompatibility = "17"
targetCompatibility = "17"
options.release.set(17)
}

named<Test>("testSpring4") {
isEnabled = testSpring3 // same condition as Spring 3 (requires Java 17+)
}

named<Jar>("jar") {
from(sourceSets["javaSpring3"].output)
from(sourceSets["javaSpring4"].output)
}

named<Jar>("sourcesJar") {
from(sourceSets["javaSpring3"].java)
from(sourceSets["javaSpring4"].java)
}

val testStableSemconv by registering(Test::class) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -23,6 +24,7 @@
@ConditionalOnEnabledInstrumentation(module = "jdbc")
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
@ConditionalOnBean({DataSource.class})
@ConditionalOnClass(DataSourceAutoConfiguration.class)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a comment back? e.g. "removed in Spring Boot 4"

@Configuration(proxyBeanMethods = false)
public class JdbcInstrumentationAutoConfiguration {

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

package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.jdbc;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
import javax.sql.DataSource;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
@ConditionalOnEnabledInstrumentation(module = "jdbc")
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
@ConditionalOnBean({DataSource.class})
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DataSourceAutoConfiguration.class)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

condition looks identical to boot 3

public class JdbcInstrumentationSpringBoot4AutoConfiguration {

// For error prone
public JdbcInstrumentationSpringBoot4AutoConfiguration() {}

@Bean
// static to avoid "is not eligible for getting processed by all BeanPostProcessors" warning
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really for this PR but I suspect that (proxyBeanMethods = false) has the same effect as making the method static. If I remember correctly the warning was triggered by proxying the configuration class methods.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right - that would be a nice follow-up PR

static DataSourcePostProcessor dataSourcePostProcessor(
ObjectProvider<OpenTelemetry> openTelemetryProvider,
ObjectProvider<InstrumentationConfig> configProvider) {
return new DataSourcePostProcessor(openTelemetryProvider, configProvider);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.kafka;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
import io.opentelemetry.instrumentation.spring.kafka.v2_7.SpringKafkaTelemetry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.kafka.autoconfigure.DefaultKafkaProducerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.KafkaTemplate;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
@ConditionalOnEnabledInstrumentation(module = "kafka")
@ConditionalOnClass({
KafkaTemplate.class,
ConcurrentKafkaListenerContainerFactory.class,
DefaultKafkaProducerFactoryCustomizer.class
})
@ConditionalOnMissingBean(name = "otelKafkaProducerFactoryCustomizer")
@Configuration
public class KafkaInstrumentationSpringBoot4AutoConfiguration {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this class still needed? Tests pass without it.


@Bean
DefaultKafkaProducerFactoryCustomizer otelKafkaProducerFactoryCustomizer(
OpenTelemetry openTelemetry) {
KafkaTelemetry kafkaTelemetry = KafkaTelemetry.create(openTelemetry);
return producerFactory -> producerFactory.addPostProcessor(kafkaTelemetry::wrap);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually only this bean is different for spring boot 4. Wondering whether it would make sense to move this bean into separate (maybe nested?) configuration class so that the other beans wouldn't need to be copy-pasted. Or is that too much effort?

Copy link
Member

@zeitlinger zeitlinger Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the imports are also different for the others

image


@Bean
static SpringKafkaTelemetry getTelemetry(
ObjectProvider<OpenTelemetry> openTelemetryProvider,
ObjectProvider<InstrumentationConfig> configProvider) {
return SpringKafkaTelemetry.builder(openTelemetryProvider.getObject())
.setCaptureExperimentalSpanAttributes(
configProvider
.getObject()
.getBoolean("otel.instrumentation.kafka.experimental-span-attributes", false))
.build();
}

// static to avoid "is not eligible for getting processed by all BeanPostProcessors" warning
@Bean
@ConditionalOnProperty(
name = "otel.instrumentation.kafka.autoconfigure-interceptor",
havingValue = "true",
matchIfMissing = true)
@ConditionalOnMissingBean
static ConcurrentKafkaListenerContainerFactoryPostProcessor
otelKafkaListenerContainerFactoryBeanPostProcessor(
ObjectProvider<OpenTelemetry> openTelemetryProvider,
ObjectProvider<InstrumentationConfig> configProvider) {
return new ConcurrentKafkaListenerContainerFactoryPostProcessor(
() -> getTelemetry(openTelemetryProvider, configProvider));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.micrometer;

import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.MeterRegistry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.micrometer.v1_5.OpenTelemetryMeterRegistry;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.micrometer.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
@ConditionalOnEnabledInstrumentation(module = "micrometer", enabledByDefault = false)
@AutoConfigureAfter({MetricsAutoConfiguration.class, OpenTelemetryAutoConfiguration.class})
@AutoConfigureBefore(CompositeMeterRegistryAutoConfiguration.class)
@ConditionalOnBean(Clock.class)
@ConditionalOnClass({MeterRegistry.class, MetricsAutoConfiguration.class})
@Configuration
public class MicrometerBridgeSpringBoot4AutoConfiguration {

@Bean
MeterRegistry otelMeterRegistry(OpenTelemetry openTelemetry, Clock micrometerClock) {
return OpenTelemetryMeterRegistry.builder(openTelemetry).setClock(micrometerClock).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.mongo;

import com.mongodb.MongoClientSettings;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.mongodb.autoconfigure.MongoClientSettingsBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
@ConditionalOnClass({MongoClientSettings.class, MongoClientSettingsBuilderCustomizer.class})
@ConditionalOnEnabledInstrumentation(module = "mongo")
@Configuration
public class MongoClientInstrumentationSpringBoot4AutoConfiguration {

@Bean
MongoClientSettingsBuilderCustomizer customizer(
OpenTelemetry openTelemetry, InstrumentationConfig config) {
return builder ->
builder.addCommandListener(
MongoTelemetry.builder(openTelemetry)
.setStatementSanitizationEnabled(
InstrumentationConfigUtil.isStatementSanitizationEnabled(
config, "otel.instrumentation.mongo.statement-sanitizer.enabled"))
.build()
.newCommandListener());
}
}
Loading
Loading