Skip to content

Commit e6ec4f5

Browse files
jeanbisuttitrask
andauthored
Make the JDBC driver config work with the OTel starter (#9625)
Co-authored-by: Trask Stalnaker <[email protected]>
1 parent 71c8c37 commit e6ec4f5

File tree

12 files changed

+134
-9
lines changed

12 files changed

+134
-9
lines changed

instrumentation/jdbc/library/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,7 @@ public class DataSourceConfig {
6868
1. Activate tracing for JDBC connections by setting `jdbc:otel:` prefix to the JDBC URL, e.g. `jdbc:otel:h2:mem:test`.
6969

7070
2. Set the driver class to `io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver`.
71+
72+
3. Inject `OpenTelemetry` into `io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver` _before the initialization of the database connection pool_.
73+
You can do this with the `void setOpenTelemetry(OpenTelemetry openTelemetry)` method of `io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver`.
74+
Another way is to use `OpenTelemetryDriver.install(OpenTelemetry openTelemetry)`.

instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/OpenTelemetryDriver.java

+34-2
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@
2121
package io.opentelemetry.instrumentation.jdbc;
2222

2323
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcInstrumenterFactory.INSTRUMENTATION_NAME;
24-
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcSingletons.statementInstrumenter;
2524

25+
import io.opentelemetry.api.OpenTelemetry;
26+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
2627
import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
28+
import io.opentelemetry.instrumentation.jdbc.internal.DbRequest;
2729
import io.opentelemetry.instrumentation.jdbc.internal.JdbcConnectionUrlParser;
30+
import io.opentelemetry.instrumentation.jdbc.internal.JdbcInstrumenterFactory;
2831
import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection;
2932
import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo;
3033
import java.sql.Connection;
@@ -35,6 +38,7 @@
3538
import java.sql.SQLFeatureNotSupportedException;
3639
import java.util.Collection;
3740
import java.util.Collections;
41+
import java.util.Enumeration;
3842
import java.util.List;
3943
import java.util.Properties;
4044
import java.util.concurrent.CopyOnWriteArrayList;
@@ -48,6 +52,8 @@ public final class OpenTelemetryDriver implements Driver {
4852
// visible for testing
4953
static final OpenTelemetryDriver INSTANCE = new OpenTelemetryDriver();
5054

55+
private volatile OpenTelemetry openTelemetry = OpenTelemetry.noop();
56+
5157
private static final int MAJOR_VERSION;
5258
private static final int MINOR_VERSION;
5359

@@ -192,6 +198,30 @@ private static int[] parseInstrumentationVersion() {
192198
return new int[] {0, 0};
193199
}
194200

201+
/**
202+
* Installs the {@link OpenTelemetry} instance on the {@code OpenTelemetryDriver}. OpenTelemetry
203+
* has to be set before the initialization of the database connection pool.
204+
*/
205+
public static void install(OpenTelemetry openTelemetry) {
206+
Enumeration<Driver> drivers = DriverManager.getDrivers();
207+
while (drivers.hasMoreElements()) {
208+
Driver driver = drivers.nextElement();
209+
if (driver instanceof io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver) {
210+
OpenTelemetryDriver openTelemetryDriver = (OpenTelemetryDriver) driver;
211+
openTelemetryDriver.setOpenTelemetry(openTelemetry);
212+
}
213+
}
214+
}
215+
216+
/**
217+
* Configures the {@link OpenTelemetry}. See {@link #install(OpenTelemetry)} for simple
218+
* installation option. OpenTelemetry has to be set before the initialization of the database
219+
* connection pool.
220+
*/
221+
public void setOpenTelemetry(OpenTelemetry openTelemetry) {
222+
this.openTelemetry = openTelemetry;
223+
}
224+
195225
@Nullable
196226
@Override
197227
public Connection connect(String url, Properties info) throws SQLException {
@@ -212,7 +242,9 @@ public Connection connect(String url, Properties info) throws SQLException {
212242

213243
DbInfo dbInfo = JdbcConnectionUrlParser.parse(realUrl, info);
214244

215-
return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter());
245+
Instrumenter<DbRequest, Void> statementInstrumenter =
246+
JdbcInstrumenterFactory.createStatementInstrumenter(openTelemetry);
247+
return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter);
216248
}
217249

218250
@Override

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

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies {
2929
compileOnly("org.apache.logging.log4j:log4j-core:2.17.0")
3030
implementation(project(":instrumentation:logback:logback-appender-1.0:library"))
3131
compileOnly("ch.qos.logback:logback-classic:1.0.0")
32+
implementation(project(":instrumentation:jdbc:library"))
3233

3334
library("org.springframework.kafka:spring-kafka:2.9.0")
3435
library("org.springframework.boot:spring-boot-starter-actuator:$springBootVersion")

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

+17-7
Original file line numberDiff line numberDiff line change
@@ -132,20 +132,30 @@ public Resource otelResource(
132132
}
133133

134134
@Bean
135+
// If you change the bean name, also change it in the OpenTelemetryJdbcDriverAutoConfiguration
136+
// class
135137
public OpenTelemetry openTelemetry(
136138
ObjectProvider<ContextPropagators> propagatorsProvider,
137139
SdkTracerProvider tracerProvider,
138140
SdkMeterProvider meterProvider,
139-
SdkLoggerProvider loggerProvider) {
141+
SdkLoggerProvider loggerProvider,
142+
ObjectProvider<List<OpenTelemetryInjector>> openTelemetryConsumerProvider) {
140143

141144
ContextPropagators propagators = propagatorsProvider.getIfAvailable(ContextPropagators::noop);
142145

143-
return OpenTelemetrySdk.builder()
144-
.setTracerProvider(tracerProvider)
145-
.setMeterProvider(meterProvider)
146-
.setLoggerProvider(loggerProvider)
147-
.setPropagators(propagators)
148-
.build();
146+
OpenTelemetrySdk openTelemetry =
147+
OpenTelemetrySdk.builder()
148+
.setTracerProvider(tracerProvider)
149+
.setMeterProvider(meterProvider)
150+
.setLoggerProvider(loggerProvider)
151+
.setPropagators(propagators)
152+
.build();
153+
154+
List<OpenTelemetryInjector> openTelemetryInjectors =
155+
openTelemetryConsumerProvider.getIfAvailable(() -> Collections.emptyList());
156+
openTelemetryInjectors.forEach(consumer -> consumer.accept(openTelemetry));
157+
158+
return openTelemetry;
149159
}
150160
}
151161

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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 io.opentelemetry.api.OpenTelemetry;
9+
import java.util.function.Consumer;
10+
11+
/** To inject an OpenTelemetry bean into non-Spring components */
12+
public interface OpenTelemetryInjector extends Consumer<OpenTelemetry> {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.jdbc;
7+
8+
import io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver;
9+
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryInjector;
10+
import org.springframework.beans.factory.config.BeanDefinition;
11+
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
12+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
13+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
14+
import org.springframework.context.annotation.Bean;
15+
import org.springframework.context.annotation.Configuration;
16+
17+
@ConditionalOnClass(OpenTelemetryDriver.class)
18+
@ConditionalOnProperty(
19+
name = "spring.datasource.driver-class-name",
20+
havingValue = "io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver")
21+
@Configuration(proxyBeanMethods = false)
22+
public class OpenTelemetryJdbcDriverAutoConfiguration {
23+
@Bean
24+
OpenTelemetryInjector injectOtelIntoJdbcDriver() {
25+
return openTelemetry -> OpenTelemetryDriver.install(openTelemetry);
26+
}
27+
28+
// To be sure OpenTelemetryDriver knows the OpenTelemetry bean before the initialization of the
29+
// database connection pool
30+
// See org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration and
31+
// io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration
32+
@Bean
33+
BeanFactoryPostProcessor openTelemetryBeanCreatedBeforeDatasourceBean() {
34+
return configurableBeanFactory -> {
35+
BeanDefinition dataSourceBean = configurableBeanFactory.getBeanDefinition("dataSource");
36+
dataSourceBean.setDependsOn("openTelemetry");
37+
};
38+
}
39+
}

instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpa
99
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.annotations.InstrumentationAnnotationsAutoConfiguration,\
1010
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.kafka.KafkaInstrumentationAutoConfiguration,\
1111
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.logging.OpenTelemetryAppenderAutoConfiguration,\
12+
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.jdbc.OpenTelemetryJdbcDriverAutoConfiguration,\
1213
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.micrometer.MicrometerBridgeAutoConfiguration,\
1314
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.web.SpringWebInstrumentationAutoConfiguration,\
1415
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.webflux.SpringWebfluxInstrumentationAutoConfiguration,\

instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpa
88
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.annotations.InstrumentationAnnotationsAutoConfiguration
99
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.kafka.KafkaInstrumentationAutoConfiguration
1010
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.logging.OpenTelemetryAppenderAutoConfiguration
11+
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.jdbc.OpenTelemetryJdbcDriverAutoConfiguration
1112
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.micrometer.MicrometerBridgeAutoConfiguration
1213
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.web.SpringWebInstrumentationAutoConfiguration
1314
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.webflux.SpringWebfluxInstrumentationAutoConfiguration

smoke-tests-otel-starter/build.gradle.kts

+5
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,9 @@ graalvmNative {
5959
buildArgs.add("--initialize-at-build-time=org.junit.platform.launcher.core.LauncherConfig")
6060
buildArgs.add("--initialize-at-build-time=org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter")
6161
}
62+
63+
tasks.test {
64+
useJUnitPlatform()
65+
setForkEvery(1)
66+
}
6267
}

smoke-tests-otel-starter/src/main/java/io/opentelemetry/spring/smoketest/DatasourceConfig.java

+2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
import org.apache.commons.dbcp2.BasicDataSource;
1212
import org.springframework.context.annotation.Bean;
1313
import org.springframework.context.annotation.Configuration;
14+
import org.springframework.context.annotation.Profile;
1415

1516
@Configuration
17+
@Profile("!jdbc-driver-config")
1618
public class DatasourceConfig {
1719

1820
@Bean
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
spring.datasource.username=root
2+
spring.datasource.password=root
3+
spring.datasource.url=jdbc:otel:h2:mem:db
4+
spring.datasource.driver-class-name=io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.smoketest;
7+
8+
import org.junit.jupiter.api.condition.DisabledInNativeImage;
9+
import org.springframework.test.context.ActiveProfiles;
10+
11+
@DisabledInNativeImage // Spring native does not support the profile setting at runtime:
12+
// https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.aot.conditions
13+
@ActiveProfiles(value = "jdbc-driver-config")
14+
class OtelSpringStarterJdbcDriverConfigSmokeTest extends OtelSpringStarterSmokeTest {}

0 commit comments

Comments
 (0)