Skip to content

Commit 33024dc

Browse files
authored
Add result set wrapping for jdbc library instrumentation (#13646)
1 parent a6dacb3 commit 33024dc

File tree

15 files changed

+1441
-34
lines changed

15 files changed

+1441
-34
lines changed

instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/PreparedStatementInstrumentation.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
1313
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
1414
import static net.bytebuddy.matcher.ElementMatchers.named;
15+
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
1516
import static net.bytebuddy.matcher.ElementMatchers.not;
1617
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
1718
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
@@ -45,7 +46,7 @@ public ElementMatcher<TypeDescription> typeMatcher() {
4546
public void transform(TypeTransformer transformer) {
4647
transformer.applyAdviceToMethod(
4748
nameStartsWith("execute")
48-
.and(not(named("executeBatch")))
49+
.and(not(namedOneOf("executeBatch", "executeLargeBatch")))
4950
.and(takesArguments(0))
5051
.and(isPublic()),
5152
PreparedStatementInstrumentation.class.getName() + "$PreparedStatementAdvice");

instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/StatementInstrumentation.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
1313
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
1414
import static net.bytebuddy.matcher.ElementMatchers.named;
15+
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
1516
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
1617
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
1718

@@ -52,7 +53,7 @@ public void transform(TypeTransformer transformer) {
5253
named("clearBatch").and(isPublic()),
5354
StatementInstrumentation.class.getName() + "$ClearBatchAdvice");
5455
transformer.applyAdviceToMethod(
55-
named("executeBatch").and(takesNoArguments()).and(isPublic()),
56+
namedOneOf("executeBatch", "executeLargeBatch").and(takesNoArguments()).and(isPublic()),
5657
StatementInstrumentation.class.getName() + "$ExecuteBatchAdvice");
5758
}
5859

instrumentation/jdbc/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jdbc/test/JdbcInstrumentationTest.java

+108-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM_NAME;
2626
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER;
2727
import static java.util.Arrays.asList;
28+
import static org.junit.jupiter.api.Assumptions.assumeTrue;
2829

2930
import com.google.common.collect.ImmutableMap;
3031
import com.google.common.collect.Maps;
@@ -61,6 +62,7 @@
6162
import javax.sql.DataSource;
6263
import org.apache.derby.jdbc.EmbeddedDataSource;
6364
import org.apache.derby.jdbc.EmbeddedDriver;
65+
import org.assertj.core.api.ThrowingConsumer;
6466
import org.h2.jdbcx.JdbcDataSource;
6567
import org.hsqldb.jdbc.JDBCDriver;
6668
import org.junit.jupiter.api.AfterAll;
@@ -803,10 +805,81 @@ void testPreparedStatementUpdate(
803805
String url,
804806
String table)
805807
throws SQLException {
808+
testPreparedStatementUpdateImpl(
809+
system,
810+
connection,
811+
username,
812+
query,
813+
spanName,
814+
url,
815+
table,
816+
statement -> assertThat(statement.executeUpdate()).isEqualTo(0));
817+
}
818+
819+
static Stream<Arguments> preparedStatementLargeUpdateStream() throws SQLException {
820+
return Stream.of(
821+
Arguments.of(
822+
"h2",
823+
new org.h2.Driver().connect(jdbcUrls.get("h2"), null),
824+
null,
825+
"CREATE TABLE PS_LARGE_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))",
826+
"CREATE TABLE jdbcunittest.PS_LARGE_H2",
827+
"h2:mem:",
828+
"PS_LARGE_H2"),
829+
Arguments.of(
830+
"h2",
831+
cpDatasources.get("tomcat").get("h2").getConnection(),
832+
null,
833+
"CREATE TABLE PS_LARGE_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))",
834+
"CREATE TABLE jdbcunittest.PS_LARGE_H2_TOMCAT",
835+
"h2:mem:",
836+
"PS_LARGE_H2_TOMCAT"),
837+
Arguments.of(
838+
"h2",
839+
cpDatasources.get("hikari").get("h2").getConnection(),
840+
null,
841+
"CREATE TABLE PS_LARGE_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))",
842+
"CREATE TABLE jdbcunittest.PS_LARGE_H2_HIKARI",
843+
"h2:mem:",
844+
"PS_LARGE_H2_HIKARI"));
845+
}
846+
847+
@ParameterizedTest
848+
@MethodSource("preparedStatementLargeUpdateStream")
849+
void testPreparedStatementLargeUpdate(
850+
String system,
851+
Connection connection,
852+
String username,
853+
String query,
854+
String spanName,
855+
String url,
856+
String table)
857+
throws SQLException {
858+
testPreparedStatementUpdateImpl(
859+
system,
860+
connection,
861+
username,
862+
query,
863+
spanName,
864+
url,
865+
table,
866+
statement -> assertThat(statement.executeLargeUpdate()).isEqualTo(0));
867+
}
868+
869+
void testPreparedStatementUpdateImpl(
870+
String system,
871+
Connection connection,
872+
String username,
873+
String query,
874+
String spanName,
875+
String url,
876+
String table,
877+
ThrowingConsumer<PreparedStatement> action)
878+
throws SQLException {
806879
String sql = connection.nativeSQL(query);
807880
PreparedStatement statement = connection.prepareStatement(sql);
808881
cleanup.deferCleanup(statement);
809-
testing.runWithSpan("parent", () -> assertThat(statement.executeUpdate()).isEqualTo(0));
882+
testing.runWithSpan("parent", () -> action.accept(statement));
810883

811884
testing.waitAndAssertTraces(
812885
trace ->
@@ -1333,7 +1406,39 @@ static Stream<Arguments> batchStream() throws SQLException {
13331406
@MethodSource("batchStream")
13341407
void testBatch(String system, Connection connection, String username, String url)
13351408
throws SQLException {
1336-
String tableName = "simple_batch_test";
1409+
testBatchImpl(
1410+
system,
1411+
connection,
1412+
username,
1413+
url,
1414+
"simple_batch_test",
1415+
statement -> assertThat(statement.executeBatch()).isEqualTo(new int[] {1, 1}));
1416+
}
1417+
1418+
@ParameterizedTest
1419+
@MethodSource("batchStream")
1420+
void testLargeBatch(String system, Connection connection, String username, String url)
1421+
throws SQLException {
1422+
// derby and hsqldb used in this test don't support executeLargeBatch
1423+
assumeTrue("h2".equals(system));
1424+
1425+
testBatchImpl(
1426+
system,
1427+
connection,
1428+
username,
1429+
url,
1430+
"simple_batch_test_large",
1431+
statement -> assertThat(statement.executeLargeBatch()).isEqualTo(new long[] {1, 1}));
1432+
}
1433+
1434+
private static void testBatchImpl(
1435+
String system,
1436+
Connection connection,
1437+
String username,
1438+
String url,
1439+
String tableName,
1440+
ThrowingConsumer<Statement> action)
1441+
throws SQLException {
13371442
Statement createTable = connection.createStatement();
13381443
createTable.execute("CREATE TABLE " + tableName + " (id INTEGER not NULL, PRIMARY KEY ( id ))");
13391444
cleanup.deferCleanup(createTable);
@@ -1347,8 +1452,7 @@ void testBatch(String system, Connection connection, String username, String url
13471452
statement.clearBatch();
13481453
statement.addBatch("INSERT INTO " + tableName + " VALUES(1)");
13491454
statement.addBatch("INSERT INTO " + tableName + " VALUES(2)");
1350-
testing.runWithSpan(
1351-
"parent", () -> assertThat(statement.executeBatch()).isEqualTo(new int[] {1, 1}));
1455+
testing.runWithSpan("parent", () -> action.accept(statement));
13521456

13531457
testing.waitAndAssertTraces(
13541458
trace ->

instrumentation/jdbc/library/build.gradle.kts

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ dependencies {
1616
}
1717

1818
tasks {
19+
// We cannot use "--release" javac option here because that will forbid using apis that were added
20+
// in later versions. In JDBC wrappers we wish to implement delegation for methods that are not
21+
// present in jdk8.
22+
compileJava {
23+
sourceCompatibility = "1.8"
24+
targetCompatibility = "1.8"
25+
options.release.set(null as Int?)
26+
}
27+
1928
shadowJar {
2029
dependencies {
2130
// including only current module excludes its transitive dependencies

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ public Connection connect(String url, Properties info) throws SQLException {
244244

245245
Instrumenter<DbRequest, Void> statementInstrumenter =
246246
JdbcInstrumenterFactory.createStatementInstrumenter(openTelemetry);
247-
return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter);
247+
return OpenTelemetryConnection.create(connection, dbInfo, statementInstrumenter);
248248
}
249249

250250
@Override

instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/datasource/OpenTelemetryDataSource.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,14 @@ public OpenTelemetryDataSource(DataSource delegate, OpenTelemetry openTelemetry)
9393
public Connection getConnection() throws SQLException {
9494
Connection connection = wrapCall(delegate::getConnection);
9595
DbInfo dbInfo = getDbInfo(connection);
96-
return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter);
96+
return OpenTelemetryConnection.create(connection, dbInfo, statementInstrumenter);
9797
}
9898

9999
@Override
100100
public Connection getConnection(String username, String password) throws SQLException {
101101
Connection connection = wrapCall(() -> delegate.getConnection(username, password));
102102
DbInfo dbInfo = getDbInfo(connection);
103-
return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter);
103+
return OpenTelemetryConnection.create(connection, dbInfo, statementInstrumenter);
104104
}
105105

106106
@Override

instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/OpenTelemetryCallableStatement.java

+51-5
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,15 @@
3535
import java.sql.Ref;
3636
import java.sql.RowId;
3737
import java.sql.SQLException;
38+
import java.sql.SQLType;
3839
import java.sql.SQLXML;
3940
import java.sql.Time;
4041
import java.sql.Timestamp;
4142
import java.util.Calendar;
4243
import java.util.Map;
4344

44-
/**
45-
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
46-
* any time.
47-
*/
48-
public class OpenTelemetryCallableStatement<S extends CallableStatement>
45+
@SuppressWarnings("OverloadMethodsDeclarationOrder")
46+
class OpenTelemetryCallableStatement<S extends CallableStatement>
4947
extends OpenTelemetryPreparedStatement<S> implements CallableStatement {
5048

5149
public OpenTelemetryCallableStatement(
@@ -489,6 +487,7 @@ public void setBinaryStream(String parameterName, InputStream x) throws SQLExcep
489487
delegate.setBinaryStream(parameterName, x);
490488
}
491489

490+
@SuppressWarnings("UngroupedOverloads")
492491
@Override
493492
public void setObject(String parameterName, Object x, int targetSqlType, int scale)
494493
throws SQLException {
@@ -710,4 +709,51 @@ public Reader getCharacterStream(int parameterIndex) throws SQLException {
710709
public Reader getCharacterStream(String parameterName) throws SQLException {
711710
return delegate.getCharacterStream(parameterName);
712711
}
712+
713+
// JDBC 4.2
714+
715+
@Override
716+
public void setObject(String parameterName, Object x, SQLType targetSqlType, int scaleOrLength)
717+
throws SQLException {
718+
delegate.setObject(parameterName, x, targetSqlType, scaleOrLength);
719+
}
720+
721+
@Override
722+
public void setObject(String parameterName, Object x, SQLType targetSqlType) throws SQLException {
723+
delegate.setObject(parameterName, x, targetSqlType);
724+
}
725+
726+
@Override
727+
public void registerOutParameter(int parameterIndex, SQLType sqlType) throws SQLException {
728+
delegate.registerOutParameter(parameterIndex, sqlType);
729+
}
730+
731+
@Override
732+
public void registerOutParameter(int parameterIndex, SQLType sqlType, int scale)
733+
throws SQLException {
734+
delegate.registerOutParameter(parameterIndex, sqlType, scale);
735+
}
736+
737+
@Override
738+
public void registerOutParameter(int parameterIndex, SQLType sqlType, String typeName)
739+
throws SQLException {
740+
delegate.registerOutParameter(parameterIndex, sqlType, typeName);
741+
}
742+
743+
@Override
744+
public void registerOutParameter(String parameterName, SQLType sqlType) throws SQLException {
745+
delegate.registerOutParameter(parameterName, sqlType);
746+
}
747+
748+
@Override
749+
public void registerOutParameter(String parameterName, SQLType sqlType, int scale)
750+
throws SQLException {
751+
delegate.registerOutParameter(parameterName, sqlType, scale);
752+
}
753+
754+
@Override
755+
public void registerOutParameter(String parameterName, SQLType sqlType, String typeName)
756+
throws SQLException {
757+
delegate.registerOutParameter(parameterName, sqlType, typeName);
758+
}
713759
}

0 commit comments

Comments
 (0)