diff --git a/docs/standalone-library-instrumentation.md b/docs/standalone-library-instrumentation.md index d170510202da..d24fe8bc23b7 100644 --- a/docs/standalone-library-instrumentation.md +++ b/docs/standalone-library-instrumentation.md @@ -10,6 +10,7 @@ that can be used if you prefer that over using the Java agent: * [AWS SDK 2.2+](../instrumentation/aws-sdk/aws-sdk-2.2/library) * [gRPC](../instrumentation/grpc-1.6/library) * [Guava](../instrumentation/guava-10.0/library) +* [JDBC](../instrumentation/jdbc/library) * [Lettuce](../instrumentation/lettuce/lettuce-5.1/library) * [Log4j](../instrumentation/log4j/log4j-2.13.2/library) * [Logback](../instrumentation/logback-1.0/library) diff --git a/instrumentation/jdbc/javaagent-unit-tests/build.gradle.kts b/instrumentation/jdbc/javaagent-unit-tests/build.gradle.kts deleted file mode 100644 index 04d22a3178f2..000000000000 --- a/instrumentation/jdbc/javaagent-unit-tests/build.gradle.kts +++ /dev/null @@ -1,7 +0,0 @@ -plugins { - id("otel.java-conventions") -} - -dependencies { - testImplementation(project(":instrumentation:jdbc:javaagent")) -} diff --git a/instrumentation/jdbc/javaagent/build.gradle.kts b/instrumentation/jdbc/javaagent/build.gradle.kts index fdbd87b39146..2b3b41e5c165 100644 --- a/instrumentation/jdbc/javaagent/build.gradle.kts +++ b/instrumentation/jdbc/javaagent/build.gradle.kts @@ -9,6 +9,7 @@ muzzle { } dependencies { + implementation(project(":instrumentation:jdbc:library")) compileOnly("com.google.auto.value:auto-value-annotations") annotationProcessor("com.google.auto.value:auto-value") @@ -26,6 +27,8 @@ dependencies { testLibrary("com.mchange:c3p0:0.9.5") latestDepTestLibrary("org.apache.derby:derby:10.14.+") + + testImplementation(project(":instrumentation:jdbc:testing")) } tasks.withType().configureEach { diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/ConnectionInstrumentation.java b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/ConnectionInstrumentation.java index b3a899937ad5..4f3a9dd5a740 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/ConnectionInstrumentation.java +++ b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/ConnectionInstrumentation.java @@ -12,6 +12,7 @@ import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import io.opentelemetry.instrumentation.jdbc.internal.JdbcMaps; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import java.sql.PreparedStatement; diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/DriverInstrumentation.java b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/DriverInstrumentation.java index 2bc70288036d..d6607e42f91c 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/DriverInstrumentation.java +++ b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/DriverInstrumentation.java @@ -12,6 +12,9 @@ import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import io.opentelemetry.instrumentation.jdbc.internal.DbInfo; +import io.opentelemetry.instrumentation.jdbc.internal.JdbcConnectionUrlParser; +import io.opentelemetry.instrumentation.jdbc.internal.JdbcMaps; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import java.sql.Connection; diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java index 1c8c935ce4e2..6ce589beb462 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java +++ b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java @@ -11,6 +11,9 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbSpanNameExtractor; +import io.opentelemetry.instrumentation.jdbc.internal.DbRequest; +import io.opentelemetry.instrumentation.jdbc.internal.JdbcAttributesExtractor; +import io.opentelemetry.instrumentation.jdbc.internal.JdbcNetAttributesExtractor; import io.opentelemetry.javaagent.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; public final class JdbcSingletons { diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/PreparedStatementInstrumentation.java b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/PreparedStatementInstrumentation.java index 9a704ce0e353..a198ecc5cb8a 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/PreparedStatementInstrumentation.java +++ b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/PreparedStatementInstrumentation.java @@ -16,6 +16,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.jdbc.internal.DbRequest; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap; diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/StatementInstrumentation.java b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/StatementInstrumentation.java index 06047d33ba02..3cbbcf857ddc 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/StatementInstrumentation.java +++ b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/StatementInstrumentation.java @@ -16,6 +16,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.jdbc.internal.DbRequest; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap; diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceInstrumentation.java b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceInstrumentation.java index e82eb1cb5cf7..bc5305bdfd61 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceInstrumentation.java +++ b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceInstrumentation.java @@ -5,8 +5,8 @@ package io.opentelemetry.javaagent.instrumentation.jdbc.datasource; +import static io.opentelemetry.instrumentation.jdbc.internal.DataSourceSingletons.instrumenter; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; -import static io.opentelemetry.javaagent.instrumentation.jdbc.datasource.DataSourceSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.named; import io.opentelemetry.context.Context; diff --git a/instrumentation/jdbc/javaagent/src/test/groovy/JdbcInstrumentationTest.groovy b/instrumentation/jdbc/javaagent/src/test/groovy/JdbcInstrumentationTest.groovy index 2cde833052ed..045d4b706c96 100644 --- a/instrumentation/jdbc/javaagent/src/test/groovy/JdbcInstrumentationTest.groovy +++ b/instrumentation/jdbc/javaagent/src/test/groovy/JdbcInstrumentationTest.groovy @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +import io.opentelemetry.instrumentation.jdbc.TestConnection +import io.opentelemetry.instrumentation.jdbc.TestDriver + import static io.opentelemetry.api.trace.SpanKind.CLIENT import static io.opentelemetry.api.trace.SpanKind.INTERNAL import static io.opentelemetry.instrumentation.test.utils.TraceUtils.basicSpan @@ -28,8 +31,6 @@ import org.h2.jdbcx.JdbcDataSource import org.hsqldb.jdbc.JDBCDriver import spock.lang.Shared import spock.lang.Unroll -import test.TestConnection -import test.TestDriver @Unroll class JdbcInstrumentationTest extends AgentInstrumentationSpecification { @@ -466,6 +467,7 @@ class JdbcInstrumentationTest extends AgentInstrumentationSpecification { when: try { connection = new TestConnection(true) + connection.url = "jdbc:testdb://localhost" } catch (Exception ignored) { connection = driver.connect(jdbcUrl, null) } @@ -580,6 +582,7 @@ class JdbcInstrumentationTest extends AgentInstrumentationSpecification { def "test getClientInfo exception"() { setup: Connection connection = new TestConnection(false) + connection.url = "jdbc:testdb://localhost" when: Statement statement = null @@ -720,6 +723,7 @@ class JdbcInstrumentationTest extends AgentInstrumentationSpecification { def "should handle recursive Statements inside Connection.getMetaData(): #desc"() { given: def connection = new DbCallingConnection(usePreparedStatementInConnection) + connection.url = "jdbc:testdb://localhost" when: runUnderTrace("parent") { diff --git a/instrumentation/jdbc/library/NOTICE.txt b/instrumentation/jdbc/library/NOTICE.txt new file mode 100644 index 000000000000..b46b353fc4b2 --- /dev/null +++ b/instrumentation/jdbc/library/NOTICE.txt @@ -0,0 +1,19 @@ +This product contains a modified part of OpenTracing JDBC Instrumentation: + + * License: + + Copyright 2017-2021 The OpenTracing Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + * Homepage: https://github.com/opentracing-contrib/java-jdbc diff --git a/instrumentation/jdbc/library/README.md b/instrumentation/jdbc/library/README.md new file mode 100644 index 000000000000..e4f5a56a06af --- /dev/null +++ b/instrumentation/jdbc/library/README.md @@ -0,0 +1,78 @@ +# Manual Instrumentation for JDBC + +Provides OpenTelemetry instrumentation for +[Java JDBC API](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/). + +## Quickstart + +### Add these dependencies to your project. + +Replace `OPENTELEMETRY_VERSION` with the latest stable +[release](https://mvnrepository.com/artifact/io.opentelemetry). `Minimum version: 1.4.0` + +For Maven add to your `pom.xml`: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-jdbc + OPENTELEMETRY_VERSION + + +``` + +For Gradle add to your dependencies: + +```groovy +implementation("io.opentelemetry.instrumentation:opentelemetry-jdbc:OPENTELEMETRY_VERSION") +``` + +##### Usage + +There are three possible ways to activate the OpenTelemetry JDBC instrumentation. The first way is more preferable for +DI frameworks which uses connection pools, as it wraps a `DataSource` with a special OpenTelemetry wrapper. The second +one requires to change the connection URL and switch to use a special OpenTelemetry driver. + +### Datasource way + +If your application uses a DataSource, simply wrap your current DataSource object with `OpenTelemetryDataSource`. +`OpenTelemetryDataSource` has a constructor method that accepts the `DataSource` to wrap. This is by far the simplest +method especially if you use a dependency injection (DI) frameworks such as +[Spring Framework](https://spring.io/projects/spring-framework), [Micronaut](https://micronaut.io), +[Quarkus](https://quarkus.io), or [Guice](https://github.com/google/guice). + +```java +import org.apache.commons.dbcp2.BasicDataSource; +import org.springframework.context.annotation.Configuration; +import io.opentelemetry.instrumentation.jdbc.datasource.OpenTelemetryDataSource; + +@Configuration +public class DataSourceConfig { + + @Bean + public DataSource dataSource() { + BasicDataSource dataSource = new BasicDataSource(); + dataSource.setDriverClassName("org.postgresql.Driver"); + dataSource.setUrl("jdbc:postgresql://127.0.0.1:5432/example"); + dataSource.setUsername("postgres"); + dataSource.setPassword("root"); + return new OpenTelemetryDataSource(dataSource); + } + +} +``` + +### Driver way + +1. Activate tracing for JDBC connections by setting `jdbc:otel:` prefix to the JDBC URL: + +``` +jdbc:otel:h2:mem:test +``` + +2. Set the driver class to `io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver` and initialize the driver with: + +```java +Class.forName("io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver"); +``` diff --git a/instrumentation/jdbc/library/build.gradle.kts b/instrumentation/jdbc/library/build.gradle.kts new file mode 100644 index 000000000000..e3689fbbfd01 --- /dev/null +++ b/instrumentation/jdbc/library/build.gradle.kts @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +plugins { + id("otel.library-instrumentation") +} + +dependencies { + implementation(project(":instrumentation-api")) + + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") + + implementation("org.slf4j:slf4j-api") + + testImplementation(project(":instrumentation:jdbc:testing")) +} diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/OpenTelemetryDriver.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/OpenTelemetryDriver.java new file mode 100644 index 000000000000..ac492c860213 --- /dev/null +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/OpenTelemetryDriver.java @@ -0,0 +1,202 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright 2017-2021 The OpenTracing Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package io.opentelemetry.instrumentation.jdbc; + +import io.opentelemetry.instrumentation.api.InstrumentationVersion; +import io.opentelemetry.instrumentation.jdbc.internal.DbInfo; +import io.opentelemetry.instrumentation.jdbc.internal.JdbcConnectionUrlParser; +import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection; +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.Collections; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** JDBC driver for OpenTelemetry. */ +public final class OpenTelemetryDriver implements Driver { + + // visible for testing + static final OpenTelemetryDriver INSTANCE = new OpenTelemetryDriver(); + + private static final int MAJOR_VERSION; + private static final int MINOR_VERSION; + + private static final String URL_PREFIX = "jdbc:otel:"; + private static final AtomicBoolean REGISTERED = new AtomicBoolean(); + + static { + try { + int[] version = parseInstrumentationVersion(); + MAJOR_VERSION = version[0]; + MINOR_VERSION = version[1]; + + register(); + } catch (SQLException e) { + throw new ExceptionInInitializerError(e); + } + } + + /** + * Register the driver against {@link DriverManager}. This is done automatically when the class is + * loaded. Dropping the driver from DriverManager's list is possible using {@link #deregister()} + * method. + * + * @throws IllegalStateException if the driver is already registered + * @throws SQLException if registering the driver fails + */ + public static void register() throws SQLException { + if (!REGISTERED.compareAndSet(false, true)) { + throw new IllegalStateException( + "Driver is already registered. It can only be registered once."); + } + DriverManager.registerDriver(INSTANCE); + } + + /** + * According to JDBC specification, this driver is registered against {@link DriverManager} when + * the class is loaded. To avoid leaks, this method allow unregistering the driver so that the + * class can be gc'ed if necessary. + * + * @throws IllegalStateException if the driver is not registered + * @throws SQLException if deregistering the driver fails + */ + public static void deregister() throws SQLException { + if (!REGISTERED.compareAndSet(true, false)) { + throw new IllegalStateException( + "Driver is not registered (or it has not been registered using Driver.register() method)"); + } + DriverManager.deregisterDriver(INSTANCE); + } + + /** Returns {@code true} if the driver is registered against {@link DriverManager}. */ + public static boolean isRegistered() { + return REGISTERED.get(); + } + + private static Driver findDriver(String realUrl) { + for (Driver candidate : Collections.list(DriverManager.getDrivers())) { + try { + if (!(candidate instanceof OpenTelemetryDriver) && candidate.acceptsURL(realUrl)) { + return candidate; + } + } catch (SQLException ignored) { + // intentionally ignore exception + } + } + + throw new IllegalStateException("Unable to find a driver that accepts url: " + realUrl); + } + + /** + * Parses out the real JDBC connection URL by removing "otel:" prefix. + * + * @param url the connection URL + * @return the parsed URL + */ + private static String extractRealUrl(String url) { + return url.startsWith(URL_PREFIX) ? url.replace(URL_PREFIX, "jdbc:") : url; + } + + private static int[] parseInstrumentationVersion() { + String[] parts = InstrumentationVersion.VERSION.split("\\."); + if (parts.length >= 2) { + try { + int majorVersion = Integer.parseInt(parts[0]); + int minorVersion = Integer.parseInt(parts[1]); + + return new int[] {majorVersion, minorVersion}; + } catch (NumberFormatException ignored) { + // ignore incorrect version + } + } + // return 0.0 as a fallback + return new int[] {0, 0}; + } + + @Nullable + @Override + public Connection connect(String url, Properties info) throws SQLException { + if (url == null || url.trim().isEmpty()) { + throw new IllegalArgumentException("url is required"); + } + + if (!acceptsURL(url)) { + return null; + } + + final String realUrl = extractRealUrl(url); + + // find the real driver for the URL + final Driver wrappedDriver = findDriver(realUrl); + + final Connection connection = wrappedDriver.connect(realUrl, info); + + final DbInfo dbInfo = JdbcConnectionUrlParser.parse(realUrl, info); + + return new OpenTelemetryConnection(connection, dbInfo); + } + + @Override + public boolean acceptsURL(String url) { + if (url == null) { + return false; + } + return url.startsWith(URL_PREFIX) && url.length() > URL_PREFIX.length(); + } + + @Override + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { + if (url == null || url.trim().isEmpty()) { + throw new IllegalArgumentException("url is required"); + } + + final String realUrl = extractRealUrl(url); + final Driver wrappedDriver = findDriver(realUrl); + return wrappedDriver.getPropertyInfo(realUrl, info); + } + + @Override + public int getMajorVersion() { + return MAJOR_VERSION; + } + + @Override + public int getMinorVersion() { + return MINOR_VERSION; + } + + /** Returns {@literal false} because not all delegated drivers are JDBC compliant. */ + @Override + public boolean jdbcCompliant() { + return false; + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + throw new SQLFeatureNotSupportedException("Feature not supported"); + } +} diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/datasource/OpenTelemetryDataSource.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/datasource/OpenTelemetryDataSource.java new file mode 100644 index 000000000000..398e70ff5f37 --- /dev/null +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/datasource/OpenTelemetryDataSource.java @@ -0,0 +1,131 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright 2017-2021 The OpenTracing Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package io.opentelemetry.instrumentation.jdbc.datasource; + +import static io.opentelemetry.instrumentation.jdbc.internal.DataSourceSingletons.instrumenter; +import static io.opentelemetry.instrumentation.jdbc.internal.JdbcUtils.computeDbInfo; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.jdbc.internal.DbInfo; +import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection; +import io.opentelemetry.instrumentation.jdbc.internal.ThrowingSupplier; +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.logging.Logger; +import javax.sql.DataSource; + +/** OpenTelemetry {@link DataSource} implementation. */ +public class OpenTelemetryDataSource implements DataSource, AutoCloseable { + + private final DataSource delegate; + + /** + * Create a OpenTelemetry DataSource wrapping another DataSource. This constructor is primarily + * used by dependency injection frameworks. + * + * @param delegate the DataSource to wrap + */ + public OpenTelemetryDataSource(DataSource delegate) { + this.delegate = delegate; + } + + @Override + public Connection getConnection() throws SQLException { + Connection connection = wrapCall(delegate::getConnection); + DbInfo dbInfo = computeDbInfo(connection); + return new OpenTelemetryConnection(connection, dbInfo); + } + + @Override + public Connection getConnection(final String username, final String password) + throws SQLException { + Connection connection = wrapCall(() -> delegate.getConnection(username, password)); + DbInfo dbInfo = computeDbInfo(connection); + return new OpenTelemetryConnection(connection, dbInfo); + } + + @Override + public PrintWriter getLogWriter() throws SQLException { + return delegate.getLogWriter(); + } + + @Override + public void setLogWriter(final PrintWriter out) throws SQLException { + delegate.setLogWriter(out); + } + + @Override + public int getLoginTimeout() throws SQLException { + return delegate.getLoginTimeout(); + } + + @Override + public void setLoginTimeout(final int seconds) throws SQLException { + delegate.setLoginTimeout(seconds); + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return delegate.getParentLogger(); + } + + @Override + public T unwrap(final Class iface) throws SQLException { + return delegate.unwrap(iface); + } + + @Override + public boolean isWrapperFor(final Class iface) throws SQLException { + return delegate.isWrapperFor(iface); + } + + @Override + public void close() throws Exception { + if (delegate instanceof AutoCloseable) { + ((AutoCloseable) delegate).close(); + } + } + + private T wrapCall(ThrowingSupplier callable) throws E { + Context parentContext = Context.current(); + + if (!Span.fromContext(parentContext).getSpanContext().isValid()) { + // this instrumentation is already very noisy, and calls to getConnection outside of an + // existing trace do not tend to be very interesting + return callable.call(); + } + + Context context = instrumenter().start(parentContext, delegate); + T result; + try (Scope ignored = context.makeCurrent()) { + result = callable.call(); + } catch (Throwable t) { + instrumenter().end(context, delegate, null, t); + throw t; + } + instrumenter().end(context, delegate, null, null); + return result; + } +} diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceCodeAttributesExtractor.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DataSourceCodeAttributesExtractor.java similarity index 91% rename from instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceCodeAttributesExtractor.java rename to instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DataSourceCodeAttributesExtractor.java index a4a96fe1f0cb..5792a245c2d1 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceCodeAttributesExtractor.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DataSourceCodeAttributesExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.jdbc.datasource; +package io.opentelemetry.instrumentation.jdbc.internal; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; import javax.sql.DataSource; diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceSingletons.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DataSourceSingletons.java similarity index 94% rename from instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceSingletons.java rename to instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DataSourceSingletons.java index 1338b80fa024..c66d86de294d 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceSingletons.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DataSourceSingletons.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.jdbc.datasource; +package io.opentelemetry.instrumentation.jdbc.internal; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/DbInfo.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DbInfo.java similarity index 92% rename from instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/DbInfo.java rename to instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DbInfo.java index a0aa44019235..c704b1a1baaf 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/DbInfo.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DbInfo.java @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.jdbc; +package io.opentelemetry.instrumentation.jdbc.internal; import com.google.auto.value.AutoValue; -import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; @AutoValue public abstract class DbInfo { diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/DbRequest.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DbRequest.java similarity index 80% rename from instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/DbRequest.java rename to instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DbRequest.java index 94d24aa1bb12..829d24b4e5fe 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/DbRequest.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DbRequest.java @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.jdbc; +package io.opentelemetry.instrumentation.jdbc.internal; -import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcUtils.connectionFromStatement; -import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcUtils.extractDbInfo; +import static io.opentelemetry.instrumentation.jdbc.internal.JdbcUtils.connectionFromStatement; +import static io.opentelemetry.instrumentation.jdbc.internal.JdbcUtils.extractDbInfo; import com.google.auto.value.AutoValue; import java.sql.Connection; diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcAttributesExtractor.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcAttributesExtractor.java similarity index 88% rename from instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcAttributesExtractor.java rename to instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcAttributesExtractor.java index 8647eea54b00..38831e01bfdc 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcAttributesExtractor.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcAttributesExtractor.java @@ -3,14 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.jdbc; +package io.opentelemetry.instrumentation.jdbc.internal; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.instrumentation.api.instrumenter.db.SqlAttributesExtractor; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import org.checkerframework.checker.nullness.qual.Nullable; -final class JdbcAttributesExtractor extends SqlAttributesExtractor { +public final class JdbcAttributesExtractor extends SqlAttributesExtractor { @Nullable @Override protected String system(DbRequest request) { diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcConnectionUrlParser.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParser.java similarity index 99% rename from instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcConnectionUrlParser.java rename to instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParser.java index 1e1a03bf944b..5566f384ba63 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcConnectionUrlParser.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParser.java @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.jdbc; +package io.opentelemetry.instrumentation.jdbc.internal; -import static io.opentelemetry.javaagent.instrumentation.jdbc.DbInfo.DEFAULT; +import static io.opentelemetry.instrumentation.jdbc.internal.DbInfo.DEFAULT; import static java.util.regex.Pattern.CASE_INSENSITIVE; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues; diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcMaps.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcMaps.java similarity index 91% rename from instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcMaps.java rename to instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcMaps.java index 9e2f472e0dc7..5be37d687e65 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcMaps.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcMaps.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.jdbc; +package io.opentelemetry.instrumentation.jdbc.internal; import io.opentelemetry.instrumentation.api.caching.Cache; import java.sql.Connection; diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcNetAttributesExtractor.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcNetAttributesExtractor.java similarity index 82% rename from instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcNetAttributesExtractor.java rename to instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcNetAttributesExtractor.java index f4efad618ca3..c0b9bd67525c 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcNetAttributesExtractor.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcNetAttributesExtractor.java @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.jdbc; +package io.opentelemetry.instrumentation.jdbc.internal; import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor; import org.checkerframework.checker.nullness.qual.Nullable; -final class JdbcNetAttributesExtractor extends NetAttributesExtractor { +public final class JdbcNetAttributesExtractor extends NetAttributesExtractor { @Nullable @Override diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcSingletons.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcSingletons.java new file mode 100644 index 000000000000..8a16ae4b2c32 --- /dev/null +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcSingletons.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.jdbc.internal; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.db.DbAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.db.DbSpanNameExtractor; + +public final class JdbcSingletons { + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jdbc"; + + private static final Instrumenter INSTRUMENTER; + + static { + DbAttributesExtractor dbAttributesExtractor = new JdbcAttributesExtractor(); + SpanNameExtractor spanName = DbSpanNameExtractor.create(dbAttributesExtractor); + JdbcNetAttributesExtractor netAttributesExtractor = new JdbcNetAttributesExtractor(); + + INSTRUMENTER = + Instrumenter.newBuilder( + GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, spanName) + .addAttributesExtractor(dbAttributesExtractor) + .addAttributesExtractor(netAttributesExtractor) + .newInstrumenter(SpanKindExtractor.alwaysClient()); + } + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private JdbcSingletons() {} +} diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcUtils.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcUtils.java similarity index 96% rename from instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcUtils.java rename to instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcUtils.java index a4337faff4a4..e3e089fdc0b9 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcUtils.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcUtils.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.jdbc; +package io.opentelemetry.instrumentation.jdbc.internal; import java.lang.reflect.Field; import java.sql.Connection; @@ -68,7 +68,7 @@ public static DbInfo extractDbInfo(Connection connection) { return JdbcMaps.connectionInfo.computeIfAbsent(connection, JdbcUtils::computeDbInfo); } - private static DbInfo computeDbInfo(Connection connection) { + public static DbInfo computeDbInfo(Connection connection) { /* * Logic to get the DBInfo from a JDBC Connection, if the connection was not created via * Driver.connect, or it has never seen before, the connectionInfo map will return null and will diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryCallableStatement.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryCallableStatement.java new file mode 100644 index 000000000000..e92366a39b27 --- /dev/null +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryCallableStatement.java @@ -0,0 +1,702 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright 2017-2021 The OpenTracing Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package io.opentelemetry.instrumentation.jdbc.internal; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Ref; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Map; + +public class OpenTelemetryCallableStatement + extends OpenTelemetryPreparedStatement implements CallableStatement { + + public OpenTelemetryCallableStatement(S delegate, DbInfo dbInfo, String query) { + super(delegate, dbInfo, query); + } + + @Override + public boolean wasNull() throws SQLException { + return delegate.wasNull(); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public String getString(int parameterIndex) throws SQLException { + return delegate.getString(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public String getString(String parameterName) throws SQLException { + return delegate.getString(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public boolean getBoolean(int parameterIndex) throws SQLException { + return delegate.getBoolean(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public boolean getBoolean(String parameterName) throws SQLException { + return delegate.getBoolean(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public byte getByte(int parameterIndex) throws SQLException { + return delegate.getByte(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public byte getByte(String parameterName) throws SQLException { + return delegate.getByte(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public short getShort(int parameterIndex) throws SQLException { + return delegate.getShort(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public short getShort(String parameterName) throws SQLException { + return delegate.getShort(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public int getInt(int parameterIndex) throws SQLException { + return delegate.getInt(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public int getInt(String parameterName) throws SQLException { + return delegate.getInt(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public long getLong(int parameterIndex) throws SQLException { + return delegate.getLong(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public long getLong(String parameterName) throws SQLException { + return delegate.getLong(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public float getFloat(int parameterIndex) throws SQLException { + return delegate.getFloat(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public float getFloat(String parameterName) throws SQLException { + return delegate.getFloat(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public double getDouble(int parameterIndex) throws SQLException { + return delegate.getDouble(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public double getDouble(String parameterName) throws SQLException { + return delegate.getDouble(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public byte[] getBytes(int parameterIndex) throws SQLException { + return delegate.getBytes(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public byte[] getBytes(String parameterName) throws SQLException { + return delegate.getBytes(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Time getTime(int parameterIndex) throws SQLException { + return delegate.getTime(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Time getTime(String parameterName, Calendar cal) throws SQLException { + return delegate.getTime(parameterName, cal); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Time getTime(int parameterIndex, Calendar cal) throws SQLException { + return delegate.getTime(parameterIndex, cal); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Time getTime(String parameterName) throws SQLException { + return delegate.getTime(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Timestamp getTimestamp(int parameterIndex) throws SQLException { + return delegate.getTimestamp(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException { + return delegate.getTimestamp(parameterName, cal); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Timestamp getTimestamp(String parameterName) throws SQLException { + return delegate.getTimestamp(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException { + return delegate.getTimestamp(parameterIndex, cal); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Object getObject(int parameterIndex) throws SQLException { + return delegate.getObject(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Object getObject(int parameterIndex, Map> map) throws SQLException { + return delegate.getObject(parameterIndex, map); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Object getObject(String parameterName) throws SQLException { + return delegate.getObject(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public T getObject(int parameterIndex, Class type) throws SQLException { + return delegate.getObject(parameterIndex, type); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public T getObject(String parameterName, Class type) throws SQLException { + return delegate.getObject(parameterName, type); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Object getObject(String parameterName, Map> map) throws SQLException { + return delegate.getObject(parameterName, map); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Ref getRef(int parameterIndex) throws SQLException { + return delegate.getRef(parameterIndex); + } + + @Override + public Ref getRef(String parameterName) throws SQLException { + return delegate.getRef(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Blob getBlob(int parameterIndex) throws SQLException { + return delegate.getBlob(parameterIndex); + } + + @Override + public Blob getBlob(String parameterName) throws SQLException { + return delegate.getBlob(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Clob getClob(int parameterIndex) throws SQLException { + return delegate.getClob(parameterIndex); + } + + @Override + public Clob getClob(String parameterName) throws SQLException { + return delegate.getClob(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Array getArray(int parameterIndex) throws SQLException { + return delegate.getArray(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Array getArray(String parameterName) throws SQLException { + return delegate.getArray(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void registerOutParameter(int parameterIndex, int sqlType, String typeName) + throws SQLException { + delegate.registerOutParameter(parameterIndex, sqlType, typeName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void registerOutParameter(String parameterName, int sqlType) throws SQLException { + delegate.registerOutParameter(parameterName, sqlType); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void registerOutParameter(String parameterName, int sqlType, int scale) + throws SQLException { + delegate.registerOutParameter(parameterName, sqlType, scale); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void registerOutParameter(String parameterName, int sqlType, String typeName) + throws SQLException { + delegate.registerOutParameter(parameterName, sqlType, typeName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { + delegate.registerOutParameter(parameterIndex, sqlType); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { + delegate.registerOutParameter(parameterIndex, sqlType, scale); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public URL getURL(int parameterIndex) throws SQLException { + return delegate.getURL(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public URL getURL(String parameterName) throws SQLException { + return delegate.getURL(parameterName); + } + + @Override + public void setURL(String parameterName, URL val) throws SQLException { + delegate.setURL(parameterName, val); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNull(String parameterName, int sqlType) throws SQLException { + delegate.setNull(parameterName, sqlType); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNull(String parameterName, int sqlType, String typeName) throws SQLException { + delegate.setNull(parameterName, sqlType, typeName); + } + + @Override + public void setBoolean(String parameterName, boolean x) throws SQLException { + delegate.setBoolean(parameterName, x); + } + + @Override + public void setByte(String parameterName, byte x) throws SQLException { + delegate.setByte(parameterName, x); + } + + @Override + public void setShort(String parameterName, short x) throws SQLException { + delegate.setShort(parameterName, x); + } + + @Override + public void setInt(String parameterName, int x) throws SQLException { + delegate.setInt(parameterName, x); + } + + @Override + public void setLong(String parameterName, long x) throws SQLException { + delegate.setLong(parameterName, x); + } + + @Override + public void setFloat(String parameterName, float x) throws SQLException { + delegate.setFloat(parameterName, x); + } + + @Override + public void setDouble(String parameterName, double x) throws SQLException { + delegate.setDouble(parameterName, x); + } + + @Override + public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException { + delegate.setBigDecimal(parameterName, x); + } + + @Override + public void setString(String parameterName, String x) throws SQLException { + delegate.setString(parameterName, x); + } + + @Override + public void setBytes(String parameterName, byte[] x) throws SQLException { + delegate.setBytes(parameterName, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setDate(String parameterName, Date x) throws SQLException { + delegate.setDate(parameterName, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setDate(String parameterName, Date x, Calendar cal) throws SQLException { + delegate.setDate(parameterName, x, cal); + } + + @Override + public void setTime(String parameterName, Time x) throws SQLException { + delegate.setTime(parameterName, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setTime(String parameterName, Time x, Calendar cal) throws SQLException { + delegate.setTime(parameterName, x, cal); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setTimestamp(String parameterName, Timestamp x) throws SQLException { + delegate.setTimestamp(parameterName, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException { + delegate.setTimestamp(parameterName, x, cal); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException { + delegate.setAsciiStream(parameterName, x, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException { + delegate.setAsciiStream(parameterName, x, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setAsciiStream(String parameterName, InputStream x) throws SQLException { + delegate.setAsciiStream(parameterName, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException { + delegate.setBinaryStream(parameterName, x, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setBinaryStream(String parameterName, InputStream x, long length) + throws SQLException { + delegate.setBinaryStream(parameterName, x, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setBinaryStream(String parameterName, InputStream x) throws SQLException { + delegate.setBinaryStream(parameterName, x); + } + + @Override + public void setObject(String parameterName, Object x, int targetSqlType, int scale) + throws SQLException { + delegate.setObject(parameterName, x, targetSqlType, scale); + } + + @Override + public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException { + delegate.setObject(parameterName, x, targetSqlType); + } + + @Override + public void setObject(String parameterName, Object x) throws SQLException { + delegate.setObject(parameterName, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setCharacterStream(String parameterName, Reader reader, int length) + throws SQLException { + delegate.setCharacterStream(parameterName, reader, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setCharacterStream(String parameterName, Reader reader, long length) + throws SQLException { + delegate.setCharacterStream(parameterName, reader, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setCharacterStream(String parameterName, Reader reader) throws SQLException { + delegate.setCharacterStream(parameterName, reader); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Date getDate(String parameterName) throws SQLException { + return delegate.getDate(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Date getDate(int parameterIndex, Calendar cal) throws SQLException { + return delegate.getDate(parameterIndex, cal); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Date getDate(int parameterIndex) throws SQLException { + return delegate.getDate(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public Date getDate(String parameterName, Calendar cal) throws SQLException { + return delegate.getDate(parameterName, cal); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public BigDecimal getBigDecimal(String parameterName) throws SQLException { + return delegate.getBigDecimal(parameterName); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { + return delegate.getBigDecimal(parameterIndex); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + @Deprecated + public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { + return delegate.getBigDecimal(parameterIndex, scale); + } + + @Override + public RowId getRowId(int parameterIndex) throws SQLException { + return delegate.getRowId(parameterIndex); + } + + @Override + public RowId getRowId(String parameterName) throws SQLException { + return delegate.getRowId(parameterName); + } + + @Override + public void setRowId(String parameterName, RowId x) throws SQLException { + delegate.setRowId(parameterName, x); + } + + @Override + public void setNString(String parameterName, String value) throws SQLException { + delegate.setNString(parameterName, value); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNCharacterStream(String parameterName, Reader value, long length) + throws SQLException { + delegate.setNCharacterStream(parameterName, value, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNCharacterStream(String parameterName, Reader value) throws SQLException { + delegate.setNCharacterStream(parameterName, value); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNClob(String parameterName, NClob value) throws SQLException { + delegate.setNClob(parameterName, value); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNClob(String parameterName, Reader reader) throws SQLException { + delegate.setNClob(parameterName, reader); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNClob(String parameterName, Reader reader, long length) throws SQLException { + delegate.setNClob(parameterName, reader, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setClob(String parameterName, Reader reader, long length) throws SQLException { + delegate.setClob(parameterName, reader, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setClob(String parameterName, Reader reader) throws SQLException { + delegate.setClob(parameterName, reader); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setClob(String parameterName, Clob x) throws SQLException { + delegate.setClob(parameterName, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setBlob(String parameterName, InputStream inputStream, long length) + throws SQLException { + delegate.setBlob(parameterName, inputStream, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setBlob(String parameterName, InputStream inputStream) throws SQLException { + delegate.setBlob(parameterName, inputStream); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setBlob(String parameterName, Blob x) throws SQLException { + delegate.setBlob(parameterName, x); + } + + @Override + public NClob getNClob(int parameterIndex) throws SQLException { + return delegate.getNClob(parameterIndex); + } + + @Override + public NClob getNClob(String parameterName) throws SQLException { + return delegate.getNClob(parameterName); + } + + @Override + public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { + delegate.setSQLXML(parameterName, xmlObject); + } + + @Override + public SQLXML getSQLXML(int parameterIndex) throws SQLException { + return delegate.getSQLXML(parameterIndex); + } + + @Override + public SQLXML getSQLXML(String parameterName) throws SQLException { + return delegate.getSQLXML(parameterName); + } + + @Override + public String getNString(int parameterIndex) throws SQLException { + return delegate.getNString(parameterIndex); + } + + @Override + public String getNString(String parameterName) throws SQLException { + return delegate.getNString(parameterName); + } + + @Override + public Reader getNCharacterStream(int parameterIndex) throws SQLException { + return delegate.getNCharacterStream(parameterIndex); + } + + @Override + public Reader getNCharacterStream(String parameterName) throws SQLException { + return delegate.getNCharacterStream(parameterName); + } + + @Override + public Reader getCharacterStream(int parameterIndex) throws SQLException { + return delegate.getCharacterStream(parameterIndex); + } + + @Override + public Reader getCharacterStream(String parameterName) throws SQLException { + return delegate.getCharacterStream(parameterName); + } +} diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryConnection.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryConnection.java new file mode 100644 index 000000000000..298b8302ba4e --- /dev/null +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryConnection.java @@ -0,0 +1,355 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright 2017-2021 The OpenTracing Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package io.opentelemetry.instrumentation.jdbc.internal; + +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +public class OpenTelemetryConnection implements Connection { + + private final Connection delegate; + private final DbInfo dbInfo; + + public OpenTelemetryConnection(Connection delegate, DbInfo dbInfo) { + this.delegate = delegate; + this.dbInfo = dbInfo; + } + + @Override + public Statement createStatement() throws SQLException { + final Statement statement = delegate.createStatement(); + return new OpenTelemetryStatement<>(statement, dbInfo); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) + throws SQLException { + final Statement statement = delegate.createStatement(resultSetType, resultSetConcurrency); + return new OpenTelemetryStatement<>(statement, dbInfo); + } + + @Override + public Statement createStatement( + int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + final Statement statement = + delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + return new OpenTelemetryStatement<>(statement, dbInfo); + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + final PreparedStatement statement = delegate.prepareStatement(sql); + return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) + throws SQLException { + final PreparedStatement statement = + delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); + return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); + } + + @Override + public PreparedStatement prepareStatement( + String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + final PreparedStatement statement = + delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + final PreparedStatement statement = delegate.prepareStatement(sql, autoGeneratedKeys); + return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + final PreparedStatement statement = delegate.prepareStatement(sql, columnIndexes); + return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + final PreparedStatement statement = delegate.prepareStatement(sql, columnNames); + return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + final CallableStatement statement = delegate.prepareCall(sql); + return new OpenTelemetryCallableStatement<>(statement, dbInfo, sql); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) + throws SQLException { + final CallableStatement statement = + delegate.prepareCall(sql, resultSetType, resultSetConcurrency); + return new OpenTelemetryCallableStatement<>(statement, dbInfo, sql); + } + + @Override + public CallableStatement prepareCall( + String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + final CallableStatement statement = + delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + return new OpenTelemetryCallableStatement(statement, dbInfo, sql); + } + + @Override + public void commit() throws SQLException { + delegate.commit(); + } + + @Override + public void close() throws SQLException { + delegate.close(); + } + + @Override + public String nativeSQL(String sql) throws SQLException { + return delegate.nativeSQL(sql); + } + + @Override + public boolean getAutoCommit() throws SQLException { + return delegate.getAutoCommit(); + } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + delegate.setAutoCommit(autoCommit); + } + + @Override + public boolean isClosed() throws SQLException { + return delegate.isClosed(); + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return delegate.getMetaData(); + } + + @Override + public boolean isReadOnly() throws SQLException { + return delegate.isReadOnly(); + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + delegate.setReadOnly(readOnly); + } + + @Override + public String getCatalog() throws SQLException { + return delegate.getCatalog(); + } + + @Override + public void setCatalog(String catalog) throws SQLException { + delegate.setCatalog(catalog); + } + + @Override + public int getTransactionIsolation() throws SQLException { + return delegate.getTransactionIsolation(); + } + + @Override + public void setTransactionIsolation(int level) throws SQLException { + delegate.setTransactionIsolation(level); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return delegate.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + delegate.clearWarnings(); + } + + @Override + public Map> getTypeMap() throws SQLException { + return delegate.getTypeMap(); + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + delegate.setTypeMap(map); + } + + @Override + public int getHoldability() throws SQLException { + return delegate.getHoldability(); + } + + @Override + public void setHoldability(int holdability) throws SQLException { + delegate.setHoldability(holdability); + } + + @Override + public Savepoint setSavepoint() throws SQLException { + return delegate.setSavepoint(); + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + return delegate.setSavepoint(name); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void rollback() throws SQLException { + delegate.rollback(); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void rollback(Savepoint savepoint) throws SQLException { + delegate.rollback(savepoint); + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + delegate.releaseSavepoint(savepoint); + } + + @Override + public Clob createClob() throws SQLException { + return delegate.createClob(); + } + + @Override + public Blob createBlob() throws SQLException { + return delegate.createBlob(); + } + + @Override + public NClob createNClob() throws SQLException { + return delegate.createNClob(); + } + + @Override + public SQLXML createSQLXML() throws SQLException { + return delegate.createSQLXML(); + } + + @Override + public boolean isValid(int timeout) throws SQLException { + return delegate.isValid(timeout); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + delegate.setClientInfo(name, value); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + delegate.setClientInfo(properties); + } + + @Override + public String getClientInfo(String name) throws SQLException { + return delegate.getClientInfo(name); + } + + @Override + public Properties getClientInfo() throws SQLException { + return delegate.getClientInfo(); + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return delegate.createArrayOf(typeName, elements); + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return delegate.createStruct(typeName, attributes); + } + + @Override + public String getSchema() throws SQLException { + return delegate.getSchema(); + } + + @Override + public void setSchema(String schema) throws SQLException { + delegate.setSchema(schema); + } + + @Override + public void abort(Executor executor) throws SQLException { + delegate.abort(executor); + } + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + delegate.setNetworkTimeout(executor, milliseconds); + } + + @Override + public int getNetworkTimeout() throws SQLException { + return delegate.getNetworkTimeout(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return delegate.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return delegate.isWrapperFor(iface); + } + + // visible for testing + public DbInfo getDbInfo() { + return dbInfo; + } +} diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryPreparedStatement.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryPreparedStatement.java new file mode 100644 index 000000000000..0e127aa57e87 --- /dev/null +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryPreparedStatement.java @@ -0,0 +1,355 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright 2017-2021 The OpenTracing Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package io.opentelemetry.instrumentation.jdbc.internal; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; + +public class OpenTelemetryPreparedStatement + extends OpenTelemetryStatement implements PreparedStatement { + + public OpenTelemetryPreparedStatement(S delegate, DbInfo dbInfo, String query) { + super(delegate, dbInfo, query); + } + + @Override + public ResultSet executeQuery() throws SQLException { + return wrapCall(query, delegate::executeQuery); + } + + @Override + public int executeUpdate() throws SQLException { + return wrapCall(query, delegate::executeUpdate); + } + + @Override + public boolean execute() throws SQLException { + return wrapCall(query, delegate::execute); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNull(int parameterIndex, int sqlType) throws SQLException { + delegate.setNull(parameterIndex, sqlType); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + delegate.setNull(parameterIndex, sqlType, typeName); + } + + @Override + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + delegate.setBoolean(parameterIndex, x); + } + + @Override + public void setByte(int parameterIndex, byte x) throws SQLException { + delegate.setByte(parameterIndex, x); + } + + @Override + public void setShort(int parameterIndex, short x) throws SQLException { + delegate.setShort(parameterIndex, x); + } + + @Override + public void setInt(int parameterIndex, int x) throws SQLException { + delegate.setInt(parameterIndex, x); + } + + @Override + public void setLong(int parameterIndex, long x) throws SQLException { + delegate.setLong(parameterIndex, x); + } + + @Override + public void setFloat(int parameterIndex, float x) throws SQLException { + delegate.setFloat(parameterIndex, x); + } + + @Override + public void setDouble(int parameterIndex, double x) throws SQLException { + delegate.setDouble(parameterIndex, x); + } + + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + delegate.setBigDecimal(parameterIndex, x); + } + + @Override + public void setString(int parameterIndex, String x) throws SQLException { + delegate.setString(parameterIndex, x); + } + + @Override + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + delegate.setBytes(parameterIndex, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setDate(int parameterIndex, Date x) throws SQLException { + delegate.setDate(parameterIndex, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + delegate.setDate(parameterIndex, x, cal); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setTime(int parameterIndex, Time x) throws SQLException { + delegate.setTime(parameterIndex, x); + } + + @Override + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + delegate.setTime(parameterIndex, x, cal); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + delegate.setTimestamp(parameterIndex, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + delegate.setTimestamp(parameterIndex, x, cal); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + delegate.setAsciiStream(parameterIndex, x, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + delegate.setAsciiStream(parameterIndex, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + delegate.setAsciiStream(parameterIndex, x, length); + } + + @Override + @Deprecated + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + delegate.setUnicodeStream(parameterIndex, x, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + delegate.setBinaryStream(parameterIndex, x, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + delegate.setBinaryStream(parameterIndex, x, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + delegate.setBinaryStream(parameterIndex, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + delegate.setObject(parameterIndex, x, targetSqlType); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setObject(int parameterIndex, Object x) throws SQLException { + delegate.setObject(parameterIndex, x); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) + throws SQLException { + delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void addBatch() throws SQLException { + delegate.addBatch(); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) + throws SQLException { + delegate.setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + delegate.setCharacterStream(parameterIndex, reader); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setCharacterStream(int parameterIndex, Reader reader, long length) + throws SQLException { + delegate.setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setRef(int parameterIndex, Ref x) throws SQLException { + delegate.setRef(parameterIndex, x); + } + + @Override + public void setBlob(int parameterIndex, Blob x) throws SQLException { + delegate.setBlob(parameterIndex, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + delegate.setBlob(parameterIndex, inputStream); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream, long length) + throws SQLException { + delegate.setBlob(parameterIndex, inputStream, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setClob(int parameterIndex, Clob x) throws SQLException { + delegate.setClob(parameterIndex, x); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setClob(int parameterIndex, Reader reader) throws SQLException { + delegate.setClob(parameterIndex, reader); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + delegate.setClob(parameterIndex, reader, length); + } + + @Override + public void setArray(int parameterIndex, Array x) throws SQLException { + delegate.setArray(parameterIndex, x); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + return delegate.getMetaData(); + } + + @Override + public void setURL(int parameterIndex, URL x) throws SQLException { + delegate.setURL(parameterIndex, x); + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + return delegate.getParameterMetaData(); + } + + @Override + public void setRowId(int parameterIndex, RowId x) throws SQLException { + delegate.setRowId(parameterIndex, x); + } + + @Override + public void setNString(int parameterIndex, String value) throws SQLException { + delegate.setNString(parameterIndex, value); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNCharacterStream(int parameterIndex, Reader value, long length) + throws SQLException { + delegate.setNCharacterStream(parameterIndex, value, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + delegate.setNCharacterStream(parameterIndex, value); + } + + @Override + public void setNClob(int parameterIndex, NClob value) throws SQLException { + delegate.setNClob(parameterIndex, value); + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + delegate.setNClob(parameterIndex, reader, length); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + delegate.setNClob(parameterIndex, reader); + } + + @Override + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + delegate.setSQLXML(parameterIndex, xmlObject); + } + + @Override + public void clearParameters() throws SQLException { + delegate.clearParameters(); + } +} diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryStatement.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryStatement.java new file mode 100644 index 000000000000..08fbcdded199 --- /dev/null +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryStatement.java @@ -0,0 +1,308 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright 2017-2021 The OpenTracing Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package io.opentelemetry.instrumentation.jdbc.internal; + +import static io.opentelemetry.instrumentation.jdbc.internal.JdbcSingletons.instrumenter; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; +import java.util.ArrayList; + +public class OpenTelemetryStatement implements Statement { + + protected final S delegate; + protected final DbInfo dbInfo; + protected final String query; + + private final ArrayList batchCommands = new ArrayList<>(); + + OpenTelemetryStatement(S delegate, DbInfo dbInfo) { + this(delegate, dbInfo, null); + } + + OpenTelemetryStatement(S delegate, DbInfo dbInfo, String query) { + this.delegate = delegate; + this.dbInfo = dbInfo; + this.query = query; + } + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + return wrapCall(sql, () -> delegate.executeQuery(sql)); + } + + @Override + public int executeUpdate(String sql) throws SQLException { + return wrapCall(sql, () -> delegate.executeUpdate(sql)); + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return wrapCall(sql, () -> delegate.executeUpdate(sql, autoGeneratedKeys)); + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + return wrapCall(sql, () -> delegate.executeUpdate(sql, columnIndexes)); + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + return wrapCall(sql, () -> delegate.executeUpdate(sql, columnNames)); + } + + @Override + public boolean execute(String sql) throws SQLException { + return wrapCall(sql, () -> delegate.execute(sql)); + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + return wrapCall(sql, () -> delegate.execute(sql, autoGeneratedKeys)); + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + return wrapCall(sql, () -> delegate.execute(sql, columnIndexes)); + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + return wrapCall(sql, () -> delegate.execute(sql, columnNames)); + } + + @Override + public int[] executeBatch() throws SQLException { + return wrapCall(buildSqlForBatch(), delegate::executeBatch); + } + + @Override + public void close() throws SQLException { + delegate.close(); + } + + @Override + public int getMaxFieldSize() throws SQLException { + return delegate.getMaxFieldSize(); + } + + @Override + public void setMaxFieldSize(int max) throws SQLException { + delegate.setMaxFieldSize(max); + } + + @Override + public int getMaxRows() throws SQLException { + return delegate.getMaxRows(); + } + + @Override + public void setMaxRows(int max) throws SQLException { + delegate.setMaxRows(max); + } + + @Override + public void setEscapeProcessing(boolean enable) throws SQLException { + delegate.setEscapeProcessing(enable); + } + + @Override + public int getQueryTimeout() throws SQLException { + return delegate.getQueryTimeout(); + } + + @Override + public void setQueryTimeout(int seconds) throws SQLException { + delegate.setQueryTimeout(seconds); + } + + @Override + public void cancel() throws SQLException { + delegate.cancel(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return delegate.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + delegate.clearWarnings(); + } + + @Override + public void setCursorName(String name) throws SQLException { + delegate.setCursorName(name); + } + + @Override + public ResultSet getResultSet() throws SQLException { + return delegate.getResultSet(); + } + + @Override + public int getUpdateCount() throws SQLException { + return delegate.getUpdateCount(); + } + + @Override + public boolean getMoreResults() throws SQLException { + return delegate.getMoreResults(); + } + + @SuppressWarnings("UngroupedOverloads") + @Override + public boolean getMoreResults(int current) throws SQLException { + return delegate.getMoreResults(current); + } + + @Override + public int getFetchDirection() throws SQLException { + return delegate.getFetchDirection(); + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + delegate.setFetchDirection(direction); + } + + @Override + public int getFetchSize() throws SQLException { + return delegate.getFetchSize(); + } + + @Override + public void setFetchSize(int rows) throws SQLException { + delegate.setFetchSize(rows); + } + + @Override + public int getResultSetConcurrency() throws SQLException { + return delegate.getResultSetConcurrency(); + } + + @Override + public int getResultSetType() throws SQLException { + return delegate.getResultSetType(); + } + + @Override + public void addBatch(String sql) throws SQLException { + delegate.addBatch(sql); + batchCommands.add(sql); + } + + @Override + public void clearBatch() throws SQLException { + delegate.clearBatch(); + batchCommands.clear(); + } + + @Override + public Connection getConnection() throws SQLException { + return delegate.getConnection(); + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + return delegate.getGeneratedKeys(); + } + + @Override + public int getResultSetHoldability() throws SQLException { + return delegate.getResultSetHoldability(); + } + + @Override + public boolean isClosed() throws SQLException { + return delegate.isClosed(); + } + + @Override + public boolean isPoolable() throws SQLException { + return delegate.isPoolable(); + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + delegate.setPoolable(poolable); + } + + @Override + public void closeOnCompletion() throws SQLException { + delegate.closeOnCompletion(); + } + + @Override + public boolean isCloseOnCompletion() throws SQLException { + return delegate.isCloseOnCompletion(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return delegate.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return delegate.isWrapperFor(iface); + } + + protected T wrapCall(String sql, ThrowingSupplier callable) + throws E { + Context parentContext = Context.current(); + DbRequest request = DbRequest.create(dbInfo, sql); + + if (!instrumenter().shouldStart(parentContext, request)) { + return callable.call(); + } + + Context context = instrumenter().start(parentContext, request); + T result; + try (Scope ignored = context.makeCurrent()) { + result = callable.call(); + } catch (Throwable t) { + instrumenter().end(context, request, null, t); + throw t; + } + instrumenter().end(context, request, null, null); + return result; + } + + private String buildSqlForBatch() { + StringBuilder sqlBuilder = new StringBuilder(); + if (query != null) { + sqlBuilder.append(query); + } + + for (String batchCommand : batchCommands) { + sqlBuilder.append(batchCommand); + } + + return sqlBuilder.toString(); + } +} diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/ThrowingSupplier.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/ThrowingSupplier.java new file mode 100644 index 000000000000..9fed1175ac8e --- /dev/null +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/ThrowingSupplier.java @@ -0,0 +1,12 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.jdbc.internal; + +@FunctionalInterface +public interface ThrowingSupplier { + + T call() throws E; +} diff --git a/instrumentation/jdbc/library/src/main/resources/META-INF/services/java.sql.Driver b/instrumentation/jdbc/library/src/main/resources/META-INF/services/java.sql.Driver new file mode 100644 index 000000000000..39bfb3d82f9d --- /dev/null +++ b/instrumentation/jdbc/library/src/main/resources/META-INF/services/java.sql.Driver @@ -0,0 +1 @@ +io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver diff --git a/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/OpenTelemetryConnectionTest.groovy b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/OpenTelemetryConnectionTest.groovy new file mode 100644 index 000000000000..ab9c21414d29 --- /dev/null +++ b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/OpenTelemetryConnectionTest.groovy @@ -0,0 +1,179 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.jdbc + +import io.opentelemetry.instrumentation.jdbc.internal.* +import io.opentelemetry.instrumentation.test.InstrumentationSpecification +import io.opentelemetry.instrumentation.test.LibraryTestTrait +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes + +import static io.opentelemetry.api.trace.SpanKind.CLIENT +import static io.opentelemetry.instrumentation.test.utils.TraceUtils.basicSpan +import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace + +class OpenTelemetryConnectionTest extends InstrumentationSpecification implements LibraryTestTrait { + + def "verify create statement"() { + setup: + def dbInfo = getDbInfo() + def connection = new OpenTelemetryConnection(new TestConnection(), dbInfo) + String query = "SELECT * FROM users" + def statement = connection.createStatement() + runUnderTrace("parent") { + assert statement.execute(query) + } + + expect: + assertTraces(1) { + trace(0, 2) { + basicSpan(it, 0, "parent") + span(1) { + name "SELECT my_name.users" + kind CLIENT + childOf span(0) + attributes { + "$SemanticAttributes.DB_SYSTEM.key" dbInfo.system + "$SemanticAttributes.DB_NAME.key" dbInfo.name + "$SemanticAttributes.DB_USER.key" dbInfo.user + "$SemanticAttributes.DB_CONNECTION_STRING.key" dbInfo.shortUrl + "$SemanticAttributes.NET_PEER_NAME.key" dbInfo.host + "$SemanticAttributes.NET_PEER_PORT.key" dbInfo.port + "$SemanticAttributes.DB_STATEMENT.key" query + "$SemanticAttributes.DB_OPERATION.key" "SELECT" + "$SemanticAttributes.DB_SQL_TABLE.key" "users" + } + } + } + } + + cleanup: + statement.close() + connection.close() + } + + def "verify create statement returns otel wrapper"() { + when: + def connection = new OpenTelemetryConnection(new TestConnection(), DbInfo.DEFAULT) + + then: + connection.createStatement().class == OpenTelemetryStatement + connection.createStatement(0, 0).class == OpenTelemetryStatement + connection.createStatement(0, 0, 0).class == OpenTelemetryStatement + } + + def "verify prepare statement"() { + setup: + def dbInfo = getDbInfo() + def connection = new OpenTelemetryConnection(new TestConnection(), dbInfo) + String query = "SELECT * FROM users" + def statement = connection.prepareStatement(query) + runUnderTrace("parent") { + assert statement.execute() + } + + expect: + assertTraces(1) { + trace(0, 2) { + basicSpan(it, 0, "parent") + span(1) { + name "SELECT my_name.users" + kind CLIENT + childOf span(0) + attributes { + "$SemanticAttributes.DB_SYSTEM.key" dbInfo.system + "$SemanticAttributes.DB_NAME.key" dbInfo.name + "$SemanticAttributes.DB_USER.key" dbInfo.user + "$SemanticAttributes.DB_CONNECTION_STRING.key" dbInfo.shortUrl + "$SemanticAttributes.NET_PEER_NAME.key" dbInfo.host + "$SemanticAttributes.NET_PEER_PORT.key" dbInfo.port + "$SemanticAttributes.DB_STATEMENT.key" query + "$SemanticAttributes.DB_OPERATION.key" "SELECT" + "$SemanticAttributes.DB_SQL_TABLE.key" "users" + } + } + } + } + + cleanup: + statement.close() + connection.close() + } + + def "verify prepare statement returns otel wrapper"() { + when: + def connection = new OpenTelemetryConnection(new TestConnection(), DbInfo.DEFAULT) + + then: + connection.prepareStatement("SELECT * FROM users").class == OpenTelemetryPreparedStatement + connection.prepareStatement("SELECT * FROM users", [0] as int[]).class == OpenTelemetryPreparedStatement + connection.prepareStatement("SELECT * FROM users", ["id"] as String[]).class == OpenTelemetryPreparedStatement + connection.prepareStatement("SELECT * FROM users", 0).class == OpenTelemetryPreparedStatement + connection.prepareStatement("SELECT * FROM users", 0, 0).class == OpenTelemetryPreparedStatement + connection.prepareStatement("SELECT * FROM users", 0, 0, 0).class == OpenTelemetryPreparedStatement + } + + def "verify prepare call"() { + setup: + def dbInfo = getDbInfo() + def connection = new OpenTelemetryConnection(new TestConnection(), dbInfo) + String query = "SELECT * FROM users" + def statement = connection.prepareCall(query) + runUnderTrace("parent") { + assert statement.execute() + } + + expect: + assertTraces(1) { + trace(0, 2) { + basicSpan(it, 0, "parent") + span(1) { + name "SELECT my_name.users" + kind CLIENT + childOf span(0) + attributes { + "$SemanticAttributes.DB_SYSTEM.key" dbInfo.system + "$SemanticAttributes.DB_NAME.key" dbInfo.name + "$SemanticAttributes.DB_USER.key" dbInfo.user + "$SemanticAttributes.DB_CONNECTION_STRING.key" dbInfo.shortUrl + "$SemanticAttributes.NET_PEER_NAME.key" dbInfo.host + "$SemanticAttributes.NET_PEER_PORT.key" dbInfo.port + "$SemanticAttributes.DB_STATEMENT.key" query + "$SemanticAttributes.DB_OPERATION.key" "SELECT" + "$SemanticAttributes.DB_SQL_TABLE.key" "users" + } + } + } + } + + cleanup: + statement.close() + connection.close() + } + + def "verify prepare call returns otel wrapper"() { + when: + def connection = new OpenTelemetryConnection(new TestConnection(), DbInfo.DEFAULT) + + then: + connection.prepareCall("SELECT * FROM users").class == OpenTelemetryCallableStatement + connection.prepareCall("SELECT * FROM users", 0, 0).class == OpenTelemetryCallableStatement + connection.prepareCall("SELECT * FROM users", 0, 0, 0).class == OpenTelemetryCallableStatement + } + + private DbInfo getDbInfo() { + DbInfo.builder() + .system("my_system") + .subtype("my_sub_type") + .shortUrl("my_connection_string") + .user("my_user") + .name("my_name") + .db("my_db") + .host("my_host") + .port(1234) + .build() + } + +} diff --git a/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/OpenTelemetryDriverTest.groovy b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/OpenTelemetryDriverTest.groovy new file mode 100644 index 000000000000..e4d43f826b66 --- /dev/null +++ b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/OpenTelemetryDriverTest.groovy @@ -0,0 +1,177 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.jdbc + +import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection +import spock.lang.Specification + +import java.sql.DriverManager +import java.sql.SQLFeatureNotSupportedException + +class OpenTelemetryDriverTest extends Specification { + + def cleanup() { + if (!OpenTelemetryDriver.registered) { + OpenTelemetryDriver.register() + } + } + + def "verify driver auto registered"() { + when: + Class driverClass = Class.forName("io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver") + def drivers = DriverManager.drivers + + then: + driverClass != null + OpenTelemetryDriver.registered + drivers.any { driver -> + driver instanceof OpenTelemetryDriver && driver == OpenTelemetryDriver.INSTANCE + } + } + + def "verify standard properties"() { + expect: + !OpenTelemetryDriver.INSTANCE.jdbcCompliant() + // replace with actual version of instrumentation library + OpenTelemetryDriver.INSTANCE.majorVersion == 1 + OpenTelemetryDriver.INSTANCE.minorVersion == 4 + } + + def "verify parent logger thrown an exception"() { + when: + OpenTelemetryDriver.INSTANCE.parentLogger + + then: + def e = thrown(SQLFeatureNotSupportedException) + e.message == "Feature not supported" + } + + def "verify accepted urls"() { + expect: + def driver = OpenTelemetryDriver.INSTANCE + driver.acceptsURL(url) == expected + + where: + url | expected + null | false + "" | false + "jdbc:" | false + "jdbc::" | false + "bogus:string" | false + "jdbc:postgresql://127.0.0.1:5432/dbname" | false + "jdbc:otel:postgresql://127.0.0.1:5432/dbname" | true + } + + def "verify deregister"() { + when: + if (OpenTelemetryDriver.registered) { + OpenTelemetryDriver.deregister() + } + + then: + !OpenTelemetryDriver.registered + DriverManager.drivers.every { driver -> + !(driver instanceof OpenTelemetryDriver) + } + } + + def "verify register"() { + when: + if (OpenTelemetryDriver.registered) { + OpenTelemetryDriver.deregister() + } + OpenTelemetryDriver.register() + + then: + OpenTelemetryDriver.registered + DriverManager.drivers.any { driver -> + driver instanceof OpenTelemetryDriver && driver == OpenTelemetryDriver.INSTANCE + } + } + + def "verify connection with null url"() { + when: + OpenTelemetryDriver.INSTANCE.connect(null, null) + + then: + def e = thrown(IllegalArgumentException) + e.message == "url is required" + } + + def "verify connection with empty url"() { + when: + OpenTelemetryDriver.INSTANCE.connect(" ", null) + + then: + def e = thrown(IllegalArgumentException) + e.message == "url is required" + } + + def "verify connection with not accepted url"() { + when: + def connection = OpenTelemetryDriver.INSTANCE.connect("abc:xyz", null) + + then: + connection == null + } + + def "verify connection with accepted url"() { + when: + registerTestDriver() + def connection = OpenTelemetryDriver.INSTANCE.connect("jdbc:otel:test:", null) + + then: + connection != null + connection instanceof OpenTelemetryConnection + } + + def "verify get property info with null url"() { + when: + OpenTelemetryDriver.INSTANCE.getPropertyInfo(null, null) + + then: + def e = thrown(IllegalArgumentException) + e.message == "url is required" + } + + def "verify get property info with empty url"() { + when: + OpenTelemetryDriver.INSTANCE.getPropertyInfo(" ", null) + + then: + def e = thrown(IllegalArgumentException) + e.message == "url is required" + } + + def "verify get property info with unknown driver url"() { + when: + def realUrl = "jdbc:unknown" + OpenTelemetryDriver.INSTANCE.getPropertyInfo(realUrl, null) + + then: + def e = thrown(IllegalStateException) + e.message == "Unable to find a driver that accepts url: ${realUrl}" + } + + def "verify get property info with test driver url"() { + when: + registerTestDriver() + def realUrl = "jdbc:otel:test:" + def propertyInfos = OpenTelemetryDriver.INSTANCE.getPropertyInfo(realUrl, null) + + then: + propertyInfos.size() == 1 + propertyInfos[0].name == "test" + propertyInfos[0].value == "test" + } + + private static void registerTestDriver() { + if (!(DriverManager.drivers.any { it instanceof TestDriver })) { + DriverManager.registerDriver(new TestDriver()) + } + } + +} diff --git a/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/datasource/OpenTelemetryDataSourceTest.groovy b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/datasource/OpenTelemetryDataSourceTest.groovy new file mode 100644 index 000000000000..7e8e92fdaa74 --- /dev/null +++ b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/datasource/OpenTelemetryDataSourceTest.groovy @@ -0,0 +1,59 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.jdbc.datasource + +import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection +import spock.lang.Specification + +class OpenTelemetryDataSourceTest extends Specification { + + def "verify get connection"() { + when: + def dataSource = new OpenTelemetryDataSource(new TestDataSource()) + def connection = dataSource.getConnection() + + then: + connection != null + connection instanceof OpenTelemetryConnection + + when: + def dbInfo = ((OpenTelemetryConnection) connection).dbInfo + + then: + dbInfo.system == "postgresql" + dbInfo.subtype == null + dbInfo.shortUrl == "postgresql://127.0.0.1:5432" + dbInfo.user == null + dbInfo.name == null + dbInfo.db == "dbname" + dbInfo.host == "127.0.0.1" + dbInfo.port == 5432 + } + + def "verify get connection with username and password"() { + when: + def dataSource = new OpenTelemetryDataSource(new TestDataSource()) + def connection = dataSource.getConnection(null, null) + + then: + connection != null + connection instanceof OpenTelemetryConnection + + when: + def dbInfo = ((OpenTelemetryConnection) connection).dbInfo + + then: + dbInfo.system == "postgresql" + dbInfo.subtype == null + dbInfo.shortUrl == "postgresql://127.0.0.1:5432" + dbInfo.user == null + dbInfo.name == null + dbInfo.db == "dbname" + dbInfo.host == "127.0.0.1" + dbInfo.port == 5432 + } + +} diff --git a/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/datasource/TestDataSource.groovy b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/datasource/TestDataSource.groovy new file mode 100644 index 000000000000..78e2d6366fc5 --- /dev/null +++ b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/datasource/TestDataSource.groovy @@ -0,0 +1,64 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.jdbc.datasource + +import io.opentelemetry.instrumentation.jdbc.TestConnection + +import javax.sql.DataSource +import java.sql.Connection +import java.sql.SQLException +import java.sql.SQLFeatureNotSupportedException +import java.util.logging.Logger + +class TestDataSource implements DataSource { + + @Override + Connection getConnection() throws SQLException { + return new TestConnection() + } + + @Override + Connection getConnection(String username, String password) throws SQLException { + return new TestConnection() + } + + @Override + PrintWriter getLogWriter() throws SQLException { + return null + } + + @Override + void setLogWriter(PrintWriter out) throws SQLException { + + } + + @Override + void setLoginTimeout(int seconds) throws SQLException { + + } + + @Override + int getLoginTimeout() throws SQLException { + return 0 + } + + @Override + Logger getParentLogger() throws SQLFeatureNotSupportedException { + return null + } + + @Override + def T unwrap(Class iface) throws SQLException { + return null + } + + @Override + boolean isWrapperFor(Class iface) throws SQLException { + return false + } + +} + diff --git a/instrumentation/jdbc/javaagent-unit-tests/src/test/groovy/JdbcConnectionUrlParserTest.groovy b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParserTest.groovy similarity index 99% rename from instrumentation/jdbc/javaagent-unit-tests/src/test/groovy/JdbcConnectionUrlParserTest.groovy rename to instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParserTest.groovy index d4c1f29e79e3..60488e4eab7d 100644 --- a/instrumentation/jdbc/javaagent-unit-tests/src/test/groovy/JdbcConnectionUrlParserTest.groovy +++ b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParserTest.groovy @@ -3,9 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcConnectionUrlParser.parse +package io.opentelemetry.instrumentation.jdbc.internal + +import static io.opentelemetry.instrumentation.jdbc.internal.JdbcConnectionUrlParser.parse -import io.opentelemetry.javaagent.instrumentation.jdbc.DbInfo import spock.lang.Shared import spock.lang.Specification diff --git a/instrumentation/jdbc/testing/build.gradle.kts b/instrumentation/jdbc/testing/build.gradle.kts new file mode 100644 index 000000000000..b1a174dfe0d3 --- /dev/null +++ b/instrumentation/jdbc/testing/build.gradle.kts @@ -0,0 +1,12 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +plugins { + id("otel.java-conventions") +} + +dependencies { + implementation("org.codehaus.groovy:groovy-all") +} diff --git a/instrumentation/jdbc/javaagent/src/test/groovy/test/TestDriver.groovy b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/AnotherTestDriver.groovy similarity index 72% rename from instrumentation/jdbc/javaagent/src/test/groovy/test/TestDriver.groovy rename to instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/AnotherTestDriver.groovy index 60b2368bac57..3590fc801e66 100644 --- a/instrumentation/jdbc/javaagent/src/test/groovy/test/TestDriver.groovy +++ b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/AnotherTestDriver.groovy @@ -3,19 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -package test +package io.opentelemetry.instrumentation.jdbc -import java.sql.Connection -import java.sql.Driver -import java.sql.DriverPropertyInfo -import java.sql.SQLException -import java.sql.SQLFeatureNotSupportedException +import java.sql.* import java.util.logging.Logger -class TestDriver implements Driver { +class AnotherTestDriver implements Driver { @Override Connection connect(String url, Properties info) throws SQLException { - return new TestConnection("connectException=true" == url) + return null } @Override diff --git a/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestCallableStatement.groovy b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestCallableStatement.groovy new file mode 100644 index 000000000000..b7980fee699e --- /dev/null +++ b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestCallableStatement.groovy @@ -0,0 +1,575 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.jdbc + +import java.sql.* + +class TestCallableStatement extends TestPreparedStatement implements CallableStatement { + @Override + void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { + + } + + @Override + void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { + + } + + @Override + boolean wasNull() throws SQLException { + return false + } + + @Override + String getString(int parameterIndex) throws SQLException { + return null + } + + @Override + boolean getBoolean(int parameterIndex) throws SQLException { + return false + } + + @Override + byte getByte(int parameterIndex) throws SQLException { + return 0 + } + + @Override + short getShort(int parameterIndex) throws SQLException { + return 0 + } + + @Override + int getInt(int parameterIndex) throws SQLException { + return 0 + } + + @Override + long getLong(int parameterIndex) throws SQLException { + return 0 + } + + @Override + float getFloat(int parameterIndex) throws SQLException { + return 0 + } + + @Override + double getDouble(int parameterIndex) throws SQLException { + return 0 + } + + @Override + BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { + return null + } + + @Override + byte[] getBytes(int parameterIndex) throws SQLException { + return new byte[0] + } + + @Override + Date getDate(int parameterIndex) throws SQLException { + return null + } + + @Override + Time getTime(int parameterIndex) throws SQLException { + return null + } + + @Override + Timestamp getTimestamp(int parameterIndex) throws SQLException { + return null + } + + @Override + Object getObject(int parameterIndex) throws SQLException { + return null + } + + @Override + BigDecimal getBigDecimal(int parameterIndex) throws SQLException { + return null + } + + @Override + Object getObject(int parameterIndex, Map> map) throws SQLException { + return null + } + + @Override + Ref getRef(int parameterIndex) throws SQLException { + return null + } + + @Override + Blob getBlob(int parameterIndex) throws SQLException { + return null + } + + @Override + Clob getClob(int parameterIndex) throws SQLException { + return null + } + + @Override + Array getArray(int parameterIndex) throws SQLException { + return null + } + + @Override + Date getDate(int parameterIndex, Calendar cal) throws SQLException { + return null + } + + @Override + Time getTime(int parameterIndex, Calendar cal) throws SQLException { + return null + } + + @Override + Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException { + return null + } + + @Override + void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException { + + } + + @Override + void registerOutParameter(String parameterName, int sqlType) throws SQLException { + + } + + @Override + void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException { + + } + + @Override + void registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException { + + } + + @Override + URL getURL(int parameterIndex) throws SQLException { + return null + } + + @Override + void setURL(String parameterName, URL val) throws SQLException { + + } + + @Override + void setNull(String parameterName, int sqlType) throws SQLException { + + } + + @Override + void setBoolean(String parameterName, boolean x) throws SQLException { + + } + + @Override + void setByte(String parameterName, byte x) throws SQLException { + + } + + @Override + void setShort(String parameterName, short x) throws SQLException { + + } + + @Override + void setInt(String parameterName, int x) throws SQLException { + + } + + @Override + void setLong(String parameterName, long x) throws SQLException { + + } + + @Override + void setFloat(String parameterName, float x) throws SQLException { + + } + + @Override + void setDouble(String parameterName, double x) throws SQLException { + + } + + @Override + void setBigDecimal(String parameterName, BigDecimal x) throws SQLException { + + } + + @Override + void setString(String parameterName, String x) throws SQLException { + + } + + @Override + void setBytes(String parameterName, byte[] x) throws SQLException { + + } + + @Override + void setDate(String parameterName, Date x) throws SQLException { + + } + + @Override + void setTime(String parameterName, Time x) throws SQLException { + + } + + @Override + void setTimestamp(String parameterName, Timestamp x) throws SQLException { + + } + + @Override + void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException { + + } + + @Override + void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException { + + } + + @Override + void setObject(String parameterName, Object x, int targetSqlType, int scale) throws SQLException { + + } + + @Override + void setObject(String parameterName, Object x, int targetSqlType) throws SQLException { + + } + + @Override + void setObject(String parameterName, Object x) throws SQLException { + + } + + @Override + void setCharacterStream(String parameterName, Reader reader, int length) throws SQLException { + + } + + @Override + void setDate(String parameterName, Date x, Calendar cal) throws SQLException { + + } + + @Override + void setTime(String parameterName, Time x, Calendar cal) throws SQLException { + + } + + @Override + void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException { + + } + + @Override + void setNull(String parameterName, int sqlType, String typeName) throws SQLException { + + } + + @Override + String getString(String parameterName) throws SQLException { + return null + } + + @Override + boolean getBoolean(String parameterName) throws SQLException { + return false + } + + @Override + byte getByte(String parameterName) throws SQLException { + return 0 + } + + @Override + short getShort(String parameterName) throws SQLException { + return 0 + } + + @Override + int getInt(String parameterName) throws SQLException { + return 0 + } + + @Override + long getLong(String parameterName) throws SQLException { + return 0 + } + + @Override + float getFloat(String parameterName) throws SQLException { + return 0 + } + + @Override + double getDouble(String parameterName) throws SQLException { + return 0 + } + + @Override + byte[] getBytes(String parameterName) throws SQLException { + return new byte[0] + } + + @Override + Date getDate(String parameterName) throws SQLException { + return null + } + + @Override + Time getTime(String parameterName) throws SQLException { + return null + } + + @Override + Timestamp getTimestamp(String parameterName) throws SQLException { + return null + } + + @Override + Object getObject(String parameterName) throws SQLException { + return null + } + + @Override + BigDecimal getBigDecimal(String parameterName) throws SQLException { + return null + } + + @Override + Object getObject(String parameterName, Map> map) throws SQLException { + return null + } + + @Override + Ref getRef(String parameterName) throws SQLException { + return null + } + + @Override + Blob getBlob(String parameterName) throws SQLException { + return null + } + + @Override + Clob getClob(String parameterName) throws SQLException { + return null + } + + @Override + Array getArray(String parameterName) throws SQLException { + return null + } + + @Override + Date getDate(String parameterName, Calendar cal) throws SQLException { + return null + } + + @Override + Time getTime(String parameterName, Calendar cal) throws SQLException { + return null + } + + @Override + Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException { + return null + } + + @Override + URL getURL(String parameterName) throws SQLException { + return null + } + + @Override + RowId getRowId(int parameterIndex) throws SQLException { + return null + } + + @Override + RowId getRowId(String parameterName) throws SQLException { + return null + } + + @Override + void setRowId(String parameterName, RowId x) throws SQLException { + + } + + @Override + void setNString(String parameterName, String value) throws SQLException { + + } + + @Override + void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException { + + } + + @Override + void setNClob(String parameterName, NClob value) throws SQLException { + + } + + @Override + void setClob(String parameterName, Reader reader, long length) throws SQLException { + + } + + @Override + void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException { + + } + + @Override + void setNClob(String parameterName, Reader reader, long length) throws SQLException { + + } + + @Override + NClob getNClob(int parameterIndex) throws SQLException { + return null + } + + @Override + NClob getNClob(String parameterName) throws SQLException { + return null + } + + @Override + void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { + + } + + @Override + SQLXML getSQLXML(int parameterIndex) throws SQLException { + return null + } + + @Override + SQLXML getSQLXML(String parameterName) throws SQLException { + return null + } + + @Override + String getNString(int parameterIndex) throws SQLException { + return null + } + + @Override + String getNString(String parameterName) throws SQLException { + return null + } + + @Override + Reader getNCharacterStream(int parameterIndex) throws SQLException { + return null + } + + @Override + Reader getNCharacterStream(String parameterName) throws SQLException { + return null + } + + @Override + Reader getCharacterStream(int parameterIndex) throws SQLException { + return null + } + + @Override + Reader getCharacterStream(String parameterName) throws SQLException { + return null + } + + @Override + void setBlob(String parameterName, Blob x) throws SQLException { + + } + + @Override + void setClob(String parameterName, Clob x) throws SQLException { + + } + + @Override + void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException { + + } + + @Override + void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException { + + } + + @Override + void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { + + } + + @Override + void setAsciiStream(String parameterName, InputStream x) throws SQLException { + + } + + @Override + void setBinaryStream(String parameterName, InputStream x) throws SQLException { + + } + + @Override + void setCharacterStream(String parameterName, Reader reader) throws SQLException { + + } + + @Override + void setNCharacterStream(String parameterName, Reader value) throws SQLException { + + } + + @Override + void setClob(String parameterName, Reader reader) throws SQLException { + + } + + @Override + void setBlob(String parameterName, InputStream inputStream) throws SQLException { + + } + + @Override + void setNClob(String parameterName, Reader reader) throws SQLException { + + } + + @Override + def T getObject(int parameterIndex, Class type) throws SQLException { + return null + } + + @Override + def T getObject(String parameterName, Class type) throws SQLException { + return null + } +} diff --git a/instrumentation/jdbc/javaagent/src/test/groovy/test/TestConnection.groovy b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestConnection.groovy similarity index 90% rename from instrumentation/jdbc/javaagent/src/test/groovy/test/TestConnection.groovy rename to instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestConnection.groovy index 9eca7a13a2f4..abee3ad6b9e9 100644 --- a/instrumentation/jdbc/javaagent/src/test/groovy/test/TestConnection.groovy +++ b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestConnection.groovy @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package test +package io.opentelemetry.instrumentation.jdbc import java.sql.Array import java.sql.Blob @@ -22,31 +22,80 @@ import java.sql.Statement import java.sql.Struct import java.util.concurrent.Executor - /** * A JDBC connection class that optionally throws an exception in the constructor, used to test */ class TestConnection implements Connection { + private String url + + TestConnection() { + this(false) + } + TestConnection(boolean throwException) { if (throwException) { throw new IllegalStateException("connection exception") } } - @Override Statement createStatement() throws SQLException { return new TestStatement(this) } + @Override + Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return new TestStatement(this) + } + + @Override + Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return new TestStatement(this) + } + @Override PreparedStatement prepareStatement(String sql) throws SQLException { return new TestPreparedStatement(this) } + @Override + PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return new TestPreparedStatement(this) + } + + @Override + PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return new TestPreparedStatement(this) + } + + @Override + PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + return new TestPreparedStatement(this) + } + + @Override + PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + return new TestPreparedStatement(this) + } + + @Override + PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + return new TestPreparedStatement(this) + } + @Override CallableStatement prepareCall(String sql) throws SQLException { - return null + return new TestCallableStatement() + } + + @Override + CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return new TestCallableStatement() + } + + @Override + CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return new TestCallableStatement() } @Override @@ -86,6 +135,9 @@ class TestConnection implements Connection { @Override DatabaseMetaData getMetaData() throws SQLException { + if (url) { + return new TestDatabaseMetaData(url) + } return new TestDatabaseMetaData() } @@ -129,21 +181,6 @@ class TestConnection implements Connection { } - @Override - Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { - return null - } - - @Override - PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - return null - } - - @Override - CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - return null - } - @Override Map> getTypeMap() throws SQLException { return null @@ -184,36 +221,6 @@ class TestConnection implements Connection { } - @Override - Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return null - } - - @Override - PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return null - } - - @Override - CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return null - } - - @Override - PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { - return null - } - - @Override - PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { - return null - } - - @Override - PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { - return null - } - @Override Clob createClob() throws SQLException { return null @@ -303,4 +310,8 @@ class TestConnection implements Connection { boolean isWrapperFor(Class iface) throws SQLException { return false } + + void setUrl(String url) { + this.url = url + } } diff --git a/instrumentation/jdbc/javaagent/src/test/groovy/test/TestDatabaseMetaData.groovy b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestDatabaseMetaData.groovy similarity index 98% rename from instrumentation/jdbc/javaagent/src/test/groovy/test/TestDatabaseMetaData.groovy rename to instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestDatabaseMetaData.groovy index 757b43a2dff5..a65835f08a9c 100644 --- a/instrumentation/jdbc/javaagent/src/test/groovy/test/TestDatabaseMetaData.groovy +++ b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestDatabaseMetaData.groovy @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package test +package io.opentelemetry.instrumentation.jdbc import java.sql.Connection import java.sql.DatabaseMetaData @@ -12,6 +12,16 @@ import java.sql.RowIdLifetime import java.sql.SQLException class TestDatabaseMetaData implements DatabaseMetaData { + final String url + + TestDatabaseMetaData() { + this("jdbc:postgresql://127.0.0.1:5432/dbname") + } + + TestDatabaseMetaData(String url) { + this.url = url + } + @Override boolean allProceduresAreCallable() throws SQLException { return false @@ -24,7 +34,7 @@ class TestDatabaseMetaData implements DatabaseMetaData { @Override String getURL() throws SQLException { - return "jdbc:testdb://localhost" + return url } @Override diff --git a/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestDriver.groovy b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestDriver.groovy new file mode 100644 index 000000000000..db93d28ed82f --- /dev/null +++ b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestDriver.groovy @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.jdbc + +import java.sql.* +import java.util.logging.Logger + +class TestDriver implements Driver { + @Override + Connection connect(String url, Properties info) throws SQLException { + return new TestConnection() + } + + @Override + boolean acceptsURL(String url) throws SQLException { + return url?.startsWith("jdbc:test:") + } + + @Override + DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { + return [new DriverPropertyInfo("test", "test")] + } + + @Override + int getMajorVersion() { + return 0 + } + + @Override + int getMinorVersion() { + return 0 + } + + @Override + boolean jdbcCompliant() { + return false + } + + @Override + Logger getParentLogger() throws SQLFeatureNotSupportedException { + return null + } +} diff --git a/instrumentation/jdbc/javaagent/src/test/groovy/test/TestPreparedStatement.groovy b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestPreparedStatement.groovy similarity index 98% rename from instrumentation/jdbc/javaagent/src/test/groovy/test/TestPreparedStatement.groovy rename to instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestPreparedStatement.groovy index 5f2b4f1a02da..c8f21d061beb 100644 --- a/instrumentation/jdbc/javaagent/src/test/groovy/test/TestPreparedStatement.groovy +++ b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestPreparedStatement.groovy @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package test +package io.opentelemetry.instrumentation.jdbc import java.sql.Array import java.sql.Blob @@ -23,10 +23,20 @@ import java.sql.Time import java.sql.Timestamp class TestPreparedStatement extends TestStatement implements PreparedStatement { + + TestPreparedStatement() { + super() + } + TestPreparedStatement(Connection connection) { super(connection) } + @Override + boolean execute() throws SQLException { + return true + } + @Override ResultSet executeQuery() throws SQLException { return null @@ -137,11 +147,6 @@ class TestPreparedStatement extends TestStatement implements PreparedStatement { } - @Override - boolean execute() throws SQLException { - return false - } - @Override void addBatch() throws SQLException { diff --git a/instrumentation/jdbc/javaagent/src/test/groovy/test/TestStatement.groovy b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestStatement.groovy similarity index 96% rename from instrumentation/jdbc/javaagent/src/test/groovy/test/TestStatement.groovy rename to instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestStatement.groovy index 3a2605c353e7..4c833c93358c 100644 --- a/instrumentation/jdbc/javaagent/src/test/groovy/test/TestStatement.groovy +++ b/instrumentation/jdbc/testing/src/main/groovy/io/opentelemetry/instrumentation/jdbc/TestStatement.groovy @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package test +package io.opentelemetry.instrumentation.jdbc import java.sql.Connection import java.sql.ResultSet @@ -14,6 +14,10 @@ import java.sql.Statement class TestStatement implements Statement { final Connection connection + TestStatement() { + this.connection = null + } + TestStatement(Connection connection) { this.connection = connection } @@ -90,7 +94,7 @@ class TestStatement implements Statement { @Override boolean execute(String sql) throws SQLException { - return false + return true } @Override @@ -185,17 +189,17 @@ class TestStatement implements Statement { @Override boolean execute(String sql, int autoGeneratedKeys) throws SQLException { - return false + return true } @Override boolean execute(String sql, int[] columnIndexes) throws SQLException { - return false + return true } @Override boolean execute(String sql, String[] columnNames) throws SQLException { - return false + return true } @Override diff --git a/settings.gradle.kts b/settings.gradle.kts index ad6ea8f2f3b0..72af6a155ec7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -180,7 +180,8 @@ include(":instrumentation:jaxws:jaxws-2.0-wildfly-testing") include(":instrumentation:jaxws:jaxws-common:library") include(":instrumentation:jaxws:jws-1.1:javaagent") include(":instrumentation:jdbc:javaagent") -include(":instrumentation:jdbc:javaagent-unit-tests") +include(":instrumentation:jdbc:library") +include(":instrumentation:jdbc:testing") include(":instrumentation:jedis:jedis-1.4:javaagent") include(":instrumentation:jedis:jedis-3.0:javaagent") include(":instrumentation:jetty:jetty-8.0:javaagent")