Allow JDBC autoinstrumentation to use a custom OpenTelemetry instance to be more DI (e.g. Spring Boot) friendly (#7697)

Related to issue
https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/7677
This commit is contained in:
Fran Pregernik 2023-02-08 17:46:13 +01:00 committed by GitHub
parent c3e877032b
commit 1d3752f21b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 218 additions and 138 deletions

View File

@ -5,6 +5,8 @@
package io.opentelemetry.javaagent.instrumentation.jdbc; package io.opentelemetry.javaagent.instrumentation.jdbc;
import static io.opentelemetry.instrumentation.jdbc.internal.DataSourceInstrumenterFactory.createDataSourceInstrumenter;
import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
@ -17,17 +19,20 @@ import io.opentelemetry.instrumentation.jdbc.internal.JdbcAttributesGetter;
import io.opentelemetry.instrumentation.jdbc.internal.JdbcNetAttributesGetter; import io.opentelemetry.instrumentation.jdbc.internal.JdbcNetAttributesGetter;
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
import javax.sql.DataSource;
public final class JdbcSingletons { public final class JdbcSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jdbc"; private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jdbc";
private static final Instrumenter<DbRequest, Void> INSTRUMENTER; private static final Instrumenter<DbRequest, Void> STATEMENT_INSTRUMENTER;
public static final Instrumenter<DataSource, Void> DATASOURCE_INSTRUMENTER =
createDataSourceInstrumenter(GlobalOpenTelemetry.get());
static { static {
JdbcAttributesGetter dbAttributesGetter = new JdbcAttributesGetter(); JdbcAttributesGetter dbAttributesGetter = new JdbcAttributesGetter();
JdbcNetAttributesGetter netAttributesGetter = new JdbcNetAttributesGetter(); JdbcNetAttributesGetter netAttributesGetter = new JdbcNetAttributesGetter();
INSTRUMENTER = STATEMENT_INSTRUMENTER =
Instrumenter.<DbRequest, Void>builder( Instrumenter.<DbRequest, Void>builder(
GlobalOpenTelemetry.get(), GlobalOpenTelemetry.get(),
INSTRUMENTATION_NAME, INSTRUMENTATION_NAME,
@ -47,8 +52,12 @@ public final class JdbcSingletons {
.buildInstrumenter(SpanKindExtractor.alwaysClient()); .buildInstrumenter(SpanKindExtractor.alwaysClient());
} }
public static Instrumenter<DbRequest, Void> instrumenter() { public static Instrumenter<DbRequest, Void> statementInstrumenter() {
return INSTRUMENTER; return STATEMENT_INSTRUMENTER;
}
public static Instrumenter<DataSource, Void> dataSourceInstrumenter() {
return DATASOURCE_INSTRUMENTER;
} }
private JdbcSingletons() {} private JdbcSingletons() {}

View File

@ -8,7 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.jdbc;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcSingletons.instrumenter; import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcSingletons.statementInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@ -70,11 +70,11 @@ public class PreparedStatementInstrumentation implements TypeInstrumentation {
Context parentContext = currentContext(); Context parentContext = currentContext();
request = DbRequest.create(statement); request = DbRequest.create(statement);
if (request == null || !instrumenter().shouldStart(parentContext, request)) { if (request == null || !statementInstrumenter().shouldStart(parentContext, request)) {
return; return;
} }
context = instrumenter().start(parentContext, request); context = statementInstrumenter().start(parentContext, request);
scope = context.makeCurrent(); scope = context.makeCurrent();
} }
@ -91,7 +91,7 @@ public class PreparedStatementInstrumentation implements TypeInstrumentation {
if (scope != null) { if (scope != null) {
scope.close(); scope.close();
instrumenter().end(context, request, null, throwable); statementInstrumenter().end(context, request, null, throwable);
} }
} }
} }

View File

@ -8,7 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.jdbc;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcSingletons.instrumenter; import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcSingletons.statementInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@ -70,11 +70,11 @@ public class StatementInstrumentation implements TypeInstrumentation {
Context parentContext = currentContext(); Context parentContext = currentContext();
request = DbRequest.create(statement, sql); request = DbRequest.create(statement, sql);
if (request == null || !instrumenter().shouldStart(parentContext, request)) { if (request == null || !statementInstrumenter().shouldStart(parentContext, request)) {
return; return;
} }
context = instrumenter().start(parentContext, request); context = statementInstrumenter().start(parentContext, request);
scope = context.makeCurrent(); scope = context.makeCurrent();
} }
@ -91,7 +91,7 @@ public class StatementInstrumentation implements TypeInstrumentation {
if (scope != null) { if (scope != null) {
scope.close(); scope.close();
instrumenter().end(context, request, null, throwable); statementInstrumenter().end(context, request, null, throwable);
} }
} }
} }

View File

@ -5,8 +5,8 @@
package io.opentelemetry.javaagent.instrumentation.jdbc.datasource; 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.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcSingletons.dataSourceInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
@ -20,6 +20,7 @@ import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
public class DataSourceInstrumentation implements TypeInstrumentation { public class DataSourceInstrumentation implements TypeInstrumentation {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("javax.sql.DataSource")); return implementsInterface(named("javax.sql.DataSource"));
@ -46,7 +47,7 @@ public class DataSourceInstrumentation implements TypeInstrumentation {
return; return;
} }
context = instrumenter().start(parentContext, ds); context = dataSourceInstrumenter().start(parentContext, ds);
scope = context.makeCurrent(); scope = context.makeCurrent();
} }
@ -60,7 +61,7 @@ public class DataSourceInstrumentation implements TypeInstrumentation {
return; return;
} }
scope.close(); scope.close();
instrumenter().end(context, ds, null, throwable); dataSourceInstrumenter().end(context, ds, null, throwable);
} }
} }
} }

View File

@ -20,7 +20,8 @@
package io.opentelemetry.instrumentation.jdbc; package io.opentelemetry.instrumentation.jdbc;
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcSingletons.INSTRUMENTATION_NAME; import static io.opentelemetry.instrumentation.jdbc.internal.JdbcInstrumenterFactory.INSTRUMENTATION_NAME;
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcSingletons.statementInstrumenter;
import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties; import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
import io.opentelemetry.instrumentation.jdbc.internal.JdbcConnectionUrlParser; import io.opentelemetry.instrumentation.jdbc.internal.JdbcConnectionUrlParser;
@ -211,7 +212,7 @@ public final class OpenTelemetryDriver implements Driver {
DbInfo dbInfo = JdbcConnectionUrlParser.parse(realUrl, info); DbInfo dbInfo = JdbcConnectionUrlParser.parse(realUrl, info);
return new OpenTelemetryConnection(connection, dbInfo); return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter());
} }
@Override @Override

View File

@ -20,12 +20,17 @@
package io.opentelemetry.instrumentation.jdbc.datasource; package io.opentelemetry.instrumentation.jdbc.datasource;
import static io.opentelemetry.instrumentation.jdbc.internal.DataSourceSingletons.instrumenter; import static io.opentelemetry.instrumentation.jdbc.internal.DataSourceInstrumenterFactory.createDataSourceInstrumenter;
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcInstrumenterFactory.createStatementInstrumenter;
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcUtils.computeDbInfo; import static io.opentelemetry.instrumentation.jdbc.internal.JdbcUtils.computeDbInfo;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.jdbc.internal.DbRequest;
import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection; import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection;
import io.opentelemetry.instrumentation.jdbc.internal.ThrowingSupplier; import io.opentelemetry.instrumentation.jdbc.internal.ThrowingSupplier;
import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo; import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo;
@ -40,29 +45,44 @@ import javax.sql.DataSource;
public class OpenTelemetryDataSource implements DataSource, AutoCloseable { public class OpenTelemetryDataSource implements DataSource, AutoCloseable {
private final DataSource delegate; private final DataSource delegate;
private final Instrumenter<DataSource, Void> dataSourceInstrumenter;
private final Instrumenter<DbRequest, Void> statementInstrumenter;
/**
* Create a OpenTelemetry DataSource wrapping another DataSource.
*
* @param delegate the DataSource to wrap
*/
@Deprecated
public OpenTelemetryDataSource(DataSource delegate) {
this(delegate, GlobalOpenTelemetry.get());
}
/** /**
* Create a OpenTelemetry DataSource wrapping another DataSource. This constructor is primarily * Create a OpenTelemetry DataSource wrapping another DataSource. This constructor is primarily
* used by dependency injection frameworks. * used by dependency injection frameworks.
* *
* @param delegate the DataSource to wrap * @param delegate the DataSource to wrap
* @param openTelemetry the OpenTelemetry instance to setup for
*/ */
public OpenTelemetryDataSource(DataSource delegate) { public OpenTelemetryDataSource(DataSource delegate, OpenTelemetry openTelemetry) {
this.delegate = delegate; this.delegate = delegate;
this.dataSourceInstrumenter = createDataSourceInstrumenter(openTelemetry);
this.statementInstrumenter = createStatementInstrumenter(openTelemetry);
} }
@Override @Override
public Connection getConnection() throws SQLException { public Connection getConnection() throws SQLException {
Connection connection = wrapCall(delegate::getConnection); Connection connection = wrapCall(delegate::getConnection);
DbInfo dbInfo = computeDbInfo(connection); DbInfo dbInfo = computeDbInfo(connection);
return new OpenTelemetryConnection(connection, dbInfo); return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter);
} }
@Override @Override
public Connection getConnection(String username, String password) throws SQLException { public Connection getConnection(String username, String password) throws SQLException {
Connection connection = wrapCall(() -> delegate.getConnection(username, password)); Connection connection = wrapCall(() -> delegate.getConnection(username, password));
DbInfo dbInfo = computeDbInfo(connection); DbInfo dbInfo = computeDbInfo(connection);
return new OpenTelemetryConnection(connection, dbInfo); return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter);
} }
@Override @Override
@ -116,15 +136,15 @@ public class OpenTelemetryDataSource implements DataSource, AutoCloseable {
return callable.call(); return callable.call();
} }
Context context = instrumenter().start(parentContext, delegate); Context context = this.dataSourceInstrumenter.start(parentContext, delegate);
T result; T result;
try (Scope ignored = context.makeCurrent()) { try (Scope ignored = context.makeCurrent()) {
result = callable.call(); result = callable.call();
} catch (Throwable t) { } catch (Throwable t) {
instrumenter().end(context, delegate, null, t); this.dataSourceInstrumenter.end(context, delegate, null, t);
throw t; throw t;
} }
instrumenter().end(context, delegate, null, null); this.dataSourceInstrumenter.end(context, delegate, null, null);
return result; return result;
} }
} }

View File

@ -0,0 +1,32 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.jdbc.internal;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor;
import javax.sql.DataSource;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class DataSourceInstrumenterFactory {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jdbc";
private static final DataSourceCodeAttributesGetter codeAttributesGetter =
new DataSourceCodeAttributesGetter();
public static Instrumenter<DataSource, Void> createDataSourceInstrumenter(
OpenTelemetry openTelemetry) {
return Instrumenter.<DataSource, Void>builder(
openTelemetry, INSTRUMENTATION_NAME, CodeSpanNameExtractor.create(codeAttributesGetter))
.addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter))
.buildInstrumenter();
}
private DataSourceInstrumenterFactory() {}
}

View File

@ -1,40 +0,0 @@
/*
* 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.code.CodeAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor;
import javax.sql.DataSource;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class DataSourceSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jdbc";
private static final Instrumenter<DataSource, Void> INSTRUMENTER;
static {
DataSourceCodeAttributesGetter codeAttributesGetter = new DataSourceCodeAttributesGetter();
INSTRUMENTER =
Instrumenter.<DataSource, Void>builder(
GlobalOpenTelemetry.get(),
INSTRUMENTATION_NAME,
CodeSpanNameExtractor.create(codeAttributesGetter))
.addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter))
.buildInstrumenter();
}
public static Instrumenter<DataSource, Void> instrumenter() {
return INSTRUMENTER;
}
private DataSourceSingletons() {}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.jdbc.internal;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.db.SqlClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class JdbcInstrumenterFactory {
public static final String INSTRUMENTATION_NAME = "io.opentelemetry.jdbc";
private static final JdbcAttributesGetter dbAttributesGetter = new JdbcAttributesGetter();
private static final JdbcNetAttributesGetter netAttributesGetter = new JdbcNetAttributesGetter();
public static Instrumenter<DbRequest, Void> createStatementInstrumenter() {
return createStatementInstrumenter(GlobalOpenTelemetry.get());
}
public static Instrumenter<DbRequest, Void> createStatementInstrumenter(
OpenTelemetry openTelemetry) {
return Instrumenter.<DbRequest, Void>builder(
openTelemetry,
INSTRUMENTATION_NAME,
DbClientSpanNameExtractor.create(dbAttributesGetter))
.addAttributesExtractor(
SqlClientAttributesExtractor.builder(dbAttributesGetter)
.setStatementSanitizationEnabled(
ConfigPropertiesUtil.getBoolean(
"otel.instrumentation.common.db-statement-sanitizer.enabled", true))
.build())
.addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter))
.buildInstrumenter(SpanKindExtractor.alwaysClient());
}
private JdbcInstrumenterFactory() {}
}

View File

@ -5,44 +5,21 @@
package io.opentelemetry.instrumentation.jdbc.internal; package io.opentelemetry.instrumentation.jdbc.internal;
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcInstrumenterFactory.createStatementInstrumenter;
import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.db.SqlClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil;
/** /**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at * This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time. * any time.
*/ */
public final class JdbcSingletons { public final class JdbcSingletons {
public static final String INSTRUMENTATION_NAME = "io.opentelemetry.jdbc"; public static final Instrumenter<DbRequest, Void> STATEMENT_INSTRUMENTER =
createStatementInstrumenter(GlobalOpenTelemetry.get());
private static final Instrumenter<DbRequest, Void> INSTRUMENTER; public static Instrumenter<DbRequest, Void> statementInstrumenter() {
return STATEMENT_INSTRUMENTER;
static {
JdbcAttributesGetter dbAttributesGetter = new JdbcAttributesGetter();
JdbcNetAttributesGetter netAttributesGetter = new JdbcNetAttributesGetter();
INSTRUMENTER =
Instrumenter.<DbRequest, Void>builder(
GlobalOpenTelemetry.get(),
INSTRUMENTATION_NAME,
DbClientSpanNameExtractor.create(dbAttributesGetter))
.addAttributesExtractor(
SqlClientAttributesExtractor.builder(dbAttributesGetter)
.setStatementSanitizationEnabled(
ConfigPropertiesUtil.getBoolean(
"otel.instrumentation.common.db-statement-sanitizer.enabled", true))
.build())
.addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter))
.buildInstrumenter(SpanKindExtractor.alwaysClient());
}
public static Instrumenter<DbRequest, Void> instrumenter() {
return INSTRUMENTER;
} }
private JdbcSingletons() {} private JdbcSingletons() {}

View File

@ -20,6 +20,7 @@
package io.opentelemetry.instrumentation.jdbc.internal; package io.opentelemetry.instrumentation.jdbc.internal;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo; import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
@ -47,8 +48,9 @@ import java.util.Map;
public class OpenTelemetryCallableStatement<S extends CallableStatement> public class OpenTelemetryCallableStatement<S extends CallableStatement>
extends OpenTelemetryPreparedStatement<S> implements CallableStatement { extends OpenTelemetryPreparedStatement<S> implements CallableStatement {
public OpenTelemetryCallableStatement(S delegate, DbInfo dbInfo, String query) { public OpenTelemetryCallableStatement(
super(delegate, dbInfo, query); S delegate, DbInfo dbInfo, String query, Instrumenter<DbRequest, Void> instrumenter) {
super(delegate, dbInfo, query, instrumenter);
} }
@Override @Override

View File

@ -20,6 +20,7 @@
package io.opentelemetry.instrumentation.jdbc.internal; package io.opentelemetry.instrumentation.jdbc.internal;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo; import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo;
import java.sql.Array; import java.sql.Array;
import java.sql.Blob; import java.sql.Blob;
@ -48,23 +49,26 @@ public class OpenTelemetryConnection implements Connection {
private final Connection delegate; private final Connection delegate;
private final DbInfo dbInfo; private final DbInfo dbInfo;
protected final Instrumenter<DbRequest, Void> statementInstrumenter;
public OpenTelemetryConnection(Connection delegate, DbInfo dbInfo) { public OpenTelemetryConnection(
Connection delegate, DbInfo dbInfo, Instrumenter<DbRequest, Void> statementInstrumenter) {
this.delegate = delegate; this.delegate = delegate;
this.dbInfo = dbInfo; this.dbInfo = dbInfo;
this.statementInstrumenter = statementInstrumenter;
} }
@Override @Override
public Statement createStatement() throws SQLException { public Statement createStatement() throws SQLException {
Statement statement = delegate.createStatement(); Statement statement = delegate.createStatement();
return new OpenTelemetryStatement<>(statement, dbInfo); return new OpenTelemetryStatement<>(statement, dbInfo, statementInstrumenter);
} }
@Override @Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) public Statement createStatement(int resultSetType, int resultSetConcurrency)
throws SQLException { throws SQLException {
Statement statement = delegate.createStatement(resultSetType, resultSetConcurrency); Statement statement = delegate.createStatement(resultSetType, resultSetConcurrency);
return new OpenTelemetryStatement<>(statement, dbInfo); return new OpenTelemetryStatement<>(statement, dbInfo, statementInstrumenter);
} }
@Override @Override
@ -72,13 +76,13 @@ public class OpenTelemetryConnection implements Connection {
int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
Statement statement = Statement statement =
delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
return new OpenTelemetryStatement<>(statement, dbInfo); return new OpenTelemetryStatement<>(statement, dbInfo, statementInstrumenter);
} }
@Override @Override
public PreparedStatement prepareStatement(String sql) throws SQLException { public PreparedStatement prepareStatement(String sql) throws SQLException {
PreparedStatement statement = delegate.prepareStatement(sql); PreparedStatement statement = delegate.prepareStatement(sql);
return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql, statementInstrumenter);
} }
@Override @Override
@ -86,7 +90,7 @@ public class OpenTelemetryConnection implements Connection {
throws SQLException { throws SQLException {
PreparedStatement statement = PreparedStatement statement =
delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); delegate.prepareStatement(sql, resultSetType, resultSetConcurrency);
return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql, statementInstrumenter);
} }
@Override @Override
@ -95,38 +99,38 @@ public class OpenTelemetryConnection implements Connection {
throws SQLException { throws SQLException {
PreparedStatement statement = PreparedStatement statement =
delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql, statementInstrumenter);
} }
@Override @Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
PreparedStatement statement = delegate.prepareStatement(sql, autoGeneratedKeys); PreparedStatement statement = delegate.prepareStatement(sql, autoGeneratedKeys);
return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql, statementInstrumenter);
} }
@Override @Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
PreparedStatement statement = delegate.prepareStatement(sql, columnIndexes); PreparedStatement statement = delegate.prepareStatement(sql, columnIndexes);
return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql, statementInstrumenter);
} }
@Override @Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
PreparedStatement statement = delegate.prepareStatement(sql, columnNames); PreparedStatement statement = delegate.prepareStatement(sql, columnNames);
return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql); return new OpenTelemetryPreparedStatement<>(statement, dbInfo, sql, statementInstrumenter);
} }
@Override @Override
public CallableStatement prepareCall(String sql) throws SQLException { public CallableStatement prepareCall(String sql) throws SQLException {
CallableStatement statement = delegate.prepareCall(sql); CallableStatement statement = delegate.prepareCall(sql);
return new OpenTelemetryCallableStatement<>(statement, dbInfo, sql); return new OpenTelemetryCallableStatement<>(statement, dbInfo, sql, statementInstrumenter);
} }
@Override @Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException { throws SQLException {
CallableStatement statement = delegate.prepareCall(sql, resultSetType, resultSetConcurrency); CallableStatement statement = delegate.prepareCall(sql, resultSetType, resultSetConcurrency);
return new OpenTelemetryCallableStatement<>(statement, dbInfo, sql); return new OpenTelemetryCallableStatement<>(statement, dbInfo, sql, statementInstrumenter);
} }
@Override @Override
@ -135,7 +139,7 @@ public class OpenTelemetryConnection implements Connection {
throws SQLException { throws SQLException {
CallableStatement statement = CallableStatement statement =
delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
return new OpenTelemetryCallableStatement<>(statement, dbInfo, sql); return new OpenTelemetryCallableStatement<>(statement, dbInfo, sql, statementInstrumenter);
} }
@Override @Override

View File

@ -20,6 +20,7 @@
package io.opentelemetry.instrumentation.jdbc.internal; package io.opentelemetry.instrumentation.jdbc.internal;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo; import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
@ -49,8 +50,9 @@ import java.util.Calendar;
public class OpenTelemetryPreparedStatement<S extends PreparedStatement> public class OpenTelemetryPreparedStatement<S extends PreparedStatement>
extends OpenTelemetryStatement<S> implements PreparedStatement { extends OpenTelemetryStatement<S> implements PreparedStatement {
public OpenTelemetryPreparedStatement(S delegate, DbInfo dbInfo, String query) { public OpenTelemetryPreparedStatement(
super(delegate, dbInfo, query); S delegate, DbInfo dbInfo, String query, Instrumenter<DbRequest, Void> instrumenter) {
super(delegate, dbInfo, query, instrumenter);
} }
@Override @Override

View File

@ -20,10 +20,9 @@
package io.opentelemetry.instrumentation.jdbc.internal; package io.opentelemetry.instrumentation.jdbc.internal;
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcSingletons.instrumenter;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo; import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo;
import java.sql.Connection; import java.sql.Connection;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -41,17 +40,20 @@ public class OpenTelemetryStatement<S extends Statement> implements Statement {
protected final S delegate; protected final S delegate;
protected final DbInfo dbInfo; protected final DbInfo dbInfo;
protected final String query; protected final String query;
protected final Instrumenter<DbRequest, Void> instrumenter;
private final ArrayList<String> batchCommands = new ArrayList<>(); private final ArrayList<String> batchCommands = new ArrayList<>();
OpenTelemetryStatement(S delegate, DbInfo dbInfo) { OpenTelemetryStatement(S delegate, DbInfo dbInfo, Instrumenter<DbRequest, Void> instrumenter) {
this(delegate, dbInfo, null); this(delegate, dbInfo, null, instrumenter);
} }
OpenTelemetryStatement(S delegate, DbInfo dbInfo, String query) { OpenTelemetryStatement(
S delegate, DbInfo dbInfo, String query, Instrumenter<DbRequest, Void> instrumenter) {
this.delegate = delegate; this.delegate = delegate;
this.dbInfo = dbInfo; this.dbInfo = dbInfo;
this.query = query; this.query = query;
this.instrumenter = instrumenter;
} }
@Override @Override
@ -282,19 +284,19 @@ public class OpenTelemetryStatement<S extends Statement> implements Statement {
Context parentContext = Context.current(); Context parentContext = Context.current();
DbRequest request = DbRequest.create(dbInfo, sql); DbRequest request = DbRequest.create(dbInfo, sql);
if (!instrumenter().shouldStart(parentContext, request)) { if (!this.instrumenter.shouldStart(parentContext, request)) {
return callable.call(); return callable.call();
} }
Context context = instrumenter().start(parentContext, request); Context context = this.instrumenter.start(parentContext, request);
T result; T result;
try (Scope ignored = context.makeCurrent()) { try (Scope ignored = context.makeCurrent()) {
result = callable.call(); result = callable.call();
} catch (Throwable t) { } catch (Throwable t) {
instrumenter().end(context, request, null, t); this.instrumenter.end(context, request, null, t);
throw t; throw t;
} }
instrumenter().end(context, request, null, null); this.instrumenter.end(context, request, null, null);
return result; return result;
} }

View File

@ -5,24 +5,29 @@
package io.opentelemetry.instrumentation.jdbc package io.opentelemetry.instrumentation.jdbc
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.api.trace.SpanKind import io.opentelemetry.api.trace.SpanKind
import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo import io.opentelemetry.context.propagation.ContextPropagators
import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryCallableStatement import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryCallableStatement
import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection
import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryPreparedStatement import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryPreparedStatement
import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryStatement import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryStatement
import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo
import io.opentelemetry.instrumentation.test.InstrumentationSpecification import io.opentelemetry.instrumentation.test.InstrumentationSpecification
import io.opentelemetry.instrumentation.test.LibraryTestTrait import io.opentelemetry.instrumentation.test.LibraryTestTrait
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import static io.opentelemetry.api.trace.SpanKind.CLIENT import static io.opentelemetry.api.trace.SpanKind.CLIENT
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcInstrumenterFactory.createStatementInstrumenter
class OpenTelemetryConnectionTest extends InstrumentationSpecification implements LibraryTestTrait { class OpenTelemetryConnectionTest extends InstrumentationSpecification implements LibraryTestTrait {
def "verify create statement"() { def "verify create statement"() {
setup: setup:
def instr = createStatementInstrumenter(openTelemetry)
def dbInfo = getDbInfo() def dbInfo = getDbInfo()
def connection = new OpenTelemetryConnection(new TestConnection(), dbInfo) def connection = new OpenTelemetryConnection(new TestConnection(), dbInfo, instr)
String query = "SELECT * FROM users" String query = "SELECT * FROM users"
def statement = connection.createStatement() def statement = connection.createStatement()
runWithSpan("parent") { runWithSpan("parent") {
@ -63,18 +68,22 @@ class OpenTelemetryConnectionTest extends InstrumentationSpecification implement
def "verify create statement returns otel wrapper"() { def "verify create statement returns otel wrapper"() {
when: when:
def connection = new OpenTelemetryConnection(new TestConnection(), DbInfo.DEFAULT) def ot = OpenTelemetry.propagating(ContextPropagators.noop())
def instr = createStatementInstrumenter(ot)
def connection = new OpenTelemetryConnection(new TestConnection(), DbInfo.DEFAULT, instr)
then: then:
connection.createStatement().class == OpenTelemetryStatement connection.createStatement().class == OpenTelemetryStatement
connection.createStatement(0, 0).class == OpenTelemetryStatement connection.createStatement(0, 0).class == OpenTelemetryStatement
connection.createStatement(0, 0, 0).class == OpenTelemetryStatement connection.createStatement(0, 0, 0).class == OpenTelemetryStatement
connection.createStatement().instrumenter == instr
} }
def "verify prepare statement"() { def "verify prepare statement"() {
setup: setup:
def instr = createStatementInstrumenter(openTelemetry)
def dbInfo = getDbInfo() def dbInfo = getDbInfo()
def connection = new OpenTelemetryConnection(new TestConnection(), dbInfo) def connection = new OpenTelemetryConnection(new TestConnection(), dbInfo, instr)
String query = "SELECT * FROM users" String query = "SELECT * FROM users"
def statement = connection.prepareStatement(query) def statement = connection.prepareStatement(query)
runWithSpan("parent") { runWithSpan("parent") {
@ -115,21 +124,26 @@ class OpenTelemetryConnectionTest extends InstrumentationSpecification implement
def "verify prepare statement returns otel wrapper"() { def "verify prepare statement returns otel wrapper"() {
when: when:
def connection = new OpenTelemetryConnection(new TestConnection(), DbInfo.DEFAULT) def ot = OpenTelemetry.propagating(ContextPropagators.noop())
def instr = createStatementInstrumenter(ot)
def connection = new OpenTelemetryConnection(new TestConnection(), DbInfo.DEFAULT, instr)
String query = "SELECT * FROM users"
then: then:
connection.prepareStatement("SELECT * FROM users").class == OpenTelemetryPreparedStatement connection.prepareStatement(query).class == OpenTelemetryPreparedStatement
connection.prepareStatement("SELECT * FROM users", [0] as int[]).class == OpenTelemetryPreparedStatement connection.prepareStatement(query, [0] as int[]).class == OpenTelemetryPreparedStatement
connection.prepareStatement("SELECT * FROM users", ["id"] as String[]).class == OpenTelemetryPreparedStatement connection.prepareStatement(query, ["id"] as String[]).class == OpenTelemetryPreparedStatement
connection.prepareStatement("SELECT * FROM users", 0).class == OpenTelemetryPreparedStatement connection.prepareStatement(query, 0).class == OpenTelemetryPreparedStatement
connection.prepareStatement("SELECT * FROM users", 0, 0).class == OpenTelemetryPreparedStatement connection.prepareStatement(query, 0, 0).class == OpenTelemetryPreparedStatement
connection.prepareStatement("SELECT * FROM users", 0, 0, 0).class == OpenTelemetryPreparedStatement connection.prepareStatement(query, 0, 0, 0).class == OpenTelemetryPreparedStatement
connection.prepareStatement(query).instrumenter == instr
} }
def "verify prepare call"() { def "verify prepare call"() {
setup: setup:
def instr = createStatementInstrumenter(openTelemetry)
def dbInfo = getDbInfo() def dbInfo = getDbInfo()
def connection = new OpenTelemetryConnection(new TestConnection(), dbInfo) def connection = new OpenTelemetryConnection(new TestConnection(), dbInfo, instr)
String query = "SELECT * FROM users" String query = "SELECT * FROM users"
def statement = connection.prepareCall(query) def statement = connection.prepareCall(query)
runWithSpan("parent") { runWithSpan("parent") {
@ -170,12 +184,16 @@ class OpenTelemetryConnectionTest extends InstrumentationSpecification implement
def "verify prepare call returns otel wrapper"() { def "verify prepare call returns otel wrapper"() {
when: when:
def connection = new OpenTelemetryConnection(new TestConnection(), DbInfo.DEFAULT) def ot = OpenTelemetry.propagating(ContextPropagators.noop())
def instr = createStatementInstrumenter(ot)
def connection = new OpenTelemetryConnection(new TestConnection(), DbInfo.DEFAULT, instr)
String query = "SELECT * FROM users"
then: then:
connection.prepareCall("SELECT * FROM users").class == OpenTelemetryCallableStatement connection.prepareCall(query).class == OpenTelemetryCallableStatement
connection.prepareCall("SELECT * FROM users", 0, 0).class == OpenTelemetryCallableStatement connection.prepareCall(query, 0, 0).class == OpenTelemetryCallableStatement
connection.prepareCall("SELECT * FROM users", 0, 0, 0).class == OpenTelemetryCallableStatement connection.prepareCall(query, 0, 0, 0).class == OpenTelemetryCallableStatement
connection.prepareCall(query).instrumenter == instr
} }
private DbInfo getDbInfo() { private DbInfo getDbInfo() {

View File

@ -5,6 +5,8 @@
package io.opentelemetry.instrumentation.jdbc.datasource package io.opentelemetry.instrumentation.jdbc.datasource
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.context.propagation.ContextPropagators
import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection
import spock.lang.Specification import spock.lang.Specification
@ -12,12 +14,14 @@ class OpenTelemetryDataSourceTest extends Specification {
def "verify get connection"() { def "verify get connection"() {
when: when:
def dataSource = new OpenTelemetryDataSource(new TestDataSource()) def ot = OpenTelemetry.propagating(ContextPropagators.noop())
def dataSource = new OpenTelemetryDataSource(new TestDataSource(), ot)
def connection = dataSource.getConnection() def connection = dataSource.getConnection()
then: then:
connection != null connection != null
connection instanceof OpenTelemetryConnection connection instanceof OpenTelemetryConnection
connection.statementInstrumenter != null
when: when:
def dbInfo = ((OpenTelemetryConnection) connection).dbInfo def dbInfo = ((OpenTelemetryConnection) connection).dbInfo
@ -35,12 +39,14 @@ class OpenTelemetryDataSourceTest extends Specification {
def "verify get connection with username and password"() { def "verify get connection with username and password"() {
when: when:
def dataSource = new OpenTelemetryDataSource(new TestDataSource()) def ot = OpenTelemetry.propagating(ContextPropagators.noop())
def dataSource = new OpenTelemetryDataSource(new TestDataSource(), ot)
def connection = dataSource.getConnection(null, null) def connection = dataSource.getConnection(null, null)
then: then:
connection != null connection != null
connection instanceof OpenTelemetryConnection connection instanceof OpenTelemetryConnection
connection.statementInstrumenter != null
when: when:
def dbInfo = ((OpenTelemetryConnection) connection).dbInfo def dbInfo = ((OpenTelemetryConnection) connection).dbInfo
@ -55,5 +61,4 @@ class OpenTelemetryDataSourceTest extends Specification {
dbInfo.host == "127.0.0.1" dbInfo.host == "127.0.0.1"
dbInfo.port == 5432 dbInfo.port == 5432
} }
} }