Skip to content

Commit 090f148

Browse files
jaydelucalaurit
andauthored
[Spring Starter] Spring boot 4 support (#15459)
Co-authored-by: Lauri Tulmin <[email protected]>
1 parent 71ca938 commit 090f148

File tree

55 files changed

+1415
-132
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1415
-132
lines changed

.github/graal-native-docker-compose.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ services:
55
- "27017:27017"
66

77
zookeeper:
8-
image: confluentinc/cp-zookeeper:6.2.10
8+
image: confluentinc/cp-zookeeper:7.7.7
99
environment:
1010
ZOOKEEPER_CLIENT_PORT: 2181
1111
ZOOKEEPER_TICK_TIME: 2000
1212
ports:
1313
- "22181:2181"
1414

1515
kafka:
16-
image: confluentinc/cp-kafka:6.2.10
16+
image: confluentinc/cp-kafka:7.7.7
1717
ports:
1818
- 9094:9094
1919
depends_on:

.github/workflows/codeql.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ jobs:
8080
./gradlew assemble -x javadoc
8181
-x :smoke-tests-otel-starter:spring-boot-3:collectReachabilityMetadata
8282
-x :smoke-tests-otel-starter:spring-boot-3.2:collectReachabilityMetadata
83+
-x :smoke-tests-otel-starter:spring-boot-4:collectReachabilityMetadata
8384
-x :smoke-tests-otel-starter:spring-boot-reactive-3:collectReachabilityMetadata
8485
--no-build-cache --no-daemon
8586

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

Lines changed: 101 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,20 @@ sourceSets {
2323
setSrcDirs(listOf("src/main/javaSpring3"))
2424
}
2525
}
26+
create("javaSpring4") {
27+
java {
28+
setSrcDirs(listOf("src/main/javaSpring4"))
29+
}
30+
}
2631
}
2732

2833
configurations {
2934
named("javaSpring3CompileOnly") {
3035
extendsFrom(configurations["compileOnly"])
3136
}
37+
named("javaSpring4CompileOnly") {
38+
extendsFrom(configurations["compileOnly"])
39+
}
3240
}
3341

3442
dependencies {
@@ -99,6 +107,9 @@ dependencies {
99107
testImplementation("io.opentelemetry:opentelemetry-exporter-otlp")
100108
testImplementation("io.opentelemetry:opentelemetry-exporter-zipkin")
101109
testImplementation(project(":instrumentation-annotations"))
110+
testImplementation(project(":instrumentation:spring:spring-boot-autoconfigure:testing"))
111+
112+
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-micrometer-metrics:latest.release")
102113

103114
// needed for the Spring Boot 3 support
104115
implementation(project(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:library"))
@@ -110,15 +121,19 @@ dependencies {
110121
add("javaSpring3CompileOnly", project(":instrumentation:spring:spring-web:spring-web-3.1:library"))
111122
add("javaSpring3CompileOnly", project(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:library"))
112123

113-
// tests don't work with spring boot 4 yet
114-
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-test:3.+") // documented limitation
115-
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-actuator:3.+") // documented limitation
116-
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-aop:3.+") // documented limitation
117-
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-web:3.+") // documented limitation
118-
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-webflux:3.+") // documented limitation
119-
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-data-mongodb:3.+") // documented limitation
120-
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-data-r2dbc:3.+") // documented limitation
121-
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-data-jdbc:3.+") // documented limitation
124+
// Spring Boot 4
125+
add("javaSpring4CompileOnly", files(sourceSets.main.get().output.classesDirs))
126+
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-starter-kafka:4.0.0")
127+
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-autoconfigure:4.0.0")
128+
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-jdbc:4.0.0")
129+
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-starter-jdbc:4.0.0")
130+
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-restclient:4.0.0")
131+
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-starter-data-mongodb:4.0.0")
132+
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.0")
133+
add("javaSpring4CompileOnly", project(":instrumentation:kafka:kafka-clients:kafka-clients-2.6:library"))
134+
add("javaSpring4CompileOnly", project(":instrumentation:spring:spring-kafka-2.7:library"))
135+
add("javaSpring4CompileOnly", project(":instrumentation:mongo:mongo-3.1:library"))
136+
add("javaSpring4CompileOnly", project(":instrumentation:micrometer:micrometer-1.5:library"))
122137
}
123138

124139
val latestDepTest = findProperty("testLatestDeps") as Boolean
@@ -172,17 +187,62 @@ testing {
172187
}
173188
}
174189

190+
val testSpring2 by registering(JvmTestSuite::class) {
191+
dependencies {
192+
implementation(project())
193+
implementation("io.opentelemetry:opentelemetry-sdk")
194+
implementation("io.opentelemetry:opentelemetry-sdk-testing")
195+
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
196+
implementation(project(":instrumentation-api"))
197+
implementation(project(":instrumentation:micrometer:micrometer-1.5:library"))
198+
implementation(project(":instrumentation:spring:spring-boot-autoconfigure:testing"))
199+
// configure Spring Boot 3.x dependencies for latest dep testing
200+
val version = if (latestDepTest) "3.+" else springBootVersion
201+
implementation("org.springframework.boot:spring-boot-starter-test:$version")
202+
implementation("org.springframework.boot:spring-boot-starter-actuator:$version")
203+
implementation("org.springframework.boot:spring-boot-starter-web:$version")
204+
implementation("org.springframework.boot:spring-boot-starter-jdbc:$version")
205+
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc:$version")
206+
val springKafkaVersion = if (latestDepTest) "3.+" else "2.9.0"
207+
implementation("org.springframework.kafka:spring-kafka:$springKafkaVersion")
208+
implementation("javax.servlet:javax.servlet-api:3.1.0")
209+
runtimeOnly("com.h2database:h2:1.4.197")
210+
runtimeOnly("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")
211+
}
212+
}
213+
175214
val testSpring3 by registering(JvmTestSuite::class) {
176215
dependencies {
177216
implementation(project())
178-
implementation("org.springframework.boot:spring-boot-starter-web:3.2.4")
217+
val version = if (latestDepTest) "3.+" else "3.2.4"
218+
implementation("org.springframework.boot:spring-boot-starter-web:$version")
179219
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
180220
implementation(project(":instrumentation:spring:spring-web:spring-web-3.1:library"))
181221
implementation(project(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:library"))
182222
implementation("jakarta.servlet:jakarta.servlet-api:5.0.0")
183-
implementation("org.springframework.boot:spring-boot-starter-test:3.2.4") {
184-
exclude("org.junit.vintage", "junit-vintage-engine")
185-
}
223+
implementation("org.springframework.boot:spring-boot-starter-test:$version")
224+
}
225+
}
226+
227+
val testSpring4 by registering(JvmTestSuite::class) {
228+
dependencies {
229+
implementation(project())
230+
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
231+
val version = if (latestDepTest) "latest.release" else "4.0.0"
232+
implementation("org.springframework.boot:spring-boot-starter-jdbc:$version")
233+
implementation("org.springframework.boot:spring-boot-restclient:$version")
234+
implementation("org.springframework.boot:spring-boot-starter-kafka:$version")
235+
implementation("org.springframework.boot:spring-boot-starter-actuator:$version")
236+
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc:$version")
237+
implementation("org.springframework.boot:spring-boot-starter-micrometer-metrics:$version")
238+
implementation("io.opentelemetry:opentelemetry-sdk")
239+
implementation("io.opentelemetry:opentelemetry-sdk-testing")
240+
implementation(project(":instrumentation-api"))
241+
implementation(project(":instrumentation:micrometer:micrometer-1.5:library"))
242+
implementation(project(":instrumentation:spring:spring-boot-autoconfigure:testing"))
243+
implementation("org.springframework.boot:spring-boot-starter-test:$version")
244+
runtimeOnly("com.h2database:h2:1.4.197")
245+
runtimeOnly("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")
186246
}
187247
}
188248

@@ -225,22 +285,50 @@ tasks {
225285
options.release.set(17)
226286
}
227287

288+
named<JavaCompile>("compileTestSpring2Java") {
289+
sourceCompatibility = "17"
290+
targetCompatibility = "17"
291+
options.release.set(17)
292+
}
293+
228294
named<JavaCompile>("compileTestSpring3Java") {
229295
sourceCompatibility = "17"
230296
targetCompatibility = "17"
231297
options.release.set(17)
232298
}
233299

300+
named<Test>("testSpring2") {
301+
isEnabled = testSpring3
302+
}
303+
234304
named<Test>("testSpring3") {
235305
isEnabled = testSpring3
236306
}
237307

308+
named<JavaCompile>("compileJavaSpring4Java") {
309+
sourceCompatibility = "17"
310+
targetCompatibility = "17"
311+
options.release.set(17)
312+
}
313+
314+
named<JavaCompile>("compileTestSpring4Java") {
315+
sourceCompatibility = "17"
316+
targetCompatibility = "17"
317+
options.release.set(17)
318+
}
319+
320+
named<Test>("testSpring4") {
321+
isEnabled = testSpring3 // same condition as Spring 3 (requires Java 17+)
322+
}
323+
238324
named<Jar>("jar") {
239325
from(sourceSets["javaSpring3"].output)
326+
from(sourceSets["javaSpring4"].output)
240327
}
241328

242329
named<Jar>("sourcesJar") {
243330
from(sourceSets["javaSpring3"].java)
331+
from(sourceSets["javaSpring4"].java)
244332
}
245333

246334
val testStableSemconv by registering(Test::class) {

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/JdbcInstrumentationAutoConfiguration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.springframework.beans.factory.ObjectProvider;
1313
import org.springframework.boot.autoconfigure.AutoConfiguration;
1414
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
15+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
1516
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
1617
import org.springframework.context.annotation.Bean;
1718
import org.springframework.context.annotation.Configuration;
@@ -23,6 +24,7 @@
2324
@ConditionalOnEnabledInstrumentation(module = "jdbc")
2425
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
2526
@ConditionalOnBean({DataSource.class})
27+
@ConditionalOnClass(DataSourceAutoConfiguration.class) // module changed in Spring Boot 4
2628
@Configuration(proxyBeanMethods = false)
2729
public class JdbcInstrumentationAutoConfiguration {
2830

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/kafka/KafkaInstrumentationAutoConfiguration.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@
77

88
import io.opentelemetry.api.OpenTelemetry;
99
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
10-
import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry;
1110
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
1211
import io.opentelemetry.instrumentation.spring.kafka.v2_7.SpringKafkaTelemetry;
1312
import org.springframework.beans.factory.ObjectProvider;
1413
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
1514
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
16-
import org.springframework.boot.autoconfigure.kafka.DefaultKafkaProducerFactoryCustomizer;
1715
import org.springframework.context.annotation.Bean;
1816
import org.springframework.context.annotation.Configuration;
1917
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
@@ -27,17 +25,11 @@
2725
@ConditionalOnClass({
2826
KafkaTemplate.class,
2927
ConcurrentKafkaListenerContainerFactory.class,
30-
DefaultKafkaProducerFactoryCustomizer.class
3128
})
3229
@Configuration
3330
public class KafkaInstrumentationAutoConfiguration {
3431

35-
@Bean
36-
DefaultKafkaProducerFactoryCustomizer otelKafkaProducerFactoryCustomizer(
37-
OpenTelemetry openTelemetry) {
38-
KafkaTelemetry kafkaTelemetry = KafkaTelemetry.create(openTelemetry);
39-
return producerFactory -> producerFactory.addPostProcessor(kafkaTelemetry::wrap);
40-
}
32+
public KafkaInstrumentationAutoConfiguration() {}
4133

4234
@Bean
4335
static SpringKafkaTelemetry getTelemetry(
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.spring.autoconfigure.internal.instrumentation.kafka;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry;
10+
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
11+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
12+
import org.springframework.boot.autoconfigure.kafka.DefaultKafkaProducerFactoryCustomizer;
13+
import org.springframework.context.annotation.Bean;
14+
import org.springframework.context.annotation.Configuration;
15+
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
16+
17+
/**
18+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
19+
* any time.
20+
*/
21+
@ConditionalOnEnabledInstrumentation(module = "kafka")
22+
// package changed in Spring Boot 4
23+
@ConditionalOnClass({
24+
DefaultKafkaProducerFactoryCustomizer.class,
25+
DefaultKafkaProducerFactory.class
26+
})
27+
@Configuration
28+
public class ProducerFactoryCustomizerConfiguration {
29+
30+
public ProducerFactoryCustomizerConfiguration() {}
31+
32+
@Bean
33+
static DefaultKafkaProducerFactoryCustomizer otelKafkaProducerFactoryCustomizer(
34+
OpenTelemetry openTelemetry) {
35+
KafkaTelemetry kafkaTelemetry = KafkaTelemetry.create(openTelemetry);
36+
return producerFactory -> producerFactory.addPostProcessor(kafkaTelemetry::wrap);
37+
}
38+
}

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/micrometer/MicrometerBridgeAutoConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
@AutoConfigureAfter({MetricsAutoConfiguration.class, OpenTelemetryAutoConfiguration.class})
2929
@AutoConfigureBefore(CompositeMeterRegistryAutoConfiguration.class)
3030
@ConditionalOnBean(Clock.class)
31-
@ConditionalOnClass(MeterRegistry.class)
31+
@ConditionalOnClass(MeterRegistry.class) // module changed in Spring Boot 4
3232
@Configuration
3333
public class MicrometerBridgeAutoConfiguration {
3434

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/mongo/MongoClientInstrumentationAutoConfiguration.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
2121
* any time.
2222
*/
23-
@ConditionalOnClass({MongoClientSettings.class, MongoClientSettingsBuilderCustomizer.class})
23+
@ConditionalOnClass({
24+
MongoClientSettings.class,
25+
MongoClientSettingsBuilderCustomizer.class
26+
}) // module changed in Spring Boot 4
2427
@ConditionalOnEnabledInstrumentation(module = "mongo")
2528
@Configuration
2629
public class MongoClientInstrumentationAutoConfiguration {

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateBeanPostProcessor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
import org.springframework.beans.factory.config.BeanPostProcessor;
1212
import org.springframework.web.client.RestTemplate;
1313

14-
final class RestTemplateBeanPostProcessor implements BeanPostProcessor {
14+
/**
15+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
16+
* any time.
17+
*/
18+
public final class RestTemplateBeanPostProcessor implements BeanPostProcessor {
1519

1620
private final ObjectProvider<OpenTelemetry> openTelemetryProvider;
1721

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.jdbc;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
10+
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
11+
import javax.sql.DataSource;
12+
import org.springframework.beans.factory.ObjectProvider;
13+
import org.springframework.boot.autoconfigure.AutoConfiguration;
14+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
15+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
16+
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
17+
import org.springframework.context.annotation.Bean;
18+
import org.springframework.context.annotation.Configuration;
19+
20+
/**
21+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
22+
* any time.
23+
*/
24+
@ConditionalOnEnabledInstrumentation(module = "jdbc")
25+
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
26+
@ConditionalOnBean({DataSource.class})
27+
@Configuration(proxyBeanMethods = false)
28+
@ConditionalOnClass(DataSourceAutoConfiguration.class)
29+
public class JdbcInstrumentationSpringBoot4AutoConfiguration {
30+
31+
// For error prone
32+
public JdbcInstrumentationSpringBoot4AutoConfiguration() {}
33+
34+
@Bean
35+
// static to avoid "is not eligible for getting processed by all BeanPostProcessors" warning
36+
static DataSourcePostProcessor dataSourcePostProcessor(
37+
ObjectProvider<OpenTelemetry> openTelemetryProvider,
38+
ObjectProvider<InstrumentationConfig> configProvider) {
39+
return new DataSourcePostProcessor(openTelemetryProvider, configProvider);
40+
}
41+
}

0 commit comments

Comments
 (0)