Make the JDBC driver config work with the OTel starter (#9625)

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
Jean Bisutti 2023-10-23 18:59:36 +02:00 committed by GitHub
parent 71c8c370fb
commit e6ec4f52ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 134 additions and 9 deletions

View File

@ -68,3 +68,7 @@ public class DataSourceConfig {
1. Activate tracing for JDBC connections by setting `jdbc:otel:` prefix to the JDBC URL, e.g. `jdbc:otel:h2:mem:test`.
2. Set the driver class to `io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver`.
3. Inject `OpenTelemetry` into `io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver` _before the initialization of the database connection pool_.
You can do this with the `void setOpenTelemetry(OpenTelemetry openTelemetry)` method of `io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver`.
Another way is to use `OpenTelemetryDriver.install(OpenTelemetry openTelemetry)`.

View File

@ -21,10 +21,13 @@
package io.opentelemetry.instrumentation.jdbc;
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcInstrumenterFactory.INSTRUMENTATION_NAME;
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcSingletons.statementInstrumenter;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
import io.opentelemetry.instrumentation.jdbc.internal.DbRequest;
import io.opentelemetry.instrumentation.jdbc.internal.JdbcConnectionUrlParser;
import io.opentelemetry.instrumentation.jdbc.internal.JdbcInstrumenterFactory;
import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection;
import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo;
import java.sql.Connection;
@ -35,6 +38,7 @@ import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;
@ -48,6 +52,8 @@ public final class OpenTelemetryDriver implements Driver {
// visible for testing
static final OpenTelemetryDriver INSTANCE = new OpenTelemetryDriver();
private volatile OpenTelemetry openTelemetry = OpenTelemetry.noop();
private static final int MAJOR_VERSION;
private static final int MINOR_VERSION;
@ -192,6 +198,30 @@ public final class OpenTelemetryDriver implements Driver {
return new int[] {0, 0};
}
/**
* Installs the {@link OpenTelemetry} instance on the {@code OpenTelemetryDriver}. OpenTelemetry
* has to be set before the initialization of the database connection pool.
*/
public static void install(OpenTelemetry openTelemetry) {
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
if (driver instanceof io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver) {
OpenTelemetryDriver openTelemetryDriver = (OpenTelemetryDriver) driver;
openTelemetryDriver.setOpenTelemetry(openTelemetry);
}
}
}
/**
* Configures the {@link OpenTelemetry}. See {@link #install(OpenTelemetry)} for simple
* installation option. OpenTelemetry has to be set before the initialization of the database
* connection pool.
*/
public void setOpenTelemetry(OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
}
@Nullable
@Override
public Connection connect(String url, Properties info) throws SQLException {
@ -212,7 +242,9 @@ public final class OpenTelemetryDriver implements Driver {
DbInfo dbInfo = JdbcConnectionUrlParser.parse(realUrl, info);
return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter());
Instrumenter<DbRequest, Void> statementInstrumenter =
JdbcInstrumenterFactory.createStatementInstrumenter(openTelemetry);
return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter);
}
@Override

View File

@ -29,6 +29,7 @@ dependencies {
compileOnly("org.apache.logging.log4j:log4j-core:2.17.0")
implementation(project(":instrumentation:logback:logback-appender-1.0:library"))
compileOnly("ch.qos.logback:logback-classic:1.0.0")
implementation(project(":instrumentation:jdbc:library"))
library("org.springframework.kafka:spring-kafka:2.9.0")
library("org.springframework.boot:spring-boot-starter-actuator:$springBootVersion")

View File

@ -132,20 +132,30 @@ public class OpenTelemetryAutoConfiguration {
}
@Bean
// If you change the bean name, also change it in the OpenTelemetryJdbcDriverAutoConfiguration
// class
public OpenTelemetry openTelemetry(
ObjectProvider<ContextPropagators> propagatorsProvider,
SdkTracerProvider tracerProvider,
SdkMeterProvider meterProvider,
SdkLoggerProvider loggerProvider) {
SdkLoggerProvider loggerProvider,
ObjectProvider<List<OpenTelemetryInjector>> openTelemetryConsumerProvider) {
ContextPropagators propagators = propagatorsProvider.getIfAvailable(ContextPropagators::noop);
return OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.setMeterProvider(meterProvider)
.setLoggerProvider(loggerProvider)
.setPropagators(propagators)
.build();
OpenTelemetrySdk openTelemetry =
OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.setMeterProvider(meterProvider)
.setLoggerProvider(loggerProvider)
.setPropagators(propagators)
.build();
List<OpenTelemetryInjector> openTelemetryInjectors =
openTelemetryConsumerProvider.getIfAvailable(() -> Collections.emptyList());
openTelemetryInjectors.forEach(consumer -> consumer.accept(openTelemetry));
return openTelemetry;
}
}

View File

@ -0,0 +1,12 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure;
import io.opentelemetry.api.OpenTelemetry;
import java.util.function.Consumer;
/** To inject an OpenTelemetry bean into non-Spring components */
public interface OpenTelemetryInjector extends Consumer<OpenTelemetry> {}

View File

@ -0,0 +1,39 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.jdbc;
import io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryInjector;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ConditionalOnClass(OpenTelemetryDriver.class)
@ConditionalOnProperty(
name = "spring.datasource.driver-class-name",
havingValue = "io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver")
@Configuration(proxyBeanMethods = false)
public class OpenTelemetryJdbcDriverAutoConfiguration {
@Bean
OpenTelemetryInjector injectOtelIntoJdbcDriver() {
return openTelemetry -> OpenTelemetryDriver.install(openTelemetry);
}
// To be sure OpenTelemetryDriver knows the OpenTelemetry bean before the initialization of the
// database connection pool
// See org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration and
// io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration
@Bean
BeanFactoryPostProcessor openTelemetryBeanCreatedBeforeDatasourceBean() {
return configurableBeanFactory -> {
BeanDefinition dataSourceBean = configurableBeanFactory.getBeanDefinition("dataSource");
dataSourceBean.setDependsOn("openTelemetry");
};
}
}

View File

@ -9,6 +9,7 @@ io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpa
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.annotations.InstrumentationAnnotationsAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.kafka.KafkaInstrumentationAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.logging.OpenTelemetryAppenderAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.jdbc.OpenTelemetryJdbcDriverAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.micrometer.MicrometerBridgeAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.web.SpringWebInstrumentationAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.webflux.SpringWebfluxInstrumentationAutoConfiguration,\

View File

@ -8,6 +8,7 @@ io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpa
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.annotations.InstrumentationAnnotationsAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.kafka.KafkaInstrumentationAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.logging.OpenTelemetryAppenderAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.jdbc.OpenTelemetryJdbcDriverAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.micrometer.MicrometerBridgeAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.web.SpringWebInstrumentationAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.webflux.SpringWebfluxInstrumentationAutoConfiguration

View File

@ -59,4 +59,9 @@ graalvmNative {
buildArgs.add("--initialize-at-build-time=org.junit.platform.launcher.core.LauncherConfig")
buildArgs.add("--initialize-at-build-time=org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter")
}
tasks.test {
useJUnitPlatform()
setForkEvery(1)
}
}

View File

@ -11,8 +11,10 @@ import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
@Profile("!jdbc-driver-config")
public class DatasourceConfig {
@Bean

View File

@ -0,0 +1,4 @@
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:otel:h2:mem:db
spring.datasource.driver-class-name=io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver

View File

@ -0,0 +1,14 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.smoketest;
import org.junit.jupiter.api.condition.DisabledInNativeImage;
import org.springframework.test.context.ActiveProfiles;
@DisabledInNativeImage // Spring native does not support the profile setting at runtime:
// https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.aot.conditions
@ActiveProfiles(value = "jdbc-driver-config")
class OtelSpringStarterJdbcDriverConfigSmokeTest extends OtelSpringStarterSmokeTest {}