From b2038d511c0dc7f47b6115e68f905ad97a424900 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Thu, 4 Dec 2025 18:28:21 -0800 Subject: [PATCH 01/26] Implement db.query.summary for SqlClientAttributesExtractor --- .../semconv/db/DbClientSpanNameExtractor.java | 14 ++ .../api/incubator/semconv/db/MultiQuery.java | 20 +- .../db/SqlClientAttributesExtractor.java | 3 + .../semconv/db/SqlStatementInfo.java | 49 ++++- .../semconv/db/SqlStatementSanitizer.java | 2 +- .../src/main/jflex/SqlSanitizer.jflex | 61 +++++- .../db/DbClientSpanNameExtractorTest.java | 15 +- .../db/SqlClientAttributesExtractorTest.java | 28 ++- .../semconv/db/SqlStatementSanitizerTest.java | 77 +++++++- .../api/internal/InstrumenterContextTest.java | 2 +- .../src/test/java/CassandraClientTest.java | 26 ++- .../v4/common/AbstractCassandraTest.java | 23 ++- .../v4_4/AbstractCassandra44Test.java | 9 +- .../v2_0/CouchbaseQuerySanitizer.java | 4 +- .../hibernate/v6_0/CriteriaTest.java | 8 +- .../hibernate/v6_0/EntityManagerTest.java | 24 ++- .../test/java/spring/jpa/SpringJpaTest.java | 78 ++++++-- .../scalaexecutors/SlickTest.scala | 12 +- .../AbstractJdbcInstrumentationTest.java | 186 +++++++++++++----- .../v1_0/AbstractR2dbcStatementTest.java | 11 +- .../spring/data/AbstractSpringJpaTest.java | 102 +++++++--- .../vertx/v4_0/sql/VertxSqlClientTest.java | 36 +++- .../vertx/v5_0/sql/VertxSqlClientTest.java | 34 +++- 23 files changed, 663 insertions(+), 161 deletions(-) diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java index 022529e16e4e..9193aa946303 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java @@ -111,9 +111,18 @@ public String extract(REQUEST request) { namespace, sanitizedStatement.getOperation(), sanitizedStatement.getMainIdentifier()); } + // For stable semconv, use query summary as span name if available if (rawQueryTexts.size() == 1) { SqlStatementInfo sanitizedStatement = SqlStatementSanitizerUtil.sanitize(rawQueryTexts.iterator().next()); + String querySummary = sanitizedStatement.getQuerySummary(); + if (querySummary != null) { + if (isBatch(request)) { + return "BATCH " + querySummary; + } + return querySummary; + } + // Fall back to old behavior if no query summary String operation = sanitizedStatement.getOperation(); if (isBatch(request)) { operation = "BATCH " + operation; @@ -122,6 +131,11 @@ public String extract(REQUEST request) { } MultiQuery multiQuery = MultiQuery.analyze(rawQueryTexts, false); + String querySummary = multiQuery.getQuerySummary(); + if (querySummary != null) { + return "BATCH " + querySummary; + } + // Fall back to old behavior if no query summary return computeSpanName( namespace, multiQuery.getOperation() != null ? "BATCH " + multiQuery.getOperation() : "BATCH", diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java index 07cb81af397c..8241c3b7e397 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java @@ -14,12 +14,17 @@ class MultiQuery { @Nullable private final String mainIdentifier; @Nullable private final String operation; + @Nullable private final String querySummary; private final Set statements; private MultiQuery( - @Nullable String mainIdentifier, @Nullable String operation, Set statements) { + @Nullable String mainIdentifier, + @Nullable String operation, + @Nullable String querySummary, + Set statements) { this.mainIdentifier = mainIdentifier; this.operation = operation; + this.querySummary = querySummary; this.statements = statements; } @@ -27,6 +32,7 @@ static MultiQuery analyze( Collection rawQueryTexts, boolean statementSanitizationEnabled) { UniqueValue uniqueMainIdentifier = new UniqueValue(); UniqueValue uniqueOperation = new UniqueValue(); + UniqueValue uniqueQuerySummary = new UniqueValue(); Set uniqueStatements = new LinkedHashSet<>(); for (String rawQueryText : rawQueryTexts) { SqlStatementInfo sanitizedStatement = SqlStatementSanitizerUtil.sanitize(rawQueryText); @@ -34,12 +40,17 @@ static MultiQuery analyze( uniqueMainIdentifier.set(mainIdentifier); String operation = sanitizedStatement.getOperation(); uniqueOperation.set(operation); + String querySummary = sanitizedStatement.getQuerySummary(); + uniqueQuerySummary.set(querySummary); uniqueStatements.add( statementSanitizationEnabled ? sanitizedStatement.getFullStatement() : rawQueryText); } return new MultiQuery( - uniqueMainIdentifier.getValue(), uniqueOperation.getValue(), uniqueStatements); + uniqueMainIdentifier.getValue(), + uniqueOperation.getValue(), + uniqueQuerySummary.getValue(), + uniqueStatements); } @Nullable @@ -52,6 +63,11 @@ public String getOperation() { return operation; } + @Nullable + public String getQuerySummary() { + return querySummary; + } + public Set getStatements() { return statements; } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java index 74ee68f5472c..631deede44cc 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java @@ -9,6 +9,7 @@ import static io.opentelemetry.semconv.DbAttributes.DB_COLLECTION_NAME; import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_BATCH_SIZE; import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_TEXT; import io.opentelemetry.api.common.AttributeKey; @@ -119,6 +120,7 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST DB_QUERY_TEXT, statementSanitizationEnabled ? sanitizedStatement.getFullStatement() : rawQueryText); internalSet(attributes, DB_OPERATION_NAME, isBatch ? "BATCH " + operation : operation); + internalSet(attributes, DB_QUERY_SUMMARY, sanitizedStatement.getQuerySummary()); if (!SQL_CALL.equals(operation)) { internalSet(attributes, DB_COLLECTION_NAME, sanitizedStatement.getMainIdentifier()); } @@ -130,6 +132,7 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST String operation = multiQuery.getOperation() != null ? "BATCH " + multiQuery.getOperation() : "BATCH"; internalSet(attributes, DB_OPERATION_NAME, operation); + internalSet(attributes, DB_QUERY_SUMMARY, multiQuery.getQuerySummary()); if (multiQuery.getMainIdentifier() != null && (multiQuery.getOperation() == null || !SQL_CALL.equals(multiQuery.getOperation()))) { diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java index 04320206b060..557ded4a0ac1 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java @@ -11,9 +11,34 @@ @AutoValue public abstract class SqlStatementInfo { + private static final int QUERY_SUMMARY_MAX_LENGTH = 255; + public static SqlStatementInfo create( - @Nullable String fullStatement, @Nullable String operation, @Nullable String identifier) { - return new AutoValue_SqlStatementInfo(fullStatement, operation, identifier); + @Nullable String fullStatement, + @Nullable String operation, + @Nullable String identifier, + @Nullable String querySummary) { + String truncatedQuerySummary = truncateQuerySummary(querySummary); + return new AutoValue_SqlStatementInfo( + fullStatement, operation, identifier, truncatedQuerySummary); + } + + /** + * Truncates the query summary to {@link #QUERY_SUMMARY_MAX_LENGTH} characters, ensuring + * truncation does not occur within an operation name or target. + */ + @Nullable + private static String truncateQuerySummary(@Nullable String querySummary) { + if (querySummary == null || querySummary.length() <= QUERY_SUMMARY_MAX_LENGTH) { + return querySummary; + } + // Truncate at the last space before the limit to avoid cutting in the middle of an identifier + int lastSpace = querySummary.lastIndexOf(' ', QUERY_SUMMARY_MAX_LENGTH); + if (lastSpace > 0) { + return querySummary.substring(0, lastSpace); + } + // If no space found, truncate at the limit + return querySummary.substring(0, QUERY_SUMMARY_MAX_LENGTH); } @Nullable @@ -24,4 +49,24 @@ public static SqlStatementInfo create( @Nullable public abstract String getMainIdentifier(); + + /** + * Returns a low cardinality summary of the database query suitable for use as a span name or + * metric attribute. + * + *

The summary contains operations (e.g., SELECT, INSERT) and their targets (e.g., table names) + * in the order they appear in the query. For example: + * + *

    + *
  • {@code SELECT wuser_table} + *
  • {@code INSERT shipping_details SELECT orders} + *
  • {@code SELECT songs artists} (multiple tables) + *
+ * + * @see Generating + * a summary of the query + */ + @Nullable + public abstract String getQuerySummary(); } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizer.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizer.java index 587b05d9d178..c47b7a288912 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizer.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizer.java @@ -39,7 +39,7 @@ public SqlStatementInfo sanitize(@Nullable String statement) { public SqlStatementInfo sanitize(@Nullable String statement, SqlDialect dialect) { if (!statementSanitizationEnabled || statement == null) { - return SqlStatementInfo.create(statement, null, null); + return SqlStatementInfo.create(statement, null, null, null); } // sanitization result will not be cached for statements larger than the threshold to avoid // cache growing too large diff --git a/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex b/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex index 83a59fc0abc9..000260cc4ea4 100644 --- a/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex +++ b/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex @@ -53,7 +53,7 @@ WHITESPACE = [ \t\r\n]+ return sanitizer.getResult(); } catch (java.io.IOException e) { // should never happen - return SqlStatementInfo.create(null, null, null); + return SqlStatementInfo.create(null, null, null, null); } } @@ -65,6 +65,10 @@ WHITESPACE = [ \t\r\n]+ private static final String IN_STATEMENT_NORMALIZED = "$1(?)"; private final StringBuilder builder = new StringBuilder(); + // Builds the query summary: operations and targets in order of appearance + private final StringBuilder querySummaryBuilder = new StringBuilder(); + // Tracks the last item added to query summary to avoid duplicates + private String lastQuerySummaryItem = null; private void appendCurrentFragment() { builder.append(zzBuffer, zzStartRead, zzMarkedPos - zzStartRead); @@ -74,6 +78,27 @@ WHITESPACE = [ \t\r\n]+ return builder.length() > LIMIT; } + private void appendOperationToSummary(String operation) { + // Avoid duplicate entries when subqueries have same operation + if (operation != null && !operation.equals(lastQuerySummaryItem)) { + if (querySummaryBuilder.length() > 0) { + querySummaryBuilder.append(' '); + } + querySummaryBuilder.append(operation); + lastQuerySummaryItem = operation; + } + } + + private void appendTargetToSummary(String target) { + if (target != null && !target.equals(lastQuerySummaryItem)) { + if (querySummaryBuilder.length() > 0) { + querySummaryBuilder.append(' '); + } + querySummaryBuilder.append(target); + lastQuerySummaryItem = target; + } + } + private String removeQuotes(String identifierName, String quote) { // remove quotes from the start and end of the identifier ("table" is transformed to table), if // identifier contains quote anywhere else besides start and end leave it as is (quotes are not @@ -164,8 +189,8 @@ WHITESPACE = [ \t\r\n]+ return false; } - SqlStatementInfo getResult(String fullStatement) { - return SqlStatementInfo.create(fullStatement, getClass().getSimpleName().toUpperCase(java.util.Locale.ROOT), mainIdentifier); + SqlStatementInfo getResult(String fullStatement, String querySummary) { + return SqlStatementInfo.create(fullStatement, getClass().getSimpleName().toUpperCase(java.util.Locale.ROOT), mainIdentifier, querySummary); } } @@ -179,6 +204,7 @@ WHITESPACE = [ \t\r\n]+ boolean handleOperationTarget(String target) { operationTarget = target; + appendOperationToSummary(target); expectingOperationTarget = false; return false; } @@ -191,23 +217,24 @@ WHITESPACE = [ \t\r\n]+ boolean handleIdentifier() { if (shouldHandleIdentifier()) { mainIdentifier = readIdentifierName(); + appendTargetToSummary(mainIdentifier); } return true; } - SqlStatementInfo getResult(String fullStatement) { + SqlStatementInfo getResult(String fullStatement, String querySummary) { if (!"".equals(operationTarget)) { - return SqlStatementInfo.create(fullStatement, getClass().getSimpleName().toUpperCase(java.util.Locale.ROOT) + " " + operationTarget, mainIdentifier); + return SqlStatementInfo.create(fullStatement, getClass().getSimpleName().toUpperCase(java.util.Locale.ROOT) + " " + operationTarget, mainIdentifier, querySummary); } - return super.getResult(fullStatement); + return super.getResult(fullStatement, querySummary); } } private static class NoOp extends Operation { static final Operation INSTANCE = new NoOp(); - SqlStatementInfo getResult(String fullStatement) { - return SqlStatementInfo.create(fullStatement, null, null); + SqlStatementInfo getResult(String fullStatement, String querySummary) { + return SqlStatementInfo.create(fullStatement, null, null, querySummary); } } @@ -263,6 +290,7 @@ WHITESPACE = [ \t\r\n]+ } mainIdentifier = readIdentifierName(); + appendTargetToSummary(mainIdentifier); mainTableSetAlready = true; expectingTableName = false; // start counting identifiers after encountering main from clause @@ -299,6 +327,7 @@ WHITESPACE = [ \t\r\n]+ } mainIdentifier = readIdentifierName(); + appendTargetToSummary(mainIdentifier); return true; } } @@ -317,6 +346,7 @@ WHITESPACE = [ \t\r\n]+ } mainIdentifier = readIdentifierName(); + appendTargetToSummary(mainIdentifier); return true; } } @@ -324,6 +354,7 @@ WHITESPACE = [ \t\r\n]+ private class Update extends Operation { boolean handleIdentifier() { mainIdentifier = readIdentifierName(); + appendTargetToSummary(mainIdentifier); return true; } } @@ -331,6 +362,7 @@ WHITESPACE = [ \t\r\n]+ private class Call extends Operation { boolean handleIdentifier() { mainIdentifier = readIdentifierName(); + appendTargetToSummary(mainIdentifier); return true; } @@ -343,6 +375,7 @@ WHITESPACE = [ \t\r\n]+ private class Merge extends Operation { boolean handleIdentifier() { mainIdentifier = readIdentifierName(); + appendTargetToSummary(mainIdentifier); return true; } } @@ -365,7 +398,8 @@ WHITESPACE = [ \t\r\n]+ // Normalize all 'in (?, ?, ...)' statements to in (?) to reduce cardinality String normalizedStatement = IN_STATEMENT_PATTERN.matcher(fullStatement).replaceAll(IN_STATEMENT_NORMALIZED); - return operation.getResult(normalizedStatement); + String querySummary = querySummaryBuilder.length() > 0 ? querySummaryBuilder.toString() : null; + return operation.getResult(normalizedStatement, querySummary); } %} @@ -377,6 +411,7 @@ WHITESPACE = [ \t\r\n]+ "SELECT" { if (!insideComment) { setOperation(new Select()); + appendOperationToSummary("SELECT"); } appendCurrentFragment(); if (isOverLimit()) return YYEOF; @@ -384,6 +419,7 @@ WHITESPACE = [ \t\r\n]+ "INSERT" { if (!insideComment) { setOperation(new Insert()); + appendOperationToSummary("INSERT"); } appendCurrentFragment(); if (isOverLimit()) return YYEOF; @@ -391,6 +427,7 @@ WHITESPACE = [ \t\r\n]+ "DELETE" { if (!insideComment) { setOperation(new Delete()); + appendOperationToSummary("DELETE"); } appendCurrentFragment(); if (isOverLimit()) return YYEOF; @@ -398,6 +435,7 @@ WHITESPACE = [ \t\r\n]+ "UPDATE" { if (!insideComment) { setOperation(new Update()); + appendOperationToSummary("UPDATE"); } appendCurrentFragment(); if (isOverLimit()) return YYEOF; @@ -405,6 +443,7 @@ WHITESPACE = [ \t\r\n]+ "CALL" { if (!insideComment) { setOperation(new Call()); + appendOperationToSummary("CALL"); } appendCurrentFragment(); if (isOverLimit()) return YYEOF; @@ -412,6 +451,7 @@ WHITESPACE = [ \t\r\n]+ "MERGE" { if (!insideComment) { setOperation(new Merge()); + appendOperationToSummary("MERGE"); } appendCurrentFragment(); if (isOverLimit()) return YYEOF; @@ -419,6 +459,7 @@ WHITESPACE = [ \t\r\n]+ "CREATE" { if (!insideComment) { setOperation(new Create()); + appendOperationToSummary("CREATE"); } appendCurrentFragment(); if (isOverLimit()) return YYEOF; @@ -426,6 +467,7 @@ WHITESPACE = [ \t\r\n]+ "DROP" { if (!insideComment) { setOperation(new Drop()); + appendOperationToSummary("DROP"); } appendCurrentFragment(); if (isOverLimit()) return YYEOF; @@ -433,6 +475,7 @@ WHITESPACE = [ \t\r\n]+ "ALTER" { if (!insideComment) { setOperation(new Alter()); + appendOperationToSummary("ALTER"); } appendCurrentFragment(); if (isOverLimit()) return YYEOF; diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java index 36ec3ff43fb5..0d307e65afaa 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java @@ -37,7 +37,11 @@ void shouldExtractFullSpanName() { String spanName = underTest.extract(dbRequest); // then - assertEquals("SELECT database.table", spanName); + // For stable semconv, span name is the query summary (without namespace) + // For old semconv, span name includes the namespace + assertEquals( + SemconvStability.emitStableDatabaseSemconv() ? "SELECT table" : "SELECT database.table", + spanName); } @Test @@ -153,9 +157,10 @@ void shouldExtractFullSpanNameForBatch() { String spanName = underTest.extract(dbRequest); // then + // For stable semconv, multi-query batch uses query summary + // For old semconv, only namespace is used assertEquals( - SemconvStability.emitStableDatabaseSemconv() ? "BATCH INSERT database.table" : "database", - spanName); + SemconvStability.emitStableDatabaseSemconv() ? "BATCH INSERT table" : "database", spanName); } @Test @@ -176,9 +181,11 @@ void shouldExtractFullSpanNameForSingleQueryBatch() { String spanName = underTest.extract(dbRequest); // then + // For stable semconv, single-query batch uses query summary with BATCH prefix + // For old semconv, it uses the full span name format with namespace assertEquals( SemconvStability.emitStableDatabaseSemconv() - ? "BATCH INSERT database.table" + ? "BATCH INSERT table" : "INSERT database.table", spanName); } diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java index e5dc0307cb95..8cd57e634c68 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java @@ -130,7 +130,8 @@ void shouldExtractAllAttributes() { entry(DbAttributes.DB_NAMESPACE, "potatoes"), entry(DbAttributes.DB_QUERY_TEXT, "SELECT * FROM potato WHERE id=?"), entry(DbAttributes.DB_OPERATION_NAME, "SELECT"), - entry(DbAttributes.DB_COLLECTION_NAME, "potato")); + entry(DbAttributes.DB_COLLECTION_NAME, "potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "SELECT potato")); } else if (SemconvStability.emitOldDatabaseSemconv()) { assertThat(startAttributes.build()) .containsOnly( @@ -148,7 +149,8 @@ void shouldExtractAllAttributes() { entry(DbAttributes.DB_NAMESPACE, "potatoes"), entry(DbAttributes.DB_QUERY_TEXT, "SELECT * FROM potato WHERE id=?"), entry(DbAttributes.DB_OPERATION_NAME, "SELECT"), - entry(DbAttributes.DB_COLLECTION_NAME, "potato")); + entry(DbAttributes.DB_COLLECTION_NAME, "potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "SELECT potato")); } assertThat(endAttributes.build().isEmpty()).isTrue(); @@ -176,7 +178,8 @@ void shouldNotExtractTableIfAttributeIsNotSet() { entry(DbIncubatingAttributes.DB_STATEMENT, "SELECT *"), entry(DbIncubatingAttributes.DB_OPERATION, "SELECT"), entry(DbAttributes.DB_QUERY_TEXT, "SELECT *"), - entry(DbAttributes.DB_OPERATION_NAME, "SELECT")); + entry(DbAttributes.DB_OPERATION_NAME, "SELECT"), + entry(DbAttributes.DB_QUERY_SUMMARY, "SELECT")); } else if (SemconvStability.emitOldDatabaseSemconv()) { assertThat(attributes.build()) .containsOnly( @@ -186,7 +189,8 @@ void shouldNotExtractTableIfAttributeIsNotSet() { assertThat(attributes.build()) .containsOnly( entry(DbAttributes.DB_QUERY_TEXT, "SELECT *"), - entry(DbAttributes.DB_OPERATION_NAME, "SELECT")); + entry(DbAttributes.DB_OPERATION_NAME, "SELECT"), + entry(DbAttributes.DB_QUERY_SUMMARY, "SELECT")); } } @@ -217,7 +221,8 @@ void shouldExtractTableToSpecifiedKey() { entry(DbIncubatingAttributes.DB_CASSANDRA_TABLE, "table"), entry(DbAttributes.DB_QUERY_TEXT, "SELECT * FROM table"), entry(DbAttributes.DB_OPERATION_NAME, "SELECT"), - entry(DbAttributes.DB_COLLECTION_NAME, "table")); + entry(DbAttributes.DB_COLLECTION_NAME, "table"), + entry(DbAttributes.DB_QUERY_SUMMARY, "SELECT table")); } else if (SemconvStability.emitOldDatabaseSemconv()) { assertThat(attributes.build()) .containsOnly( @@ -229,7 +234,8 @@ void shouldExtractTableToSpecifiedKey() { .containsOnly( entry(DbAttributes.DB_QUERY_TEXT, "SELECT * FROM table"), entry(DbAttributes.DB_OPERATION_NAME, "SELECT"), - entry(DbAttributes.DB_COLLECTION_NAME, "table")); + entry(DbAttributes.DB_COLLECTION_NAME, "table"), + entry(DbAttributes.DB_QUERY_SUMMARY, "SELECT table")); } } @@ -279,6 +285,7 @@ void shouldExtractSingleQueryBatchAttributes() { entry(DbAttributes.DB_QUERY_TEXT, "INSERT INTO potato VALUES(?)"), entry(DbAttributes.DB_OPERATION_NAME, "BATCH INSERT"), entry(DbAttributes.DB_COLLECTION_NAME, "potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "INSERT potato"), entry(DB_OPERATION_BATCH_SIZE, 2L)); } else if (SemconvStability.emitOldDatabaseSemconv()) { assertThat(startAttributes.build()) @@ -294,6 +301,7 @@ void shouldExtractSingleQueryBatchAttributes() { entry(DbAttributes.DB_QUERY_TEXT, "INSERT INTO potato VALUES(?)"), entry(DbAttributes.DB_OPERATION_NAME, "BATCH INSERT"), entry(DbAttributes.DB_COLLECTION_NAME, "potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "INSERT potato"), entry(DB_OPERATION_BATCH_SIZE, 2L)); } @@ -331,6 +339,7 @@ void shouldExtractMultiQueryBatchAttributes() { entry(DbAttributes.DB_QUERY_TEXT, "INSERT INTO potato VALUES(?)"), entry(DbAttributes.DB_OPERATION_NAME, "BATCH INSERT"), entry(DbAttributes.DB_COLLECTION_NAME, "potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "INSERT potato"), entry(DB_OPERATION_BATCH_SIZE, 2L)); } else if (SemconvStability.emitOldDatabaseSemconv()) { assertThat(startAttributes.build()) @@ -342,6 +351,7 @@ void shouldExtractMultiQueryBatchAttributes() { entry(DbAttributes.DB_QUERY_TEXT, "INSERT INTO potato VALUES(?)"), entry(DbAttributes.DB_OPERATION_NAME, "BATCH INSERT"), entry(DbAttributes.DB_COLLECTION_NAME, "potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "INSERT potato"), entry(DB_OPERATION_BATCH_SIZE, 2L)); } @@ -379,7 +389,8 @@ void shouldIgnoreBatchSizeOne() { entry(DbAttributes.DB_NAMESPACE, "potatoes"), entry(DbAttributes.DB_QUERY_TEXT, "INSERT INTO potato VALUES(?)"), entry(DbAttributes.DB_OPERATION_NAME, "INSERT"), - entry(DbAttributes.DB_COLLECTION_NAME, "potato")); + entry(DbAttributes.DB_COLLECTION_NAME, "potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "INSERT potato")); } else if (SemconvStability.emitOldDatabaseSemconv()) { assertThat(startAttributes.build()) .containsOnly( @@ -393,7 +404,8 @@ void shouldIgnoreBatchSizeOne() { entry(DbAttributes.DB_NAMESPACE, "potatoes"), entry(DbAttributes.DB_QUERY_TEXT, "INSERT INTO potato VALUES(?)"), entry(DbAttributes.DB_OPERATION_NAME, "INSERT"), - entry(DbAttributes.DB_COLLECTION_NAME, "potato")); + entry(DbAttributes.DB_COLLECTION_NAME, "potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "INSERT potato")); } assertThat(endAttributes.build().isEmpty()).isTrue(); diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java index 7e296de36c7c..05343014855b 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java @@ -51,11 +51,13 @@ void veryLongSelectStatementsAreOk() { String query = sb.toString(); String sanitizedQuery = query.replace("=123", "=?").substring(0, AutoSqlSanitizer.LIMIT); - SqlStatementInfo expected = SqlStatementInfo.create(sanitizedQuery, "SELECT", "table"); SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(query); - assertThat(result).isEqualTo(expected); + assertThat(result.getFullStatement()).isEqualTo(sanitizedQuery); + assertThat(result.getOperation()).isEqualTo("SELECT"); + assertThat(result.getMainIdentifier()).isEqualTo("table"); + assertThat(result.getQuerySummary()).isEqualTo("SELECT table"); } @ParameterizedTest @@ -156,6 +158,73 @@ public void largeStatementCached() { assertThat(SqlStatementSanitizer.isCached(largeStatement)).isFalse(); } + @ParameterizedTest + @MethodSource("querySummaryArgs") + void querySummary(String sql, String expectedSummary) { + SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(sql); + assertThat(result.getQuerySummary()).isEqualTo(expectedSummary); + } + + @Test + void querySummaryIsTruncated() { + // Build a query with many tables to exceed 255 character limit + StringBuilder sql = new StringBuilder("SELECT * FROM "); + for (int i = 0; i < 50; i++) { + if (i > 0) { + sql.append(", "); + } + sql.append("very_long_table_name_").append(i); + } + String result = SqlStatementSanitizer.create(true).sanitize(sql.toString()).getQuerySummary(); + assertThat(result).isNotNull(); + assertThat(result.length()).isLessThanOrEqualTo(255); + // For implicit join (comma-separated tables), mainIdentifier is null so only first table is in + // summary + assertThat(result).isEqualTo("SELECT very_long_table_name_0"); + } + + private static Stream querySummaryArgs() { + return Stream.of( + // Basic SELECT + Arguments.of("SELECT * FROM wuser_table", "SELECT wuser_table"), + Arguments.of("SELECT * FROM wuser_table WHERE username = ?", "SELECT wuser_table"), + // INSERT with SELECT subquery - INSERT target + SELECT operation (SELECT target not tracked + // after extraction done) + Arguments.of( + "INSERT INTO shipping_details (order_id, address) SELECT order_id, address FROM orders WHERE order_id = ?", + "INSERT shipping_details SELECT"), + // SELECT with multiple tables (implicit join) - only first table tracked since extraction + // stops on comma + Arguments.of( + "SELECT * FROM songs, artists WHERE songs.artist_id == artists.id", "SELECT songs"), + // SELECT with subquery in FROM - only SELECT operation, no table from subquery + Arguments.of( + "SELECT order_date FROM (SELECT * FROM orders o JOIN customers c ON o.customer_id = c.customer_id)", + "SELECT"), + // SELECT with JOIN - first table tracked, extraction stops on JOIN + Arguments.of("SELECT * FROM table1 JOIN table2 ON table1.id = table2.id", "SELECT table1"), + // DELETE + Arguments.of("DELETE FROM users WHERE id = ?", "DELETE users"), + // UPDATE + Arguments.of("UPDATE users SET name = ? WHERE id = ?", "UPDATE users"), + // CALL stored procedure + Arguments.of("CALL some_stored_procedure", "CALL some_stored_procedure"), + // MERGE + Arguments.of("MERGE INTO target USING source ON target.id = source.id", "MERGE target"), + // CREATE TABLE + Arguments.of("CREATE TABLE users (id INT, name VARCHAR(100))", "CREATE TABLE users"), + // DROP TABLE + Arguments.of("DROP TABLE users", "DROP TABLE users"), + // ALTER TABLE + Arguments.of("ALTER TABLE users ADD COLUMN email VARCHAR(100)", "ALTER TABLE users"), + // CREATE INDEX (no table in summary) + Arguments.of("CREATE INDEX idx_name ON users (name)", "CREATE INDEX"), + // Unknown operation + Arguments.of("and now for something completely different", null), + Arguments.of("", null), + Arguments.of(null, null)); + } + private static Stream sqlArgs() { return Stream.of( Arguments.of("SELECT * FROM TABLE WHERE FIELD=1234", "SELECT * FROM TABLE WHERE FIELD=?"), @@ -267,12 +336,12 @@ private static Stream couchbaseArgs() { } private static Function expect(String operation, String identifier) { - return sql -> SqlStatementInfo.create(sql, operation, identifier); + return sql -> SqlStatementInfo.create(sql, operation, identifier, null); } private static Function expect( String sql, String operation, String identifier) { - return ignored -> SqlStatementInfo.create(sql, operation, identifier); + return ignored -> SqlStatementInfo.create(sql, operation, identifier, null); } private static Stream simplifyArgs() { diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java index 7aa8897b7911..5c1e692ed167 100644 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java @@ -67,7 +67,7 @@ public Collection getRawQueryTexts(Object request) { // replace cached sanitization result to verify it is used sanitizedMap.put( testQuery, - SqlStatementInfo.create("SELECT name2 FROM test2 WHERE id = ?", "SELECT", "test2")); + SqlStatementInfo.create("SELECT name2 FROM test2 WHERE id = ?", "SELECT", "test2", null)); { AttributesBuilder builder = Attributes.builder(); attributesExtractor.onStart(builder, Context.root(), null); diff --git a/instrumentation/cassandra/cassandra-3.0/javaagent/src/test/java/CassandraClientTest.java b/instrumentation/cassandra/cassandra-3.0/javaagent/src/test/java/CassandraClientTest.java index df243eadb388..3d8a7e8ab7fc 100644 --- a/instrumentation/cassandra/cassandra-3.0/javaagent/src/test/java/CassandraClientTest.java +++ b/instrumentation/cassandra/cassandra-3.0/javaagent/src/test/java/CassandraClientTest.java @@ -3,10 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_ADDRESS; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_PORT; @@ -129,7 +131,10 @@ void syncTest(Parameter parameter) { equalTo(maybeStable(DB_NAME), parameter.keyspace), equalTo(maybeStable(DB_STATEMENT), parameter.expectedStatement), equalTo(maybeStable(DB_OPERATION), parameter.operation), - equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table)))); + equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? parameter.spanName : null)))); } else { testing.waitAndAssertTraces( trace -> @@ -147,7 +152,10 @@ void syncTest(Parameter parameter) { equalTo(maybeStable(DB_SYSTEM), "cassandra"), equalTo(maybeStable(DB_STATEMENT), parameter.expectedStatement), equalTo(maybeStable(DB_OPERATION), parameter.operation), - equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table)))); + equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? parameter.spanName : null)))); } session.close(); @@ -202,7 +210,10 @@ void asyncTest(Parameter parameter) { equalTo(maybeStable(DB_NAME), parameter.keyspace), equalTo(maybeStable(DB_STATEMENT), parameter.expectedStatement), equalTo(maybeStable(DB_OPERATION), parameter.operation), - equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table)), + equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? parameter.spanName : null)), span -> span.hasName("callbackListener") .hasKind(SpanKind.INTERNAL) @@ -225,7 +236,10 @@ void asyncTest(Parameter parameter) { equalTo(maybeStable(DB_SYSTEM), "cassandra"), equalTo(maybeStable(DB_STATEMENT), parameter.expectedStatement), equalTo(maybeStable(DB_OPERATION), parameter.operation), - equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table)), + equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? parameter.spanName : null)), span -> span.hasName("callbackListener") .hasKind(SpanKind.INTERNAL) @@ -303,7 +317,7 @@ private static Stream provideSyncParameters() { "sync_test", "SELECT * FROM users where name = 'alice' ALLOW FILTERING", "SELECT * FROM users where name = ? ALLOW FILTERING", - "SELECT sync_test.users", + emitStableDatabaseSemconv() ? "SELECT users" : "SELECT sync_test.users", "SELECT", "users")))); } @@ -357,7 +371,7 @@ private static Stream provideAsyncParameters() { "async_test", "SELECT * FROM users where name = 'alice' ALLOW FILTERING", "SELECT * FROM users where name = ? ALLOW FILTERING", - "SELECT async_test.users", + emitStableDatabaseSemconv() ? "SELECT users" : "SELECT async_test.users", "SELECT", "users")))); } diff --git a/instrumentation/cassandra/cassandra-4-common/testing/src/main/java/io/opentelemetry/cassandra/v4/common/AbstractCassandraTest.java b/instrumentation/cassandra/cassandra-4-common/testing/src/main/java/io/opentelemetry/cassandra/v4/common/AbstractCassandraTest.java index 74b6451eca98..13b006c5f30c 100644 --- a/instrumentation/cassandra/cassandra-4-common/testing/src/main/java/io/opentelemetry/cassandra/v4/common/AbstractCassandraTest.java +++ b/instrumentation/cassandra/cassandra-4-common/testing/src/main/java/io/opentelemetry/cassandra/v4/common/AbstractCassandraTest.java @@ -5,11 +5,13 @@ package io.opentelemetry.cassandra.v4.common; +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_ADDRESS; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_PORT; @@ -151,7 +153,10 @@ void syncTest(Parameter parameter) { val -> val.isInstanceOf(Boolean.class)), equalTo(maybeStable(DB_CASSANDRA_PAGE_SIZE), 5000), equalTo(maybeStable(DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT), 0), - equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table)))); + equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? parameter.spanName : null)))); session.close(); } @@ -205,7 +210,10 @@ void asyncTest(Parameter parameter) throws Exception { val -> val.isInstanceOf(Boolean.class)), equalTo(maybeStable(DB_CASSANDRA_PAGE_SIZE), 5000), equalTo(maybeStable(DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT), 0), - equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table)), + equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? parameter.spanName : null)), span -> span.hasName("child") .hasKind(SpanKind.INTERNAL) @@ -263,7 +271,7 @@ private static Stream provideSyncParameters() { "sync_test", "SELECT * FROM users where name = 'alice' ALLOW FILTERING", "SELECT * FROM users where name = ? ALLOW FILTERING", - "SELECT sync_test.users", + emitStableDatabaseSemconv() ? "SELECT users" : "SELECT sync_test.users", "SELECT", "users")))); } @@ -317,11 +325,18 @@ private static Stream provideAsyncParameters() { "async_test", "SELECT * FROM users where name = 'alice' ALLOW FILTERING", "SELECT * FROM users where name = ? ALLOW FILTERING", - "SELECT async_test.users", + emitStableDatabaseSemconv() ? "SELECT users" : "SELECT async_test.users", "SELECT", "users")))); } + protected static String querySummary(String operation, String table) { + if (table == null) { + return operation; + } + return operation + " " + table; + } + protected static class Parameter { public final String keyspace; public final String statement; diff --git a/instrumentation/cassandra/cassandra-4.4/testing/src/main/java/io/opentelemetry/testing/cassandra/v4_4/AbstractCassandra44Test.java b/instrumentation/cassandra/cassandra-4.4/testing/src/main/java/io/opentelemetry/testing/cassandra/v4_4/AbstractCassandra44Test.java index fb323ddf9e8e..d7508913c0e9 100644 --- a/instrumentation/cassandra/cassandra-4.4/testing/src/main/java/io/opentelemetry/testing/cassandra/v4_4/AbstractCassandra44Test.java +++ b/instrumentation/cassandra/cassandra-4.4/testing/src/main/java/io/opentelemetry/testing/cassandra/v4_4/AbstractCassandra44Test.java @@ -5,9 +5,11 @@ package io.opentelemetry.testing.cassandra.v4_4; +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_ADDRESS; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_PORT; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_TYPE; @@ -86,7 +88,10 @@ void reactiveTest(Parameter parameter) { val -> val.isInstanceOf(Boolean.class)), equalTo(maybeStable(DB_CASSANDRA_PAGE_SIZE), 5000), equalTo(maybeStable(DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT), 0), - equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table)), + equalTo(maybeStable(DB_CASSANDRA_TABLE), parameter.table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? parameter.spanName : null)), span -> span.hasName("child") .hasKind(SpanKind.INTERNAL) @@ -144,7 +149,7 @@ private static Stream provideReactiveParameters() { "reactive_test", "SELECT * FROM users where name = 'alice' ALLOW FILTERING", "SELECT * FROM users where name = ? ALLOW FILTERING", - "SELECT reactive_test.users", + emitStableDatabaseSemconv() ? "SELECT users" : "SELECT reactive_test.users", "SELECT", "users")))); } diff --git a/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizer.java b/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizer.java index 2fa42b1ca6bb..b4b22b78ce42 100644 --- a/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizer.java +++ b/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizer.java @@ -90,7 +90,7 @@ public static SqlStatementInfo sanitize(Object query) { String queryClassName = query.getClass().getName(); if (queryClassName.equals("com.couchbase.client.java.view.ViewQuery") || queryClassName.equals("com.couchbase.client.java.view.SpatialViewQuery")) { - return SqlStatementInfo.create(query.toString(), null, null); + return SqlStatementInfo.create(query.toString(), null, null, null); } // N1qlQuery is present starting from Couchbase 2.2.0 if (N1QL_QUERY_CLASS != null && N1QL_QUERY_CLASS.isAssignableFrom(query.getClass())) { @@ -106,7 +106,7 @@ public static SqlStatementInfo sanitize(Object query) { return sanitizeString(statement); } } - return SqlStatementInfo.create(query.getClass().getSimpleName(), null, null); + return SqlStatementInfo.create(query.getClass().getSimpleName(), null, null, null); } private static String getStatementString(MethodHandle handle, Object query) { diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java index 7ecbb4953faa..1f56a4095792 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java @@ -13,6 +13,7 @@ import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -84,7 +85,7 @@ void testCriteriaQuery(Consumer> interaction) { HIBERNATE_SESSION_ID, val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("SELECT db1.Value") + span.hasName(emitStableDatabaseSemconv() ? "SELECT Value" : "SELECT db1.Value") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -98,7 +99,10 @@ void testCriteriaQuery(Consumer> interaction) { maybeStable(DB_STATEMENT), stringAssert -> stringAssert.startsWith("select")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT Value" : null)), span -> span.hasName("Transaction.commit") .hasKind(SpanKind.INTERNAL) diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java index d983709f338d..0f73edb839f8 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java @@ -13,6 +13,7 @@ import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -89,7 +90,11 @@ void testHibernateAction(Parameter parameter) { span, trace.getSpan(0), "Session." + parameter.methodName + " " + parameter.resource), - span -> assertClientSpan(span, trace.getSpan(1), "SELECT db1.Value"), + span -> + assertClientSpan( + span, + trace.getSpan(1), + emitStableDatabaseSemconv() ? "SELECT Value" : "SELECT db1.Value"), span -> assertClientSpan( span, !parameter.flushOnCommit ? trace.getSpan(1) : trace.getSpan(3)), @@ -140,7 +145,7 @@ void testAttachesStateToQuery(Parameter parameter) { span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), span -> assertSessionSpan(span, trace.getSpan(0), parameter.resource), span -> - span.hasName("SELECT db1.Value") + span.hasName(emitStableDatabaseSemconv() ? "SELECT Value" : "SELECT db1.Value") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -153,7 +158,10 @@ void testAttachesStateToQuery(Parameter parameter) { satisfies( maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT Value" : null)), span -> assertTransactionCommitSpan( span, @@ -179,7 +187,7 @@ void testNoResultExceptionIgnored() { .hasStatus(StatusData.unset()) .hasEvents(emptyList()), span -> - span.hasName("SELECT db1.Value") + span.hasName(emitStableDatabaseSemconv() ? "SELECT Value" : "SELECT db1.Value") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)))); } @@ -325,11 +333,14 @@ private static void assertClientSpan(SpanDataAssert span, SpanData parent) { equalTo(DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "h2:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies(maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")); + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, val -> val.satisfiesAnyOf(v -> {}, v -> assertThat(v).isNull()))); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation private static void assertClientSpan(SpanDataAssert span, SpanData parent, String spanName) { + String querySummary = emitStableDatabaseSemconv() ? spanName.replace("db1.", "") : null; span.hasName(spanName) .hasKind(SpanKind.CLIENT) .hasParent(parent) @@ -340,7 +351,8 @@ private static void assertClientSpan(SpanDataAssert span, SpanData parent, Strin equalTo(DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "h2:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies(maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")); + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + equalTo(DB_QUERY_SUMMARY, querySummary)); } private static void assertSessionSpan(SpanDataAssert span, SpanData parent, String spanName) { diff --git a/instrumentation/hibernate/hibernate-6.0/spring-testing/src/test/java/spring/jpa/SpringJpaTest.java b/instrumentation/hibernate/hibernate-6.0/spring-testing/src/test/java/spring/jpa/SpringJpaTest.java index 702ce58149f7..fa1305a5e2b5 100644 --- a/instrumentation/hibernate/hibernate-6.0/spring-testing/src/test/java/spring/jpa/SpringJpaTest.java +++ b/instrumentation/hibernate/hibernate-6.0/spring-testing/src/test/java/spring/jpa/SpringJpaTest.java @@ -25,6 +25,7 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.semconv.DbAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -77,7 +78,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> val.isInstanceOf(String.class))), span -> - span.hasName("SELECT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT Customer" + : "SELECT test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -95,7 +99,10 @@ void testCrud() { val.matches( "select ([^.]+)\\.id([^,]*),([^.]+)\\.firstName([^,]*),([^.]+)\\.lastName(.*)from Customer(.*)")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")), + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + equalTo( + DbAttributes.DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT Customer" : null)), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -131,7 +138,7 @@ void testCrud() { stringKey("hibernate.session_id"), val -> val.isInstanceOf(String.class))), span -> - span.hasName("CALL test") + span.hasName(emitStableDatabaseSemconv() ? "CALL" : "CALL test") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -144,7 +151,10 @@ void testCrud() { equalTo( DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), - equalTo(maybeStable(DB_OPERATION), "CALL")), + equalTo(maybeStable(DB_OPERATION), "CALL"), + equalTo( + DbAttributes.DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "CALL" : null)), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -157,7 +167,10 @@ void testCrud() { .getAttributes() .get(stringKey("hibernate.session_id")))), span -> - span.hasName("INSERT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "INSERT Customer" + : "INSERT test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(3)) .hasAttributesSatisfyingExactly( @@ -174,7 +187,10 @@ void testCrud() { val -> val.matches("insert into Customer \\(.*\\) values \\(.*\\)")), equalTo(maybeStable(DB_OPERATION), "INSERT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")))); + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + equalTo( + DbAttributes.DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "INSERT Customer" : null)))); testing.clearData(); @@ -199,7 +215,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> val.isInstanceOf(String.class))), span -> - span.hasName("SELECT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT Customer" + : "SELECT test.Customer") .hasKind(CLIENT) .hasAttributesSatisfyingExactly( equalTo( @@ -216,7 +235,10 @@ void testCrud() { val.matches( "select ([^.]+)\\.id([^,]*),([^.]+)\\.firstName([^,]*),([^.]+)\\.lastName (.*)from Customer (.*)where ([^.]+)\\.id( ?)=( ?)\\?")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")), + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + equalTo( + DbAttributes.DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT Customer" : null)), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -229,7 +251,10 @@ void testCrud() { .getAttributes() .get(stringKey("hibernate.session_id")))), span -> - span.hasName("UPDATE test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "UPDATE Customer" + : "UPDATE test.Customer") .hasKind(CLIENT) .hasAttributesSatisfyingExactly( equalTo( @@ -246,7 +271,10 @@ void testCrud() { val.matches( "update Customer set firstName=\\?,(.*)lastName=\\? where id=\\?")), equalTo(maybeStable(DB_OPERATION), "UPDATE"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")))); + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + equalTo( + DbAttributes.DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "UPDATE Customer" : null)))); testing.clearData(); Customer anonymousCustomer = testing.runWithSpan("parent", () -> repo.findByLastName("Anonymous").get(0)); @@ -273,7 +301,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> val.isInstanceOf(String.class))), span -> - span.hasName("SELECT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT Customer" + : "SELECT test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -291,7 +322,10 @@ void testCrud() { val.matches( "select ([^.]+)\\.id([^,]*),([^.]+)\\.firstName([^,]*),([^.]+)\\.lastName (.*)from Customer (.*)(where ([^.]+)\\.lastName( ?)=( ?)\\?|)")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")))); + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + equalTo( + DbAttributes.DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT Customer" : null)))); testing.clearData(); testing.runWithSpan("parent", () -> repo.delete(anonymousCustomer)); @@ -316,7 +350,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> val.isInstanceOf(String.class))), span -> - span.hasName("SELECT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT Customer" + : "SELECT test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -334,7 +371,10 @@ void testCrud() { val.matches( "select ([^.]+)\\.id([^,]*),([^.]+)\\.firstName([^,]*),([^.]+)\\.lastName (.*)from Customer (.*)(where ([^.]+)\\.lastName( ?)=( ?)\\?|)")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")), + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + equalTo( + DbAttributes.DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT Customer" : null)), span -> span.hasName("Session.merge spring.jpa.Customer") .hasKind(INTERNAL) @@ -360,7 +400,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> val.isInstanceOf(String.class))), span -> - span.hasName("DELETE test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "DELETE Customer" + : "DELETE test.Customer") .hasKind(CLIENT) .hasAttributesSatisfyingExactly( equalTo( @@ -373,6 +416,9 @@ void testCrud() { emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), equalTo(maybeStable(DB_STATEMENT), "delete from Customer where id=?"), equalTo(maybeStable(DB_OPERATION), "DELETE"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")))); + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + equalTo( + DbAttributes.DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "DELETE Customer" : null)))); } } diff --git a/instrumentation/jdbc/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/scalaexecutors/SlickTest.scala b/instrumentation/jdbc/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/scalaexecutors/SlickTest.scala index 79c74f466a16..ed0614a59d97 100644 --- a/instrumentation/jdbc/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/scalaexecutors/SlickTest.scala +++ b/instrumentation/jdbc/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/scalaexecutors/SlickTest.scala @@ -18,6 +18,7 @@ import io.opentelemetry.instrumentation.testing.junit.{ import io.opentelemetry.javaagent.testing.common.Java8BytecodeBridge import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo import io.opentelemetry.sdk.testing.assertj.{SpanDataAssert, TraceAssert} +import io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY import io.opentelemetry.semconv.incubating.DbIncubatingAttributes._ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.extension.RegisterExtension @@ -80,7 +81,10 @@ class SlickTest { new Consumer[SpanDataAssert] { override def accept(span: SpanDataAssert): Unit = span - .hasName(s"SELECT ${Db}") + .hasName( + if (emitStableDatabaseSemconv()) "SELECT" + else s"SELECT ${Db}" + ) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -98,7 +102,11 @@ class SlickTest { if (emitStableDatabaseSemconv()) null else "h2:mem:" ), equalTo(maybeStable(DB_STATEMENT), "SELECT ?"), - equalTo(maybeStable(DB_OPERATION), "SELECT") + equalTo(maybeStable(DB_OPERATION), "SELECT"), + equalTo( + DB_QUERY_SUMMARY, + if (emitStableDatabaseSemconv()) "SELECT" else null + ) ) } ) diff --git a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java index 2fbc03d9ecd9..a1218bef4432 100644 --- a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java +++ b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java @@ -16,6 +16,7 @@ import static io.opentelemetry.semconv.DbAttributes.DB_NAMESPACE; import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_BATCH_SIZE; import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; @@ -228,7 +229,7 @@ static Stream basicStatementStream() throws SQLException { null, "SELECT 3", "SELECT ?", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -255,7 +256,7 @@ static Stream basicStatementStream() throws SQLException { null, "SELECT 3", "SELECT ?", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -282,7 +283,7 @@ static Stream basicStatementStream() throws SQLException { null, "SELECT 3", "SELECT ?", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -309,7 +310,7 @@ static Stream basicStatementStream() throws SQLException { null, "SELECT 3", "SELECT ?", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -336,7 +337,7 @@ static Stream basicStatementStream() throws SQLException { null, "SELECT 3", "SELECT ?", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -396,7 +397,10 @@ public void testBasicStatement( DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), equalTo(maybeStable(DB_STATEMENT), sanitizedQuery), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), table)))); + equalTo(maybeStable(DB_SQL_TABLE), table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? spanName : null)))); if (table != null) { assertDurationMetric( @@ -420,7 +424,7 @@ static Stream preparedStatementStream() throws SQLException { null, "SELECT 3", "SELECT ?", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -438,7 +442,7 @@ static Stream preparedStatementStream() throws SQLException { null, "SELECT 3", "SELECT ?", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -456,7 +460,7 @@ static Stream preparedStatementStream() throws SQLException { null, "SELECT 3", "SELECT ?", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -474,7 +478,7 @@ static Stream preparedStatementStream() throws SQLException { null, "SELECT 3", "SELECT ?", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -532,7 +536,10 @@ void testPreparedStatementExecute( DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), equalTo(maybeStable(DB_STATEMENT), sanitizedQuery), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), table)))); + equalTo(maybeStable(DB_SQL_TABLE), table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? spanName : null)))); } @ParameterizedTest @@ -572,7 +579,10 @@ void testPreparedStatementQuery( DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), equalTo(maybeStable(DB_STATEMENT), sanitizedQuery), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), table)))); + equalTo(maybeStable(DB_SQL_TABLE), table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? spanName : null)))); } @ParameterizedTest @@ -612,7 +622,10 @@ void testPreparedCall( DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), equalTo(maybeStable(DB_STATEMENT), sanitizedQuery), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), table)))); + equalTo(maybeStable(DB_SQL_TABLE), table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? spanName : null)))); } static Stream statementUpdateStream() throws SQLException { @@ -740,7 +753,8 @@ void testStatementUpdate( trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), span -> - span.hasName(spanName) + span.hasName( + emitStableDatabaseSemconv() ? "CREATE TABLE " + table : spanName) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -751,7 +765,12 @@ void testStatementUpdate( DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), equalTo(maybeStable(DB_STATEMENT), query), equalTo(maybeStable(DB_OPERATION), "CREATE TABLE"), - equalTo(maybeStable(DB_SQL_TABLE), table)))); + equalTo(maybeStable(DB_SQL_TABLE), table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() + ? "CREATE TABLE " + table + : null)))); } static Stream preparedStatementUpdateStream() throws SQLException { @@ -761,7 +780,7 @@ static Stream preparedStatementUpdateStream() throws SQLException { new org.h2.Driver().connect(jdbcUrls.get("h2"), null), null, "CREATE TABLE PS_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_H2", + emitStableDatabaseSemconv() ? "CREATE TABLE PS_H2" : "CREATE TABLE jdbcunittest.PS_H2", "h2:mem:", "PS_H2"), Arguments.of( @@ -769,7 +788,9 @@ static Stream preparedStatementUpdateStream() throws SQLException { new EmbeddedDriver().connect(jdbcUrls.get("derby"), null), "APP", "CREATE TABLE PS_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_DERBY", + emitStableDatabaseSemconv() + ? "CREATE TABLE PS_DERBY" + : "CREATE TABLE jdbcunittest.PS_DERBY", "derby:memory:", "PS_DERBY"), Arguments.of( @@ -777,7 +798,9 @@ static Stream preparedStatementUpdateStream() throws SQLException { cpDatasources.get("tomcat").get("h2").getConnection(), null, "CREATE TABLE PS_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_H2_TOMCAT", + emitStableDatabaseSemconv() + ? "CREATE TABLE PS_H2_TOMCAT" + : "CREATE TABLE jdbcunittest.PS_H2_TOMCAT", "h2:mem:", "PS_H2_TOMCAT"), Arguments.of( @@ -785,7 +808,9 @@ static Stream preparedStatementUpdateStream() throws SQLException { cpDatasources.get("tomcat").get("derby").getConnection(), "APP", "CREATE TABLE PS_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_DERBY_TOMCAT", + emitStableDatabaseSemconv() + ? "CREATE TABLE PS_DERBY_TOMCAT" + : "CREATE TABLE jdbcunittest.PS_DERBY_TOMCAT", "derby:memory:", "PS_DERBY_TOMCAT"), Arguments.of( @@ -793,7 +818,9 @@ static Stream preparedStatementUpdateStream() throws SQLException { cpDatasources.get("hikari").get("h2").getConnection(), null, "CREATE TABLE PS_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_H2_HIKARI", + emitStableDatabaseSemconv() + ? "CREATE TABLE PS_H2_HIKARI" + : "CREATE TABLE jdbcunittest.PS_H2_HIKARI", "h2:mem:", "PS_H2_HIKARI"), Arguments.of( @@ -801,7 +828,9 @@ static Stream preparedStatementUpdateStream() throws SQLException { cpDatasources.get("hikari").get("derby").getConnection(), "APP", "CREATE TABLE PS_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_DERBY_HIKARI", + emitStableDatabaseSemconv() + ? "CREATE TABLE PS_DERBY_HIKARI" + : "CREATE TABLE jdbcunittest.PS_DERBY_HIKARI", "derby:memory:", "PS_DERBY_HIKARI"), Arguments.of( @@ -809,7 +838,9 @@ static Stream preparedStatementUpdateStream() throws SQLException { cpDatasources.get("c3p0").get("h2").getConnection(), null, "CREATE TABLE PS_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_H2_C3P0", + emitStableDatabaseSemconv() + ? "CREATE TABLE PS_H2_C3P0" + : "CREATE TABLE jdbcunittest.PS_H2_C3P0", "h2:mem:", "PS_H2_C3P0"), Arguments.of( @@ -817,7 +848,9 @@ static Stream preparedStatementUpdateStream() throws SQLException { cpDatasources.get("c3p0").get("derby").getConnection(), "APP", "CREATE TABLE PS_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_DERBY_C3P0", + emitStableDatabaseSemconv() + ? "CREATE TABLE PS_DERBY_C3P0" + : "CREATE TABLE jdbcunittest.PS_DERBY_C3P0", "derby:memory:", "PS_DERBY_C3P0")); } @@ -852,7 +885,9 @@ static Stream preparedStatementLargeUpdateStream() throws SQLExceptio new org.h2.Driver().connect(jdbcUrls.get("h2"), null), null, "CREATE TABLE PS_LARGE_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_LARGE_H2", + emitStableDatabaseSemconv() + ? "CREATE TABLE PS_LARGE_H2" + : "CREATE TABLE jdbcunittest.PS_LARGE_H2", "h2:mem:", "PS_LARGE_H2"), Arguments.of( @@ -860,7 +895,9 @@ static Stream preparedStatementLargeUpdateStream() throws SQLExceptio cpDatasources.get("tomcat").get("h2").getConnection(), null, "CREATE TABLE PS_LARGE_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_LARGE_H2_TOMCAT", + emitStableDatabaseSemconv() + ? "CREATE TABLE PS_LARGE_H2_TOMCAT" + : "CREATE TABLE jdbcunittest.PS_LARGE_H2_TOMCAT", "h2:mem:", "PS_LARGE_H2_TOMCAT"), Arguments.of( @@ -868,7 +905,9 @@ static Stream preparedStatementLargeUpdateStream() throws SQLExceptio cpDatasources.get("hikari").get("h2").getConnection(), null, "CREATE TABLE PS_LARGE_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_LARGE_H2_HIKARI", + emitStableDatabaseSemconv() + ? "CREATE TABLE PS_LARGE_H2_HIKARI" + : "CREATE TABLE jdbcunittest.PS_LARGE_H2_HIKARI", "h2:mem:", "PS_LARGE_H2_HIKARI"), Arguments.of( @@ -876,7 +915,9 @@ static Stream preparedStatementLargeUpdateStream() throws SQLExceptio new EmbeddedDriver().connect(jdbcUrls.get("derby"), null), "APP", "CREATE TABLE PS_LARGE_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.PS_LARGE_DERBY", + emitStableDatabaseSemconv() + ? "CREATE TABLE PS_LARGE_DERBY" + : "CREATE TABLE jdbcunittest.PS_LARGE_DERBY", "derby:memory:", "PS_LARGE_DERBY"), Arguments.of( @@ -955,7 +996,10 @@ void testPreparedStatementUpdateImpl( DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), equalTo(maybeStable(DB_STATEMENT), query), equalTo(maybeStable(DB_OPERATION), "CREATE TABLE"), - equalTo(maybeStable(DB_SQL_TABLE), table)))); + equalTo(maybeStable(DB_SQL_TABLE), table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? spanName : null)))); } static Stream connectionConstructorStream() { @@ -968,7 +1012,7 @@ static Stream connectionConstructorStream() { null, "SELECT 3;", "SELECT ?;", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -990,7 +1034,7 @@ static Stream connectionConstructorStream() { null, "SELECT 3;", "SELECT ?;", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -1066,7 +1110,10 @@ void testConnectionConstructorThrowing( DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), equalTo(maybeStable(DB_STATEMENT), sanitizedQuery), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), table)))); + equalTo(maybeStable(DB_SQL_TABLE), table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? spanName : null)))); } static Stream getConnectionStream() { @@ -1182,7 +1229,7 @@ static Stream spanNameStream() { "jdbc:testdb://localhost?databaseName=test", "SELECT * FROM table", "SELECT * FROM table", - "SELECT test.table", + emitStableDatabaseSemconv() ? "SELECT table" : "SELECT test.table", "test", "SELECT", "table"), @@ -1190,7 +1237,7 @@ static Stream spanNameStream() { "jdbc:testdb://localhost?databaseName=test", "SELECT 42", "SELECT ?", - "SELECT test", + emitStableDatabaseSemconv() ? "SELECT" : "SELECT test", "test", "SELECT", null), @@ -1206,7 +1253,7 @@ static Stream spanNameStream() { "jdbc:testdb://localhost?databaseName=test", "CREATE TABLE table", "CREATE TABLE table", - "CREATE TABLE test.table", + emitStableDatabaseSemconv() ? "CREATE TABLE table" : "CREATE TABLE test.table", "test", "CREATE TABLE", "table"), @@ -1261,6 +1308,9 @@ void testProduceProperSpanName( equalTo(maybeStable(DB_STATEMENT), sanitizedQuery), equalTo(maybeStable(DB_OPERATION), operation), equalTo(maybeStable(DB_SQL_TABLE), table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? spanName : null), equalTo( PEER_SERVICE, hasPeerService() ? "test-peer-service" : null), equalTo(SERVER_ADDRESS, "localhost")))); @@ -1315,7 +1365,12 @@ void testConnectionCached(String connectionPoolName) throws SQLException { maybeStable(DB_STATEMENT), "SELECT ? FROM INFORMATION_SCHEMA.SYSTEM_USERS"), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "INFORMATION_SCHEMA.SYSTEM_USERS"))); + equalTo(maybeStable(DB_SQL_TABLE), "INFORMATION_SCHEMA.SYSTEM_USERS"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() + ? "SELECT INFORMATION_SCHEMA.SYSTEM_USERS" + : null))); for (int i = 0; i < numQueries; i++) { assertions.add(traceAssertConsumer); } @@ -1390,7 +1445,10 @@ void testHandleRecursiveStatements( equalTo(maybeStable(DB_SQL_TABLE), "table"), equalTo( PEER_SERVICE, hasPeerService() ? "test-peer-service" : null), - equalTo(SERVER_ADDRESS, "localhost")))); + equalTo(SERVER_ADDRESS, "localhost"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT table" : null)))); } // regression test for @@ -1415,7 +1473,8 @@ void testProxyStatement() throws Exception { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), span -> - span.hasName("SELECT " + dbNameLower) + span.hasName( + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)))); } @@ -1441,7 +1500,8 @@ void testProxyPreparedStatement() throws SQLException { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), span -> - span.hasName("SELECT " + dbNameLower) + span.hasName( + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)))); } @@ -1524,7 +1584,7 @@ private void testBatchImpl( span -> span.hasName( emitStableDatabaseSemconv() - ? "BATCH INSERT jdbcunittest." + tableName + ? "BATCH INSERT " + tableName : "jdbcunittest") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) @@ -1547,7 +1607,10 @@ DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), emitStableDatabaseSemconv() ? tableName : null), equalTo( DB_OPERATION_BATCH_SIZE, - emitStableDatabaseSemconv() ? 2L : null)))); + emitStableDatabaseSemconv() ? 2L : null), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "INSERT " + tableName : null)))); } @ParameterizedTest @@ -1635,7 +1698,10 @@ void testSingleItemBatch(String system, Connection conn, String username, String trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), span -> - span.hasName("INSERT jdbcunittest." + tableName) + span.hasName( + emitStableDatabaseSemconv() + ? "INSERT " + tableName + : "INSERT jdbcunittest." + tableName) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -1648,7 +1714,10 @@ DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), maybeStable(DB_STATEMENT), "INSERT INTO " + tableName + " VALUES(?)"), equalTo(maybeStable(DB_OPERATION), "INSERT"), - equalTo(maybeStable(DB_SQL_TABLE), tableName)))); + equalTo(maybeStable(DB_SQL_TABLE), tableName), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "INSERT " + tableName : null)))); } @ParameterizedTest @@ -1686,7 +1755,7 @@ void testPreparedBatch(String system, Connection conn, String username, String u span -> span.hasName( emitStableDatabaseSemconv() - ? "BATCH INSERT jdbcunittest." + tableName + ? "BATCH INSERT " + tableName : "INSERT jdbcunittest." + tableName) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) @@ -1705,7 +1774,10 @@ DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), equalTo(maybeStable(DB_SQL_TABLE), tableName), equalTo( DB_OPERATION_BATCH_SIZE, - emitStableDatabaseSemconv() ? 2L : null)))); + emitStableDatabaseSemconv() ? 2L : null), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "INSERT " + tableName : null)))); } // test that sqlcommenter is not enabled by default @@ -1758,7 +1830,10 @@ void testCommitTransaction(String system, Connection conn, String username, Stri trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), span -> - span.hasName("INSERT jdbcunittest." + tableName) + span.hasName( + emitStableDatabaseSemconv() + ? "INSERT " + tableName + : "INSERT jdbcunittest." + tableName) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -1771,7 +1846,10 @@ DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), maybeStable(DB_STATEMENT), "INSERT INTO " + tableName + " VALUES(?)"), equalTo(maybeStable(DB_OPERATION), "INSERT"), - equalTo(maybeStable(DB_SQL_TABLE), tableName)), + equalTo(maybeStable(DB_SQL_TABLE), tableName), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "INSERT " + tableName : null)), span -> span.hasName("COMMIT") .hasKind(SpanKind.CLIENT) @@ -1819,7 +1897,10 @@ void testRollbackTransaction(String system, Connection conn, String username, St trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), span -> - span.hasName("INSERT jdbcunittest." + tableName) + span.hasName( + emitStableDatabaseSemconv() + ? "INSERT " + tableName + : "INSERT jdbcunittest." + tableName) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -1832,7 +1913,10 @@ DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), maybeStable(DB_STATEMENT), "INSERT INTO " + tableName + " VALUES(?)"), equalTo(maybeStable(DB_OPERATION), "INSERT"), - equalTo(maybeStable(DB_SQL_TABLE), tableName)), + equalTo(maybeStable(DB_SQL_TABLE), tableName), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "INSERT " + tableName : null)), span -> span.hasName("ROLLBACK") .hasKind(SpanKind.CLIENT) @@ -1911,7 +1995,8 @@ void testPreparedStatementWrapper() throws SQLException { .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(0)), span -> - span.hasName("SELECT " + dbNameLower) + span.hasName( + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(1)))); } @@ -1956,7 +2041,8 @@ void testStatementWrapper() throws SQLException { .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(0)), span -> - span.hasName("SELECT " + dbNameLower) + span.hasName( + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(1)))); } diff --git a/instrumentation/r2dbc-1.0/testing/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/AbstractR2dbcStatementTest.java b/instrumentation/r2dbc-1.0/testing/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/AbstractR2dbcStatementTest.java index 8c5751af1aeb..d3498e62604b 100644 --- a/instrumentation/r2dbc-1.0/testing/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/AbstractR2dbcStatementTest.java +++ b/instrumentation/r2dbc-1.0/testing/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/AbstractR2dbcStatementTest.java @@ -8,6 +8,7 @@ import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; @@ -161,13 +162,18 @@ void testQueries(Parameter parameter) { .blockLast(Duration.ofMinutes(1)); }); + String spanName = + emitStableDatabaseSemconv() + ? parameter.operation + (parameter.table != null ? " " + parameter.table : "") + : parameter.spanName; + getTesting() .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName(parameter.spanName) + span.hasName(spanName) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -182,6 +188,9 @@ void testQueries(Parameter parameter) { equalTo(maybeStable(DB_STATEMENT), parameter.expectedStatement), equalTo(maybeStable(DB_OPERATION), parameter.operation), equalTo(maybeStable(DB_SQL_TABLE), parameter.table), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? spanName : null), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, container.getHost()), equalTo(SERVER_PORT, port)), diff --git a/instrumentation/spring/spring-data/spring-data-common/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/AbstractSpringJpaTest.java b/instrumentation/spring/spring-data/spring-data-common/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/AbstractSpringJpaTest.java index 085d243ba825..48b3a695beed 100644 --- a/instrumentation/spring/spring-data/spring-data-common/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/AbstractSpringJpaTest.java +++ b/instrumentation/spring/spring-data/spring-data-common/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/AbstractSpringJpaTest.java @@ -10,6 +10,7 @@ import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -84,7 +85,8 @@ static void assertHibernate4Trace(TraceAssert trace, String repoClassName) { .hasKind(SpanKind.INTERNAL) .hasAttributesSatisfyingExactly(codeFunctionAssertions(repoClassName, "save")), span -> - span.hasName("INSERT test.JpaCustomer") + span.hasName( + emitStableDatabaseSemconv() ? "INSERT JpaCustomer" : "INSERT test.JpaCustomer") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -95,7 +97,10 @@ static void assertHibernate4Trace(TraceAssert trace, String repoClassName) { DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("insert ")), equalTo(maybeStable(DB_OPERATION), "INSERT"), - equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"))); + equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "INSERT JpaCustomer" : null))); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation @@ -106,7 +111,7 @@ static void assertHibernateTrace(TraceAssert trace, String repoClassName) { .hasKind(SpanKind.INTERNAL) .hasAttributesSatisfyingExactly(codeFunctionAssertions(repoClassName, "save")), span -> - span.hasName("CALL test") + span.hasName(emitStableDatabaseSemconv() ? "CALL" : "CALL test") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfyingExactly( equalTo(maybeStable(DB_SYSTEM), "hsqldb"), @@ -116,9 +121,11 @@ static void assertHibernateTrace(TraceAssert trace, String repoClassName) { DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), satisfies( maybeStable(DB_STATEMENT), val -> val.startsWith("call next value for ")), - equalTo(maybeStable(DB_OPERATION), "CALL")), + equalTo(maybeStable(DB_OPERATION), "CALL"), + equalTo(DB_QUERY_SUMMARY, emitStableDatabaseSemconv() ? "CALL" : null)), span -> - span.hasName("INSERT test.JpaCustomer") + span.hasName( + emitStableDatabaseSemconv() ? "INSERT JpaCustomer" : "INSERT test.JpaCustomer") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -129,7 +136,10 @@ DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("insert ")), equalTo(maybeStable(DB_OPERATION), "INSERT"), - equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"))); + equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "INSERT JpaCustomer" : null))); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation @@ -153,7 +163,10 @@ void testCrud() { .hasAttributesSatisfyingExactly( codeFunctionAssertions(repoClassName, "findAll")), span -> - span.hasName("SELECT test.JpaCustomer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT JpaCustomer" + : "SELECT test.JpaCustomer") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -165,7 +178,10 @@ void testCrud() { emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("select ")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer")))); + equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT JpaCustomer" : null)))); clearData(); repo.save(customer); @@ -190,7 +206,10 @@ void testCrud() { .hasAttributesSatisfyingExactly( codeFunctionAssertions(repoClassName, "save")), span -> - span.hasName("SELECT test.JpaCustomer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT JpaCustomer" + : "SELECT test.JpaCustomer") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -202,9 +221,15 @@ void testCrud() { emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("select ")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer")), + equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT JpaCustomer" : null)), span -> - span.hasName("UPDATE test.JpaCustomer") + span.hasName( + emitStableDatabaseSemconv() + ? "UPDATE JpaCustomer" + : "UPDATE test.JpaCustomer") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -216,7 +241,10 @@ void testCrud() { emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("update ")), equalTo(maybeStable(DB_OPERATION), "UPDATE"), - equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer")))); + equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "UPDATE JpaCustomer" : null)))); clearData(); customer = findByLastName(repo, "Anonymous").get(0); @@ -229,7 +257,10 @@ void testCrud() { .hasAttributesSatisfyingExactly( codeFunctionAssertions(repoClassName, "findByLastName")), span -> - span.hasName("SELECT test.JpaCustomer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT JpaCustomer" + : "SELECT test.JpaCustomer") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -241,7 +272,10 @@ void testCrud() { emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("select ")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer")))); + equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT JpaCustomer" : null)))); clearData(); repo.delete(customer); @@ -254,7 +288,10 @@ void testCrud() { .hasAttributesSatisfyingExactly( codeFunctionAssertions(repoClassName, "delete")), span -> - span.hasName("SELECT test.JpaCustomer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT JpaCustomer" + : "SELECT test.JpaCustomer") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -266,9 +303,15 @@ void testCrud() { emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("select ")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer")), + equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT JpaCustomer" : null)), span -> - span.hasName("DELETE test.JpaCustomer") + span.hasName( + emitStableDatabaseSemconv() + ? "DELETE JpaCustomer" + : "DELETE test.JpaCustomer") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -280,7 +323,10 @@ void testCrud() { emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("delete ")), equalTo(maybeStable(DB_OPERATION), "DELETE"), - equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer")))); + equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "DELETE JpaCustomer" : null)))); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation @@ -301,7 +347,10 @@ void testCustomRepositoryMethod() { .hasAttributesSatisfyingExactly( codeFunctionAssertions(repoClassName, "findSpecialCustomers")), span -> - span.hasName("SELECT test.JpaCustomer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT JpaCustomer" + : "SELECT test.JpaCustomer") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -313,7 +362,10 @@ void testCustomRepositoryMethod() { emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("select ")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer")))); + equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT JpaCustomer" : null)))); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation @@ -347,7 +399,10 @@ void testFailedRepositoryMethod() { .hasAttributesSatisfyingExactly( codeFunctionAssertions(repoClassName, "findOneByLastName")), span -> - span.hasName("SELECT test.JpaCustomer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT JpaCustomer" + : "SELECT test.JpaCustomer") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -359,6 +414,9 @@ void testFailedRepositoryMethod() { emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("select ")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer")))); + equalTo(maybeStable(DB_SQL_TABLE), "JpaCustomer"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT JpaCustomer" : null)))); } } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientTest.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientTest.java index 9d2530c98321..39a40e479200 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientTest.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientTest.java @@ -9,6 +9,7 @@ import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; @@ -141,7 +142,7 @@ void testSimpleSelect() throws Exception { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName("SELECT tempdb.test") + span.hasName(emitStableDatabaseSemconv() ? "SELECT test" : "SELECT tempdb.test") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -152,7 +153,10 @@ void testSimpleSelect() throws Exception { equalTo(maybeStable(DB_SQL_TABLE), "test"), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, host), - equalTo(SERVER_PORT, port)), + equalTo(SERVER_PORT, port), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT test" : null)), span -> span.hasName("callback") .hasKind(SpanKind.INTERNAL) @@ -248,7 +252,7 @@ private static void assertPreparedSelect() { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName("SELECT tempdb.test") + span.hasName(emitStableDatabaseSemconv() ? "SELECT test" : "SELECT tempdb.test") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -259,7 +263,10 @@ private static void assertPreparedSelect() { equalTo(maybeStable(DB_SQL_TABLE), "test"), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, host), - equalTo(SERVER_PORT, port)))); + equalTo(SERVER_PORT, port), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT test" : null)))); } @Test @@ -279,7 +286,7 @@ void testBatch() throws Exception { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName("INSERT tempdb.test") + span.hasName(emitStableDatabaseSemconv() ? "INSERT test" : "INSERT tempdb.test") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -292,7 +299,10 @@ void testBatch() throws Exception { equalTo(maybeStable(DB_SQL_TABLE), "test"), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, host), - equalTo(SERVER_PORT, port)))); + equalTo(SERVER_PORT, port), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "INSERT test" : null)))); } @Test @@ -368,7 +378,8 @@ void testManyQueries() throws Exception { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName("SELECT tempdb.test") + span.hasName( + emitStableDatabaseSemconv() ? "SELECT test" : "SELECT tempdb.test") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -379,7 +390,10 @@ void testManyQueries() throws Exception { equalTo(maybeStable(DB_SQL_TABLE), "test"), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, host), - equalTo(SERVER_PORT, port)), + equalTo(SERVER_PORT, port), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT test" : null)), span -> span.hasName("callback") .hasKind(SpanKind.INTERNAL) @@ -434,7 +448,8 @@ void testConcurrency() throws Exception { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName("SELECT tempdb.test") + span.hasName( + emitStableDatabaseSemconv() ? "SELECT test" : "SELECT tempdb.test") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -444,6 +459,9 @@ void testConcurrency() throws Exception { maybeStable(DB_STATEMENT), "select * from test where id = $1"), equalTo(maybeStable(DB_OPERATION), "SELECT"), equalTo(maybeStable(DB_SQL_TABLE), "test"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT test" : null), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, host), equalTo(SERVER_PORT, port)), diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/VertxSqlClientTest.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/VertxSqlClientTest.java index dc72433c5649..e4786152847a 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/VertxSqlClientTest.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/VertxSqlClientTest.java @@ -9,6 +9,7 @@ import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; @@ -142,7 +143,7 @@ void testSimpleSelect() throws Exception { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName("SELECT tempdb.test") + span.hasName(emitStableDatabaseSemconv() ? "SELECT test" : "SELECT tempdb.test") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -151,6 +152,9 @@ void testSimpleSelect() throws Exception { equalTo(maybeStable(DB_STATEMENT), "select * from test"), equalTo(maybeStable(DB_OPERATION), "SELECT"), equalTo(maybeStable(DB_SQL_TABLE), "test"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT test" : null), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, host), equalTo(SERVER_PORT, port)), @@ -249,7 +253,7 @@ private static void assertPreparedSelect() { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName("SELECT tempdb.test") + span.hasName(emitStableDatabaseSemconv() ? "SELECT test" : "SELECT tempdb.test") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -260,7 +264,10 @@ private static void assertPreparedSelect() { equalTo(maybeStable(DB_SQL_TABLE), "test"), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, host), - equalTo(SERVER_PORT, port)))); + equalTo(SERVER_PORT, port), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT test" : null)))); } @Test @@ -280,7 +287,7 @@ void testBatch() throws Exception { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName("INSERT tempdb.test") + span.hasName(emitStableDatabaseSemconv() ? "INSERT test" : "INSERT tempdb.test") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -293,7 +300,10 @@ void testBatch() throws Exception { equalTo(maybeStable(DB_SQL_TABLE), "test"), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, host), - equalTo(SERVER_PORT, port)))); + equalTo(SERVER_PORT, port), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "INSERT test" : null)))); } @Test @@ -370,7 +380,8 @@ void testManyQueries() throws Exception { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName("SELECT tempdb.test") + span.hasName( + emitStableDatabaseSemconv() ? "SELECT test" : "SELECT tempdb.test") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -381,7 +392,10 @@ void testManyQueries() throws Exception { equalTo(maybeStable(DB_SQL_TABLE), "test"), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, host), - equalTo(SERVER_PORT, port)), + equalTo(SERVER_PORT, port), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT test" : null)), span -> span.hasName("callback") .hasKind(SpanKind.INTERNAL) @@ -436,7 +450,8 @@ void testConcurrency() throws Exception { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName("SELECT tempdb.test") + span.hasName( + emitStableDatabaseSemconv() ? "SELECT test" : "SELECT tempdb.test") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -446,6 +461,9 @@ void testConcurrency() throws Exception { maybeStable(DB_STATEMENT), "select * from test where id = $1"), equalTo(maybeStable(DB_OPERATION), "SELECT"), equalTo(maybeStable(DB_SQL_TABLE), "test"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT test" : null), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, host), equalTo(SERVER_PORT, port)), From ca49b9fdff6d8a52f84c2cc60e29bdf604e82d5e Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 5 Dec 2025 16:13:59 -0800 Subject: [PATCH 02/26] fix --- .../jdbc/datasource/JdbcTelemetryTest.java | 38 +++++++++++++++---- ...stractPreparedStatementParametersTest.java | 9 ++++- .../testing/AbstractSqlCommenterTest.java | 30 ++++++++++++--- 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java b/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java index 48aa7eb62fce..edb42efa7710 100644 --- a/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java +++ b/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java @@ -11,6 +11,7 @@ import static io.opentelemetry.semconv.DbAttributes.DB_NAMESPACE; import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_BATCH_SIZE; import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_TEXT; import static io.opentelemetry.semconv.DbAttributes.DB_RESPONSE_STATUS_CODE; import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; @@ -56,13 +57,18 @@ void buildWithDefaults() throws SQLException { testing.runWithSpan( "parent", () -> dataSource.getConnection().createStatement().execute("SELECT 1;")); + String spanName = SemconvStability.emitStableDatabaseSemconv() ? "SELECT" : "SELECT dbname"; testing.waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent"), span -> - span.hasName("SELECT dbname") - .hasAttribute(equalTo(maybeStable(DB_STATEMENT), "SELECT ?;")))); + span.hasName(spanName) + .hasAttribute(equalTo(maybeStable(DB_STATEMENT), "SELECT ?;")) + .hasAttribute( + equalTo( + DB_QUERY_SUMMARY, + SemconvStability.emitStableDatabaseSemconv() ? spanName : null)))); assertDurationMetric( testing, @@ -99,12 +105,13 @@ void error() throws SQLException { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent"), span -> - span.hasName("SELECT dbname") + span.hasName("SELECT") .hasAttributesSatisfyingExactly( equalTo(DB_SYSTEM_NAME, "postgresql"), equalTo(DB_OPERATION_NAME, "SELECT"), equalTo(DB_NAMESPACE, "dbname"), equalTo(DB_QUERY_TEXT, "SELECT ?;"), + equalTo(DB_QUERY_SUMMARY, "SELECT"), equalTo(DB_RESPONSE_STATUS_CODE, "42"), equalTo(SERVER_ADDRESS, "127.0.0.1"), equalTo(SERVER_PORT, 5432), @@ -155,7 +162,12 @@ void buildWithDataSourceInstrumenterDisabled() throws SQLException { testing.waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> span.hasName("parent"), span -> span.hasName("SELECT dbname"))); + span -> span.hasName("parent"), + span -> + span.hasName( + SemconvStability.emitStableDatabaseSemconv() + ? "SELECT" + : "SELECT dbname"))); } @Test @@ -215,13 +227,18 @@ void buildWithSanitizationDisabled() throws SQLException { testing.runWithSpan( "parent", () -> dataSource.getConnection().createStatement().execute("SELECT 1;")); + String spanName = SemconvStability.emitStableDatabaseSemconv() ? "SELECT" : "SELECT dbname"; testing.waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent"), span -> - span.hasName("SELECT dbname") - .hasAttribute(equalTo(maybeStable(DB_STATEMENT), "SELECT 1;")))); + span.hasName(spanName) + .hasAttribute(equalTo(maybeStable(DB_STATEMENT), "SELECT 1;")) + .hasAttribute( + equalTo( + DB_QUERY_SUMMARY, + SemconvStability.emitStableDatabaseSemconv() ? spanName : null)))); } @Test @@ -260,7 +277,7 @@ void batchStatement() throws SQLException { span -> span.hasName( SemconvStability.emitStableDatabaseSemconv() - ? "BATCH INSERT dbname.test" + ? "BATCH INSERT test" : "dbname") .hasAttributesSatisfying( equalTo(maybeStable(DB_NAME), "dbname"), @@ -279,6 +296,11 @@ void batchStatement() throws SQLException { : null), equalTo( DB_OPERATION_BATCH_SIZE, - SemconvStability.emitStableDatabaseSemconv() ? 2L : null)))); + SemconvStability.emitStableDatabaseSemconv() ? 2L : null), + equalTo( + DB_QUERY_SUMMARY, + SemconvStability.emitStableDatabaseSemconv() + ? "INSERT test" + : null)))); } } diff --git a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractPreparedStatementParametersTest.java b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractPreparedStatementParametersTest.java index 9e889c5d0afa..1ff7f3fb14dd 100644 --- a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractPreparedStatementParametersTest.java +++ b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractPreparedStatementParametersTest.java @@ -19,6 +19,8 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; + import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import io.opentelemetry.api.trace.SpanKind; @@ -86,7 +88,7 @@ static Stream preparedStatementStream() throws SQLException { null, "SELECT 3, ?", "SELECT 3, ?", - "SELECT " + dbNameLower, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + dbNameLower, "h2:mem:", null), Arguments.of( @@ -632,7 +634,10 @@ DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), equalTo(maybeStable(DB_SQL_TABLE), table), equalTo( DB_QUERY_PARAMETER.getAttributeKey("0"), - expectedParameterValue)))); + expectedParameterValue), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? spanName : null)))); } public interface ThrowingConsumer { diff --git a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractSqlCommenterTest.java b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractSqlCommenterTest.java index fcb0bcf5d535..8558621abf98 100644 --- a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractSqlCommenterTest.java +++ b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractSqlCommenterTest.java @@ -48,7 +48,12 @@ void testSqlCommenterStatement() throws SQLException { trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasNoParent(), - span -> span.hasName("SELECT dbname").hasParent(trace.getSpan(0)))); + span -> + span.hasName( + SemconvStability.emitStableDatabaseSemconv() + ? "SELECT" + : "SELECT dbname") + .hasParent(trace.getSpan(0)))); assertThat(executedSql).hasSize(1); assertThat(executedSql.get(0)).contains(query).contains("traceparent"); @@ -81,7 +86,12 @@ void testSqlCommenterStatementUpdate(boolean largeUpdate) throws SQLException { trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasNoParent(), - span -> span.hasName("INSERT dbname.test").hasParent(trace.getSpan(0)))); + span -> + span.hasName( + SemconvStability.emitStableDatabaseSemconv() + ? "INSERT test" + : "INSERT dbname.test") + .hasParent(trace.getSpan(0)))); assertThat(executedSql).hasSize(1); assertThat(executedSql.get(0)).contains(query).contains("traceparent"); @@ -118,7 +128,7 @@ void testSqlCommenterStatementBatch(boolean largeUpdate) throws SQLException { span -> span.hasName( SemconvStability.emitStableDatabaseSemconv() - ? "BATCH INSERT dbname.test" + ? "BATCH INSERT test" : "dbname") .hasParent(trace.getSpan(0)))); @@ -149,7 +159,12 @@ void testSqlCommenterPreparedStatement() throws SQLException { trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasNoParent(), - span -> span.hasName("SELECT dbname").hasParent(trace.getSpan(0)))); + span -> + span.hasName( + SemconvStability.emitStableDatabaseSemconv() + ? "SELECT" + : "SELECT dbname") + .hasParent(trace.getSpan(0)))); assertThat(executedSql).hasSize(1); assertThat(executedSql.get(0)).contains(query).contains("traceparent"); @@ -182,7 +197,12 @@ void testSqlCommenterPreparedStatementUpdate(boolean largeUpdate) throws SQLExce trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasNoParent(), - span -> span.hasName("INSERT dbname.test").hasParent(trace.getSpan(0)))); + span -> + span.hasName( + SemconvStability.emitStableDatabaseSemconv() + ? "INSERT test" + : "INSERT dbname.test") + .hasParent(trace.getSpan(0)))); assertThat(executedSql).hasSize(1); assertThat(executedSql.get(0)).contains(query).contains("traceparent"); From 29f7b6ba5a6efbb662ec42d74dc06518427b51eb Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 5 Dec 2025 18:10:38 -0800 Subject: [PATCH 03/26] fix --- .../jdbc/datasource/JdbcTelemetryTest.java | 8 ++++---- .../testing/AbstractPreparedStatementParametersTest.java | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java b/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java index edb42efa7710..da43e76f0b25 100644 --- a/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java +++ b/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java @@ -64,8 +64,8 @@ void buildWithDefaults() throws SQLException { span -> span.hasName("parent"), span -> span.hasName(spanName) - .hasAttribute(equalTo(maybeStable(DB_STATEMENT), "SELECT ?;")) - .hasAttribute( + .hasAttributesSatisfying( + equalTo(maybeStable(DB_STATEMENT), "SELECT ?;"), equalTo( DB_QUERY_SUMMARY, SemconvStability.emitStableDatabaseSemconv() ? spanName : null)))); @@ -234,8 +234,8 @@ void buildWithSanitizationDisabled() throws SQLException { span -> span.hasName("parent"), span -> span.hasName(spanName) - .hasAttribute(equalTo(maybeStable(DB_STATEMENT), "SELECT 1;")) - .hasAttribute( + .hasAttributesSatisfying( + equalTo(maybeStable(DB_STATEMENT), "SELECT 1;"), equalTo( DB_QUERY_SUMMARY, SemconvStability.emitStableDatabaseSemconv() ? spanName : null)))); diff --git a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractPreparedStatementParametersTest.java b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractPreparedStatementParametersTest.java index 1ff7f3fb14dd..6a32328e6eb1 100644 --- a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractPreparedStatementParametersTest.java +++ b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractPreparedStatementParametersTest.java @@ -10,6 +10,7 @@ import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStableDbSystemName; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -19,8 +20,6 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; -import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; - import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import io.opentelemetry.api.trace.SpanKind; From b4f59639a236ca0e8c76902cde271272c4ea3634 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 5 Dec 2025 19:50:04 -0800 Subject: [PATCH 04/26] fix --- .../hibernate/v3_3/AbstractHibernateTest.java | 12 +++++++++--- .../instrumentation/hibernate/v4_0/CriteriaTest.java | 8 ++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java index 31d266a36cc4..4c5691301acb 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java @@ -13,6 +13,7 @@ import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -78,12 +79,16 @@ static void assertClientSpan(SpanDataAssert span, SpanData parent) { equalTo(DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "h2:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies(maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")); + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, val -> val.satisfiesAnyOf(v -> {}, v -> assertThat(v).isNull()))); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation static void assertClientSpan(SpanDataAssert span, SpanData parent, String verb) { - span.hasName(verb.concat(" db1.Value")) + String spanName = emitStableDatabaseSemconv() ? verb + " Value" : verb + " db1.Value"; + String querySummary = emitStableDatabaseSemconv() ? verb + " Value" : null; + span.hasName(spanName) .hasKind(SpanKind.CLIENT) .hasParent(parent) .hasAttributesSatisfyingExactly( @@ -95,7 +100,8 @@ static void assertClientSpan(SpanDataAssert span, SpanData parent, String verb) maybeStable(DB_STATEMENT), stringAssert -> stringAssert.startsWith(verb.toLowerCase(Locale.ROOT))), equalTo(maybeStable(DB_OPERATION), verb), - equalTo(maybeStable(DB_SQL_TABLE), "Value")); + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + equalTo(DB_QUERY_SUMMARY, querySummary)); } static SpanDataAssert assertSessionSpan(SpanDataAssert span, SpanData parent, String spanName) { diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaTest.java index d2d0774b38a1..39906a29cc5d 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaTest.java @@ -15,6 +15,7 @@ import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -74,7 +75,7 @@ void testCriteria(String methodName, Consumer interaction) { HIBERNATE_SESSION_ID, val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("SELECT db1.Value") + span.hasName(emitStableDatabaseSemconv() ? "SELECT Value" : "SELECT db1.Value") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -88,7 +89,10 @@ void testCriteria(String methodName, Consumer interaction) { maybeStable(DB_STATEMENT), stringAssert -> stringAssert.startsWith("select")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT Value" : null)), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) From 43e0b240cd85f734909afad950d80e5a4d9e3cfc Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 5 Dec 2025 20:23:46 -0800 Subject: [PATCH 05/26] fix --- .../hibernate/v4_0/EntityManagerTest.java | 44 ++++-- .../hibernate/v4_0/QueryTest.java | 29 +++- .../hibernate/v4_0/SessionTest.java | 73 ++++++++-- .../test/java/spring/jpa/SpringJpaTest.java | 131 +++++++++++++++--- 4 files changed, 235 insertions(+), 42 deletions(-) diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java index 6811fd97898a..3b5e643ca70e 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java @@ -15,6 +15,7 @@ import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -125,7 +126,13 @@ void testHibernateActions(Parameter parameter) { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value"))); + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class))))); } else { trace.hasSpansSatisfyingExactly( @@ -156,7 +163,13 @@ void testHibernateActions(Parameter parameter) { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -226,7 +239,7 @@ void testHibernatePersist() { .hasParent(trace.getSpan(0)), // persist test has an extra query for getting id of inserted element span -> - span.hasName("SELECT db1.Value") + span.hasName(emitStableDatabaseSemconv() ? "SELECT Value" : "SELECT db1.Value") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -240,7 +253,13 @@ void testHibernatePersist() { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -264,7 +283,13 @@ void testHibernatePersist() { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")))); + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))))); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation @@ -299,7 +324,7 @@ void testAttachesStateToQuery(Function queryBuildMethod) { HIBERNATE_SESSION_ID, val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("SELECT db1.Value") + span.hasName(emitStableDatabaseSemconv() ? "SELECT Value" : "SELECT db1.Value") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -313,7 +338,10 @@ void testAttachesStateToQuery(Function queryBuildMethod) { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "SELECT Value" : null)), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -343,7 +371,7 @@ void testNoResultExceptionIgnored() { .hasStatus(StatusData.unset()) .hasEvents(emptyList()), span -> - span.hasName("SELECT db1.Value") + span.hasName(emitStableDatabaseSemconv() ? "SELECT Value" : "SELECT db1.Value") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)))); } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java index 6938558ec3da..35a88014881e 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java @@ -15,6 +15,7 @@ import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -83,7 +84,13 @@ void testHibernateQueryExecuteUpdateWithTransaction() { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -125,7 +132,7 @@ void testHibernateQuerySingleCall(Parameter parameter) { HIBERNATE_SESSION_ID, val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("SELECT db1.Value") + span.hasName(emitStableDatabaseSemconv() ? "SELECT Value" : "SELECT db1.Value") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -137,7 +144,13 @@ void testHibernateQuerySingleCall(Parameter parameter) { emitStableDatabaseSemconv() ? null : "h2:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("select ")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Value")))); + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))))); } private static Stream providesArgumentsSingleCall() { @@ -199,7 +212,7 @@ void testHibernateQueryIterate() { HIBERNATE_SESSION_ID, val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("SELECT db1.Value") + span.hasName(emitStableDatabaseSemconv() ? "SELECT Value" : "SELECT db1.Value") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -211,7 +224,13 @@ void testHibernateQueryIterate() { emitStableDatabaseSemconv() ? null : "h2:mem:"), satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("select ")), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java index 2642eaf53b39..6af34c794d09 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java @@ -15,6 +15,7 @@ import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -87,7 +88,13 @@ void testHibernateAction(Parameter parameter) { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -196,7 +203,13 @@ void testHibernateActionStateless(Parameter parameter) { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -341,7 +354,13 @@ void testHibernateReplicate(Parameter parameter) { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -365,7 +384,13 @@ void testHibernateReplicate(Parameter parameter) { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")))); + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))))); } private static Stream provideArgumentsHibernateReplicate() { @@ -491,7 +516,13 @@ void testHibernateCommitAction(Parameter parameter) { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")))); + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))))); } private static Stream provideArgumentsHibernateCommitAction() { @@ -670,7 +701,13 @@ void testAttachesStateToQueryCreated(Consumer queryBuilder) { maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -765,7 +802,13 @@ void testHibernateOverlappingSessions() { maybeStable(DB_STATEMENT), stringAssert -> stringAssert.startsWith("insert")), equalTo(maybeStable(DB_OPERATION), "INSERT"), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> { span.hasName("Session.save " + Value.class.getName()) .hasKind(INTERNAL) @@ -803,7 +846,13 @@ void testHibernateOverlappingSessions() { maybeStable(DB_STATEMENT), stringAssert -> stringAssert.startsWith("insert")), equalTo(maybeStable(DB_OPERATION), "INSERT"), - equalTo(maybeStable(DB_SQL_TABLE), "Value")), + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("DELETE db1.Value") .hasKind(CLIENT) @@ -819,7 +868,13 @@ void testHibernateOverlappingSessions() { maybeStable(DB_STATEMENT), stringAssert -> stringAssert.startsWith("delete")), equalTo(maybeStable(DB_OPERATION), "DELETE"), - equalTo(maybeStable(DB_SQL_TABLE), "Value")))); + equalTo(maybeStable(DB_SQL_TABLE), "Value"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))))); assertThat(sessionId1.get()).isNotEqualTo(sessionId2.get()); assertThat(sessionId1.get()).isNotEqualTo(sessionId3.get()); diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java index d2f67a542b25..fd3a4119dc33 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java @@ -14,6 +14,7 @@ import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -72,7 +73,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("SELECT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT Customer" + : "SELECT test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -89,7 +93,13 @@ void testCrud() { Pattern.compile( "select ([^.]+).id([^,]*), ([^.]+).firstName([^,]*), ([^.]+).lastName(.*)from Customer(.*)"))), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")), + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -130,7 +140,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("INSERT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "INSERT Customer" + : "INSERT test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -147,7 +160,13 @@ void testCrud() { Pattern.compile( "insert into Customer (.*) values \\(.*, \\?, \\?\\)"))), equalTo(maybeStable(DB_OPERATION), "INSERT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")), + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -203,7 +222,10 @@ void testCrud() { .getAttributes() .get(stringKey("hibernate.session_id"))))), span -> - span.hasName("INSERT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "INSERT Customer" + : "INSERT test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(3)) .hasAttributesSatisfyingExactly( @@ -220,7 +242,13 @@ void testCrud() { Pattern.compile( "insert into Customer (.*) values \\(.* \\?, \\?\\)"))), equalTo(maybeStable(DB_OPERATION), "INSERT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer"))); + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class))))); } }); testing.clearData(); @@ -252,7 +280,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("SELECT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT Customer" + : "SELECT test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -269,7 +300,13 @@ void testCrud() { Pattern.compile( "select ([^.]+).id([^,]*), ([^.]+).firstName([^,]*), ([^.]+).lastName (.*)from Customer (.*)where ([^.]+).id=\\?"))), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")), + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -283,7 +320,10 @@ void testCrud() { .getAttributes() .get(stringKey("hibernate.session_id"))))), span -> - span.hasName("UPDATE test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "UPDATE Customer" + : "UPDATE test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(3)) .hasAttributesSatisfyingExactly( @@ -297,7 +337,13 @@ void testCrud() { maybeStable(DB_STATEMENT), "update Customer set firstName=?, lastName=? where id=?"), equalTo(maybeStable(DB_OPERATION), "UPDATE"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")))); + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))))); testing.clearData(); Customer foundCustomer = @@ -323,7 +369,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("SELECT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT Customer" + : "SELECT test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -340,7 +389,13 @@ void testCrud() { Pattern.compile( "select ([^.]+).id([^,]*), ([^.]+).firstName([^,]*), ([^.]+).lastName (.*)from Customer (.*)(where ([^.]+).lastName=\\?)"))), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")))); + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))))); testing.clearData(); testing.runWithSpan("parent", () -> repo.delete(foundCustomer)); @@ -363,7 +418,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("SELECT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT Customer" + : "SELECT test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -380,7 +438,13 @@ void testCrud() { Pattern.compile( "select ([^.]+).id([^,]*), ([^.]+).firstName([^,]*), ([^.]+).lastName (.*)from Customer (.*)where ([^.]+).id=\\?"))), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")), + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Session.delete spring.jpa.Customer") .hasKind(INTERNAL) @@ -398,7 +462,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("DELETE test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "DELETE Customer" + : "DELETE test.Customer") .hasKind(CLIENT) .hasAttributesSatisfyingExactly( equalTo(maybeStable(DB_SYSTEM), "hsqldb"), @@ -409,7 +476,13 @@ void testCrud() { emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), equalTo(maybeStable(DB_STATEMENT), "delete from Customer where id=?"), equalTo(maybeStable(DB_OPERATION), "DELETE"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer"))); + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class))))); } else { String findAction; @@ -430,7 +503,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("SELECT test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "SELECT Customer" + : "SELECT test.Customer") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -447,7 +523,13 @@ void testCrud() { Pattern.compile( "select ([^.]+).id([^,]*), ([^.]+).firstName([^,]*), ([^.]+).lastName (.*)from Customer (.*)where ([^.]+).id=\\?"))), equalTo(maybeStable(DB_OPERATION), "SELECT"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer")), + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Session.merge spring.jpa.Customer") .hasKind(INTERNAL) @@ -473,7 +555,10 @@ void testCrud() { stringKey("hibernate.session_id"), val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("DELETE test.Customer") + span.hasName( + emitStableDatabaseSemconv() + ? "DELETE Customer" + : "DELETE test.Customer") .hasKind(CLIENT) .hasAttributesSatisfyingExactly( equalTo(maybeStable(DB_SYSTEM), "hsqldb"), @@ -484,7 +569,13 @@ void testCrud() { emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), equalTo(maybeStable(DB_STATEMENT), "delete from Customer where id=?"), equalTo(maybeStable(DB_OPERATION), "DELETE"), - equalTo(maybeStable(DB_SQL_TABLE), "Customer"))); + equalTo(maybeStable(DB_SQL_TABLE), "Customer"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class))))); } }); } From 06f4804e2a8678e92d0544d9fd9ec01b5b444e5a Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 06:05:24 -0800 Subject: [PATCH 06/26] Fix testStableSemconv tests for hibernate-procedure-call and r2dbc --- .../hibernate/v4_3/ProcedureCallTest.java | 13 +++++++++++-- .../r2dbc/v1_0/SqlCommenterTest.java | 3 ++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java index 49ddba42ce93..7bfe0ad9df6b 100644 --- a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java +++ b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java @@ -13,6 +13,8 @@ import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -118,7 +120,8 @@ void testProcedureCall() { HIBERNATE_SESSION_ID, val -> assertThat(val).isInstanceOf(String.class))), span -> - span.hasName("CALL test.TEST_PROC") + span.hasName( + emitStableDatabaseSemconv() ? "CALL TEST_PROC" : "CALL test.TEST_PROC") .hasKind(CLIENT) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( @@ -129,7 +132,13 @@ void testProcedureCall() { equalTo( DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), - equalTo(maybeStable(DB_OPERATION), "CALL")), + equalTo(maybeStable(DB_OPERATION), "CALL"), + satisfies( + DB_QUERY_SUMMARY, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(String.class)))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) diff --git a/instrumentation/r2dbc-1.0/library/src/test/java/io/opentelemetry/instrumentation/r2dbc/v1_0/SqlCommenterTest.java b/instrumentation/r2dbc-1.0/library/src/test/java/io/opentelemetry/instrumentation/r2dbc/v1_0/SqlCommenterTest.java index 282c7a1d502c..4590dd608b37 100644 --- a/instrumentation/r2dbc-1.0/library/src/test/java/io/opentelemetry/instrumentation/r2dbc/v1_0/SqlCommenterTest.java +++ b/instrumentation/r2dbc-1.0/library/src/test/java/io/opentelemetry/instrumentation/r2dbc/v1_0/SqlCommenterTest.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.r2dbc.v1_0; +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.r2dbc.spi.ConnectionFactoryOptions.CONNECT_TIMEOUT; import static io.r2dbc.spi.ConnectionFactoryOptions.DATABASE; import static io.r2dbc.spi.ConnectionFactoryOptions.DRIVER; @@ -139,7 +140,7 @@ public void beforeQuery(QueryExecutionInfo execInfo) { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName("SELECT " + DB) + span.hasName(emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + DB) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)), span -> From 505d0bb4cb88a3c27d99f35621d6df606e73ec6d Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 07:02:08 -0800 Subject: [PATCH 07/26] refactor --- .../hibernate/v4_3/ProcedureCallTest.java | 8 +--- .../AbstractJdbcInstrumentationTest.java | 37 ++++++++++++------- .../v1_0/AbstractR2dbcStatementTest.java | 23 ++++++------ 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java index 7bfe0ad9df6b..f8c1bafdaadc 100644 --- a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java +++ b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java @@ -13,7 +13,6 @@ import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; @@ -133,12 +132,9 @@ void testProcedureCall() { DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), equalTo(maybeStable(DB_OPERATION), "CALL"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + emitStableDatabaseSemconv() ? "CALL TEST_PROC" : null)), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) diff --git a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java index a1218bef4432..f83dab4c0792 100644 --- a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java +++ b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java @@ -635,7 +635,7 @@ static Stream statementUpdateStream() throws SQLException { new org.h2.Driver().connect(jdbcUrls.get("h2"), null), null, "CREATE TABLE S_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.S_H2", + emitStableDatabaseSemconv() ? "CREATE TABLE S_H2" : "CREATE TABLE jdbcunittest.S_H2", "h2:mem:", "S_H2"), Arguments.of( @@ -643,7 +643,9 @@ static Stream statementUpdateStream() throws SQLException { new EmbeddedDriver().connect(jdbcUrls.get("derby"), null), "APP", "CREATE TABLE S_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.S_DERBY", + emitStableDatabaseSemconv() + ? "CREATE TABLE S_DERBY" + : "CREATE TABLE jdbcunittest.S_DERBY", "derby:memory:", "S_DERBY"), Arguments.of( @@ -659,7 +661,9 @@ static Stream statementUpdateStream() throws SQLException { cpDatasources.get("tomcat").get("h2").getConnection(), null, "CREATE TABLE S_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.S_H2_TOMCAT", + emitStableDatabaseSemconv() + ? "CREATE TABLE S_H2_TOMCAT" + : "CREATE TABLE jdbcunittest.S_H2_TOMCAT", "h2:mem:", "S_H2_TOMCAT"), Arguments.of( @@ -667,7 +671,9 @@ static Stream statementUpdateStream() throws SQLException { cpDatasources.get("tomcat").get("derby").getConnection(), "APP", "CREATE TABLE S_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.S_DERBY_TOMCAT", + emitStableDatabaseSemconv() + ? "CREATE TABLE S_DERBY_TOMCAT" + : "CREATE TABLE jdbcunittest.S_DERBY_TOMCAT", "derby:memory:", "S_DERBY_TOMCAT"), Arguments.of( @@ -683,7 +689,9 @@ static Stream statementUpdateStream() throws SQLException { cpDatasources.get("hikari").get("h2").getConnection(), null, "CREATE TABLE S_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.S_H2_HIKARI", + emitStableDatabaseSemconv() + ? "CREATE TABLE S_H2_HIKARI" + : "CREATE TABLE jdbcunittest.S_H2_HIKARI", "h2:mem:", "S_H2_HIKARI"), Arguments.of( @@ -691,7 +699,9 @@ static Stream statementUpdateStream() throws SQLException { cpDatasources.get("hikari").get("derby").getConnection(), "APP", "CREATE TABLE S_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.S_DERBY_HIKARI", + emitStableDatabaseSemconv() + ? "CREATE TABLE S_DERBY_HIKARI" + : "CREATE TABLE jdbcunittest.S_DERBY_HIKARI", "derby:memory:", "S_DERBY_HIKARI"), Arguments.of( @@ -707,7 +717,9 @@ static Stream statementUpdateStream() throws SQLException { cpDatasources.get("c3p0").get("h2").getConnection(), null, "CREATE TABLE S_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.S_H2_C3P0", + emitStableDatabaseSemconv() + ? "CREATE TABLE S_H2_C3P0" + : "CREATE TABLE jdbcunittest.S_H2_C3P0", "h2:mem:", "S_H2_C3P0"), Arguments.of( @@ -715,7 +727,9 @@ static Stream statementUpdateStream() throws SQLException { cpDatasources.get("c3p0").get("derby").getConnection(), "APP", "CREATE TABLE S_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))", - "CREATE TABLE jdbcunittest.S_DERBY_C3P0", + emitStableDatabaseSemconv() + ? "CREATE TABLE S_DERBY_C3P0" + : "CREATE TABLE jdbcunittest.S_DERBY_C3P0", "derby:memory:", "S_DERBY_C3P0"), Arguments.of( @@ -753,8 +767,7 @@ void testStatementUpdate( trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), span -> - span.hasName( - emitStableDatabaseSemconv() ? "CREATE TABLE " + table : spanName) + span.hasName(spanName) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -768,9 +781,7 @@ DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), equalTo(maybeStable(DB_SQL_TABLE), table), equalTo( DB_QUERY_SUMMARY, - emitStableDatabaseSemconv() - ? "CREATE TABLE " + table - : null)))); + emitStableDatabaseSemconv() ? spanName : null)))); } static Stream preparedStatementUpdateStream() throws SQLException { diff --git a/instrumentation/r2dbc-1.0/testing/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/AbstractR2dbcStatementTest.java b/instrumentation/r2dbc-1.0/testing/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/AbstractR2dbcStatementTest.java index d3498e62604b..0991b7ef8038 100644 --- a/instrumentation/r2dbc-1.0/testing/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/AbstractR2dbcStatementTest.java +++ b/instrumentation/r2dbc-1.0/testing/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/AbstractR2dbcStatementTest.java @@ -162,18 +162,13 @@ void testQueries(Parameter parameter) { .blockLast(Duration.ofMinutes(1)); }); - String spanName = - emitStableDatabaseSemconv() - ? parameter.operation + (parameter.table != null ? " " + parameter.table : "") - : parameter.spanName; - getTesting() .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL), span -> - span.hasName(spanName) + span.hasName(parameter.spanName) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( @@ -190,7 +185,7 @@ void testQueries(Parameter parameter) { equalTo(maybeStable(DB_SQL_TABLE), parameter.table), equalTo( DB_QUERY_SUMMARY, - emitStableDatabaseSemconv() ? spanName : null), + emitStableDatabaseSemconv() ? parameter.spanName : null), equalTo(PEER_SERVICE, "test-peer-service"), equalTo(SERVER_ADDRESS, container.getHost()), equalTo(SERVER_PORT, port)), @@ -212,7 +207,7 @@ private static Stream provideParameters() { system.system, "SELECT 3", "SELECT ?", - "SELECT " + DB, + emitStableDatabaseSemconv() ? "SELECT" : "SELECT " + DB, null, "SELECT"))), Arguments.of( @@ -222,7 +217,9 @@ private static Stream provideParameters() { system.system, "CREATE TABLE person (id SERIAL PRIMARY KEY, first_name VARCHAR(255), last_name VARCHAR(255))", "CREATE TABLE person (id SERIAL PRIMARY KEY, first_name VARCHAR(?), last_name VARCHAR(?))", - "CREATE TABLE " + DB + ".person", + emitStableDatabaseSemconv() + ? "CREATE TABLE person" + : "CREATE TABLE " + DB + ".person", "person", "CREATE TABLE"))), Arguments.of( @@ -232,7 +229,9 @@ private static Stream provideParameters() { system.system, "INSERT INTO person (id, first_name, last_name) values (1, 'tom', 'johnson')", "INSERT INTO person (id, first_name, last_name) values (?, ?, ?)", - "INSERT " + DB + ".person", + emitStableDatabaseSemconv() + ? "INSERT person" + : "INSERT " + DB + ".person", "person", "INSERT"))), Arguments.of( @@ -242,7 +241,9 @@ private static Stream provideParameters() { system.system, "SELECT * FROM person where first_name = 'tom'", "SELECT * FROM person where first_name = ?", - "SELECT " + DB + ".person", + emitStableDatabaseSemconv() + ? "SELECT person" + : "SELECT " + DB + ".person", "person", "SELECT"))))); } From c5970aee070f5256a6b5ea8d72aac06a5390b870 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 07:44:27 -0800 Subject: [PATCH 08/26] updates --- .../hibernate/v3_3/AbstractHibernateTest.java | 9 +- .../hibernate/v4_0/EntityManagerTest.java | 44 ++++--- .../hibernate/v4_0/QueryTest.java | 33 ++++-- .../hibernate/v4_0/SessionTest.java | 99 ++++++++++------ .../test/java/spring/jpa/SpringJpaTest.java | 110 +++++++++++------- .../hibernate/v6_0/EntityManagerTest.java | 12 +- 6 files changed, 199 insertions(+), 108 deletions(-) diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java index 4c5691301acb..3ad247f1900f 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java @@ -81,7 +81,14 @@ static void assertClientSpan(SpanDataAssert span, SpanData parent) { satisfies(maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( - DB_QUERY_SUMMARY, val -> val.satisfiesAnyOf(v -> {}, v -> assertThat(v).isNull()))); + DB_QUERY_SUMMARY, + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java index 3b5e643ca70e..56f5a0f2ab5b 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java @@ -129,10 +129,13 @@ void testHibernateActions(Parameter parameter) { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class))))); + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + }))); } else { trace.hasSpansSatisfyingExactly( @@ -166,10 +169,13 @@ void testHibernateActions(Parameter parameter) { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -256,10 +262,13 @@ void testHibernatePersist() { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -286,10 +295,13 @@ void testHibernatePersist() { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))))); + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })))); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java index 35a88014881e..1e5d7dc043d0 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java @@ -87,10 +87,13 @@ void testHibernateQueryExecuteUpdateWithTransaction() { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -147,10 +150,13 @@ void testHibernateQuerySingleCall(Parameter parameter) { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))))); + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })))); } private static Stream providesArgumentsSingleCall() { @@ -227,10 +233,13 @@ void testHibernateQueryIterate() { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java index 6af34c794d09..5e7bdf7c497b 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java @@ -91,10 +91,13 @@ void testHibernateAction(Parameter parameter) { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -206,10 +209,13 @@ void testHibernateActionStateless(Parameter parameter) { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -357,10 +363,13 @@ void testHibernateReplicate(Parameter parameter) { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -387,10 +396,13 @@ void testHibernateReplicate(Parameter parameter) { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))))); + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })))); } private static Stream provideArgumentsHibernateReplicate() { @@ -519,10 +531,13 @@ void testHibernateCommitAction(Parameter parameter) { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))))); + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })))); } private static Stream provideArgumentsHibernateCommitAction() { @@ -704,10 +719,13 @@ void testAttachesStateToQueryCreated(Consumer queryBuilder) { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -805,10 +823,13 @@ void testHibernateOverlappingSessions() { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> { span.hasName("Session.save " + Value.class.getName()) .hasKind(INTERNAL) @@ -849,10 +870,13 @@ void testHibernateOverlappingSessions() { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("DELETE db1.Value") .hasKind(CLIENT) @@ -871,10 +895,13 @@ void testHibernateOverlappingSessions() { equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))))); + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })))); assertThat(sessionId1.get()).isNotEqualTo(sessionId2.get()); assertThat(sessionId1.get()).isNotEqualTo(sessionId3.get()); diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java index fd3a4119dc33..231b2e73e4a4 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java @@ -96,10 +96,13 @@ void testCrud() { equalTo(maybeStable(DB_SQL_TABLE), "Customer"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -163,10 +166,13 @@ void testCrud() { equalTo(maybeStable(DB_SQL_TABLE), "Customer"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -245,10 +251,13 @@ void testCrud() { equalTo(maybeStable(DB_SQL_TABLE), "Customer"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class))))); + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + }))); } }); testing.clearData(); @@ -303,10 +312,13 @@ void testCrud() { equalTo(maybeStable(DB_SQL_TABLE), "Customer"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -340,10 +352,13 @@ void testCrud() { equalTo(maybeStable(DB_SQL_TABLE), "Customer"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))))); + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })))); testing.clearData(); Customer foundCustomer = @@ -392,10 +407,13 @@ void testCrud() { equalTo(maybeStable(DB_SQL_TABLE), "Customer"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))))); + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })))); testing.clearData(); testing.runWithSpan("parent", () -> repo.delete(foundCustomer)); @@ -441,10 +459,13 @@ void testCrud() { equalTo(maybeStable(DB_SQL_TABLE), "Customer"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Session.delete spring.jpa.Customer") .hasKind(INTERNAL) @@ -479,10 +500,13 @@ void testCrud() { equalTo(maybeStable(DB_SQL_TABLE), "Customer"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class))))); + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + }))); } else { String findAction; @@ -526,10 +550,13 @@ void testCrud() { equalTo(maybeStable(DB_SQL_TABLE), "Customer"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class)))), + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })), span -> span.hasName("Session.merge spring.jpa.Customer") .hasKind(INTERNAL) @@ -572,10 +599,13 @@ void testCrud() { equalTo(maybeStable(DB_SQL_TABLE), "Customer"), satisfies( DB_QUERY_SUMMARY, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(String.class))))); + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + }))); } }); } diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java index 0f73edb839f8..93f7f2feb317 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java @@ -335,12 +335,18 @@ private static void assertClientSpan(SpanDataAssert span, SpanData parent) { satisfies(maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), equalTo(maybeStable(DB_SQL_TABLE), "Value"), satisfies( - DB_QUERY_SUMMARY, val -> val.satisfiesAnyOf(v -> {}, v -> assertThat(v).isNull()))); + DB_QUERY_SUMMARY, + val -> { + if (emitStableDatabaseSemconv()) { + val.isInstanceOf(String.class); + } else { + val.isNull(); + } + })); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation private static void assertClientSpan(SpanDataAssert span, SpanData parent, String spanName) { - String querySummary = emitStableDatabaseSemconv() ? spanName.replace("db1.", "") : null; span.hasName(spanName) .hasKind(SpanKind.CLIENT) .hasParent(parent) @@ -352,7 +358,7 @@ private static void assertClientSpan(SpanDataAssert span, SpanData parent, Strin satisfies(maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), satisfies(maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), equalTo(maybeStable(DB_SQL_TABLE), "Value"), - equalTo(DB_QUERY_SUMMARY, querySummary)); + equalTo(DB_QUERY_SUMMARY, emitStableDatabaseSemconv() ? spanName : null)); } private static void assertSessionSpan(SpanDataAssert span, SpanData parent, String spanName) { From 40dd39024629380c70117094a6488a9d464258aa Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 10:10:51 -0800 Subject: [PATCH 09/26] updates --- .../hibernate/v4_0/EntityManagerTest.java | 10 +- .../hibernate/v4_0/QueryTest.java | 20 +--- .../hibernate/v4_0/SessionTest.java | 30 ++---- .../test/java/spring/jpa/SpringJpaTest.java | 100 ++++-------------- 4 files changed, 32 insertions(+), 128 deletions(-) diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java index 56f5a0f2ab5b..9bca33e19beb 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java @@ -260,15 +260,9 @@ void testHibernatePersist() { satisfies( maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), equalTo(maybeStable(DB_SQL_TABLE), "Value"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })), + emitStableDatabaseSemconv() ? "SELECT Value" : null)), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java index 1e5d7dc043d0..432ad3df616d 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java @@ -148,15 +148,9 @@ void testHibernateQuerySingleCall(Parameter parameter) { satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("select ")), equalTo(maybeStable(DB_OPERATION), "SELECT"), equalTo(maybeStable(DB_SQL_TABLE), "Value"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })))); + emitStableDatabaseSemconv() ? "SELECT Value" : null)))); } private static Stream providesArgumentsSingleCall() { @@ -231,15 +225,9 @@ void testHibernateQueryIterate() { satisfies(maybeStable(DB_STATEMENT), val -> val.startsWith("select ")), equalTo(maybeStable(DB_OPERATION), "SELECT"), equalTo(maybeStable(DB_SQL_TABLE), "Value"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })), + emitStableDatabaseSemconv() ? "SELECT Value" : null)), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java index 5e7bdf7c497b..39c87d152521 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java @@ -821,15 +821,9 @@ void testHibernateOverlappingSessions() { stringAssert -> stringAssert.startsWith("insert")), equalTo(maybeStable(DB_OPERATION), "INSERT"), equalTo(maybeStable(DB_SQL_TABLE), "Value"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })), + emitStableDatabaseSemconv() ? "INSERT Value" : null)), span -> { span.hasName("Session.save " + Value.class.getName()) .hasKind(INTERNAL) @@ -868,15 +862,9 @@ void testHibernateOverlappingSessions() { stringAssert -> stringAssert.startsWith("insert")), equalTo(maybeStable(DB_OPERATION), "INSERT"), equalTo(maybeStable(DB_SQL_TABLE), "Value"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })), + emitStableDatabaseSemconv() ? "INSERT Value" : null)), span -> span.hasName("DELETE db1.Value") .hasKind(CLIENT) @@ -893,15 +881,9 @@ void testHibernateOverlappingSessions() { stringAssert -> stringAssert.startsWith("delete")), equalTo(maybeStable(DB_OPERATION), "DELETE"), equalTo(maybeStable(DB_SQL_TABLE), "Value"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })))); + emitStableDatabaseSemconv() ? "DELETE Value" : null)))); assertThat(sessionId1.get()).isNotEqualTo(sessionId2.get()); assertThat(sessionId1.get()).isNotEqualTo(sessionId3.get()); diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java index 231b2e73e4a4..884107f3a581 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java @@ -94,15 +94,9 @@ void testCrud() { "select ([^.]+).id([^,]*), ([^.]+).firstName([^,]*), ([^.]+).lastName(.*)from Customer(.*)"))), equalTo(maybeStable(DB_OPERATION), "SELECT"), equalTo(maybeStable(DB_SQL_TABLE), "Customer"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })), + emitStableDatabaseSemconv() ? "SELECT Customer" : null)), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -164,15 +158,9 @@ void testCrud() { "insert into Customer (.*) values \\(.*, \\?, \\?\\)"))), equalTo(maybeStable(DB_OPERATION), "INSERT"), equalTo(maybeStable(DB_SQL_TABLE), "Customer"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })), + emitStableDatabaseSemconv() ? "INSERT Customer" : null)), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -249,15 +237,9 @@ void testCrud() { "insert into Customer (.*) values \\(.* \\?, \\?\\)"))), equalTo(maybeStable(DB_OPERATION), "INSERT"), equalTo(maybeStable(DB_SQL_TABLE), "Customer"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - }))); + emitStableDatabaseSemconv() ? "INSERT Customer" : null))); } }); testing.clearData(); @@ -310,15 +292,9 @@ void testCrud() { "select ([^.]+).id([^,]*), ([^.]+).firstName([^,]*), ([^.]+).lastName (.*)from Customer (.*)where ([^.]+).id=\\?"))), equalTo(maybeStable(DB_OPERATION), "SELECT"), equalTo(maybeStable(DB_SQL_TABLE), "Customer"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })), + emitStableDatabaseSemconv() ? "SELECT Customer" : null)), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) @@ -350,15 +326,9 @@ void testCrud() { "update Customer set firstName=?, lastName=? where id=?"), equalTo(maybeStable(DB_OPERATION), "UPDATE"), equalTo(maybeStable(DB_SQL_TABLE), "Customer"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })))); + emitStableDatabaseSemconv() ? "UPDATE Customer" : null)))); testing.clearData(); Customer foundCustomer = @@ -405,15 +375,9 @@ void testCrud() { "select ([^.]+).id([^,]*), ([^.]+).firstName([^,]*), ([^.]+).lastName (.*)from Customer (.*)(where ([^.]+).lastName=\\?)"))), equalTo(maybeStable(DB_OPERATION), "SELECT"), equalTo(maybeStable(DB_SQL_TABLE), "Customer"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })))); + emitStableDatabaseSemconv() ? "SELECT Customer" : null)))); testing.clearData(); testing.runWithSpan("parent", () -> repo.delete(foundCustomer)); @@ -457,15 +421,9 @@ void testCrud() { "select ([^.]+).id([^,]*), ([^.]+).firstName([^,]*), ([^.]+).lastName (.*)from Customer (.*)where ([^.]+).id=\\?"))), equalTo(maybeStable(DB_OPERATION), "SELECT"), equalTo(maybeStable(DB_SQL_TABLE), "Customer"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })), + emitStableDatabaseSemconv() ? "SELECT Customer" : null)), span -> span.hasName("Session.delete spring.jpa.Customer") .hasKind(INTERNAL) @@ -498,15 +456,9 @@ void testCrud() { equalTo(maybeStable(DB_STATEMENT), "delete from Customer where id=?"), equalTo(maybeStable(DB_OPERATION), "DELETE"), equalTo(maybeStable(DB_SQL_TABLE), "Customer"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - }))); + emitStableDatabaseSemconv() ? "DELETE Customer" : null))); } else { String findAction; @@ -548,15 +500,9 @@ void testCrud() { "select ([^.]+).id([^,]*), ([^.]+).firstName([^,]*), ([^.]+).lastName (.*)from Customer (.*)where ([^.]+).id=\\?"))), equalTo(maybeStable(DB_OPERATION), "SELECT"), equalTo(maybeStable(DB_SQL_TABLE), "Customer"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - })), + emitStableDatabaseSemconv() ? "SELECT Customer" : null)), span -> span.hasName("Session.merge spring.jpa.Customer") .hasKind(INTERNAL) @@ -597,15 +543,9 @@ void testCrud() { equalTo(maybeStable(DB_STATEMENT), "delete from Customer where id=?"), equalTo(maybeStable(DB_OPERATION), "DELETE"), equalTo(maybeStable(DB_SQL_TABLE), "Customer"), - satisfies( + equalTo( DB_QUERY_SUMMARY, - val -> { - if (emitStableDatabaseSemconv()) { - val.isInstanceOf(String.class); - } else { - val.isNull(); - } - }))); + emitStableDatabaseSemconv() ? "DELETE Customer" : null))); } }); } From 2401d11a85ccfed556bdff8059478fd70a8ab44d Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 10:11:42 -0800 Subject: [PATCH 10/26] update --- .../cassandra/v4/common/AbstractCassandraTest.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/instrumentation/cassandra/cassandra-4-common/testing/src/main/java/io/opentelemetry/cassandra/v4/common/AbstractCassandraTest.java b/instrumentation/cassandra/cassandra-4-common/testing/src/main/java/io/opentelemetry/cassandra/v4/common/AbstractCassandraTest.java index 13b006c5f30c..85a6a50bec21 100644 --- a/instrumentation/cassandra/cassandra-4-common/testing/src/main/java/io/opentelemetry/cassandra/v4/common/AbstractCassandraTest.java +++ b/instrumentation/cassandra/cassandra-4-common/testing/src/main/java/io/opentelemetry/cassandra/v4/common/AbstractCassandraTest.java @@ -330,13 +330,6 @@ private static Stream provideAsyncParameters() { "users")))); } - protected static String querySummary(String operation, String table) { - if (table == null) { - return operation; - } - return operation + " " + table; - } - protected static class Parameter { public final String keyspace; public final String statement; From e5e7d32342527a748128aa6262a74b59db459195 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 10:21:56 -0800 Subject: [PATCH 11/26] batch --- .../semconv/db/SqlClientAttributesExtractor.java | 12 ++++++++++-- .../semconv/db/SqlClientAttributesExtractorTest.java | 8 ++++---- .../testing/AbstractJdbcInstrumentationTest.java | 8 ++++++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java index 631deede44cc..303b293e519e 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java @@ -120,7 +120,11 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST DB_QUERY_TEXT, statementSanitizationEnabled ? sanitizedStatement.getFullStatement() : rawQueryText); internalSet(attributes, DB_OPERATION_NAME, isBatch ? "BATCH " + operation : operation); - internalSet(attributes, DB_QUERY_SUMMARY, sanitizedStatement.getQuerySummary()); + String querySummary = sanitizedStatement.getQuerySummary(); + internalSet( + attributes, + DB_QUERY_SUMMARY, + isBatch && querySummary != null ? "BATCH " + querySummary : querySummary); if (!SQL_CALL.equals(operation)) { internalSet(attributes, DB_COLLECTION_NAME, sanitizedStatement.getMainIdentifier()); } @@ -132,7 +136,11 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST String operation = multiQuery.getOperation() != null ? "BATCH " + multiQuery.getOperation() : "BATCH"; internalSet(attributes, DB_OPERATION_NAME, operation); - internalSet(attributes, DB_QUERY_SUMMARY, multiQuery.getQuerySummary()); + String querySummary = multiQuery.getQuerySummary(); + internalSet( + attributes, + DB_QUERY_SUMMARY, + querySummary != null ? "BATCH " + querySummary : null); if (multiQuery.getMainIdentifier() != null && (multiQuery.getOperation() == null || !SQL_CALL.equals(multiQuery.getOperation()))) { diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java index 8cd57e634c68..9552aa866e3a 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java @@ -285,7 +285,7 @@ void shouldExtractSingleQueryBatchAttributes() { entry(DbAttributes.DB_QUERY_TEXT, "INSERT INTO potato VALUES(?)"), entry(DbAttributes.DB_OPERATION_NAME, "BATCH INSERT"), entry(DbAttributes.DB_COLLECTION_NAME, "potato"), - entry(DbAttributes.DB_QUERY_SUMMARY, "INSERT potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "BATCH INSERT potato"), entry(DB_OPERATION_BATCH_SIZE, 2L)); } else if (SemconvStability.emitOldDatabaseSemconv()) { assertThat(startAttributes.build()) @@ -301,7 +301,7 @@ void shouldExtractSingleQueryBatchAttributes() { entry(DbAttributes.DB_QUERY_TEXT, "INSERT INTO potato VALUES(?)"), entry(DbAttributes.DB_OPERATION_NAME, "BATCH INSERT"), entry(DbAttributes.DB_COLLECTION_NAME, "potato"), - entry(DbAttributes.DB_QUERY_SUMMARY, "INSERT potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "BATCH INSERT potato"), entry(DB_OPERATION_BATCH_SIZE, 2L)); } @@ -339,7 +339,7 @@ void shouldExtractMultiQueryBatchAttributes() { entry(DbAttributes.DB_QUERY_TEXT, "INSERT INTO potato VALUES(?)"), entry(DbAttributes.DB_OPERATION_NAME, "BATCH INSERT"), entry(DbAttributes.DB_COLLECTION_NAME, "potato"), - entry(DbAttributes.DB_QUERY_SUMMARY, "INSERT potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "BATCH INSERT potato"), entry(DB_OPERATION_BATCH_SIZE, 2L)); } else if (SemconvStability.emitOldDatabaseSemconv()) { assertThat(startAttributes.build()) @@ -351,7 +351,7 @@ void shouldExtractMultiQueryBatchAttributes() { entry(DbAttributes.DB_QUERY_TEXT, "INSERT INTO potato VALUES(?)"), entry(DbAttributes.DB_OPERATION_NAME, "BATCH INSERT"), entry(DbAttributes.DB_COLLECTION_NAME, "potato"), - entry(DbAttributes.DB_QUERY_SUMMARY, "INSERT potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "BATCH INSERT potato"), entry(DB_OPERATION_BATCH_SIZE, 2L)); } diff --git a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java index f83dab4c0792..1e374701a4de 100644 --- a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java +++ b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java @@ -1621,7 +1621,9 @@ DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), emitStableDatabaseSemconv() ? 2L : null), equalTo( DB_QUERY_SUMMARY, - emitStableDatabaseSemconv() ? "INSERT " + tableName : null)))); + emitStableDatabaseSemconv() + ? "BATCH INSERT " + tableName + : null)))); } @ParameterizedTest @@ -1788,7 +1790,9 @@ DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), emitStableDatabaseSemconv() ? 2L : null), equalTo( DB_QUERY_SUMMARY, - emitStableDatabaseSemconv() ? "INSERT " + tableName : null)))); + emitStableDatabaseSemconv() + ? "BATCH INSERT " + tableName + : null)))); } // test that sqlcommenter is not enabled by default From 0da891b69061546c83d98061cab83a3add5ae5c8 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 12:02:05 -0800 Subject: [PATCH 12/26] renames --- .../semconv/db/DbClientSpanNameExtractor.java | 53 ++++++++----- .../api/incubator/semconv/db/MultiQuery.java | 68 ++++++++-------- .../db/SqlClientAttributesExtractor.java | 37 +++------ .../semconv/db/SqlStatementInfo.java | 66 ++++++++++++++-- .../db/SqlClientAttributesExtractorTest.java | 55 +++++++++++++ .../semconv/db/SqlStatementSanitizerTest.java | 34 ++++---- .../common/ClickHouseAttributesGetter.java | 2 +- .../v2_0/CouchbaseQuerySanitizerTest.java | 2 +- .../couchbase/v2_0/CouchbaseRequestInfo.java | 2 +- .../geode/GeodeDbAttributesGetter.java | 2 +- .../hibernate/v3_3/QueryInstrumentation.java | 4 +- .../v3_3/SessionInstrumentation.java | 4 +- .../hibernate/v4_0/QueryInstrumentation.java | 4 +- .../v4_0/SessionInstrumentation.java | 4 +- .../hibernate/v6_0/QueryInstrumentation.java | 4 +- .../v6_0/SessionInstrumentation.java | 4 +- ...erationNameUtil.java => SpanNameUtil.java} | 24 +++--- .../v2_4/InfluxDbAttributesGetter.java | 4 +- .../AbstractJdbcInstrumentationTest.java | 77 ++++++++++++++++++- 19 files changed, 314 insertions(+), 136 deletions(-) rename instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/{OperationNameUtil.java => SpanNameUtil.java} (77%) diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java index 9193aa946303..446e86f023b7 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java @@ -29,10 +29,10 @@ public static SpanNameExtractor create( * Returns a {@link SpanNameExtractor} that constructs the span name according to DB semantic * conventions: {@code .}. * - * @see SqlStatementInfo#getOperation() used to extract {@code }. + * @see SqlStatementInfo#getOperationName() used to extract {@code }. * @see DbClientAttributesGetter#getDbNamespace(Object) used to extract {@code }. - * @see SqlStatementInfo#getMainIdentifier() used to extract {@code } or stored - * procedure name. + * @see SqlStatementInfo#getCollectionName() used to extract table name. + * @see SqlStatementInfo#getStoredProcedureName() used to extract stored procedure name. */ public static SpanNameExtractor create( SqlClientAttributesGetter getter) { @@ -44,24 +44,30 @@ public static SpanNameExtractor create( private DbClientSpanNameExtractor() {} protected String computeSpanName( - @Nullable String dbName, @Nullable String operation, @Nullable String mainIdentifier) { + @Nullable String dbName, + @Nullable String operation, + @Nullable String collectionName, + @Nullable String storedProcedureName) { + // Use whichever identifier is available (they're mutually exclusive) + String identifier = collectionName != null ? collectionName : storedProcedureName; + if (operation == null) { return dbName == null ? DEFAULT_SPAN_NAME : dbName; } StringBuilder name = new StringBuilder(operation); - if (dbName != null || mainIdentifier != null) { + if (dbName != null || identifier != null) { name.append(' '); } // skip db name if identifier already has a db name prefixed to it - if (dbName != null && (mainIdentifier == null || mainIdentifier.indexOf('.') == -1)) { + if (dbName != null && (identifier == null || identifier.indexOf('.') == -1)) { name.append(dbName); - if (mainIdentifier != null) { + if (identifier != null) { name.append('.'); } } - if (mainIdentifier != null) { - name.append(mainIdentifier); + if (identifier != null) { + name.append(identifier); } return name.toString(); } @@ -79,7 +85,7 @@ private GenericDbClientSpanNameExtractor(DbClientAttributesGetter ge public String extract(REQUEST request) { String namespace = getter.getDbNamespace(request); String operationName = getter.getDbOperationName(request); - return computeSpanName(namespace, operationName, null); + return computeSpanName(namespace, operationName, null, null); } } @@ -98,17 +104,20 @@ public String extract(REQUEST request) { Collection rawQueryTexts = getter.getRawQueryTexts(request); if (rawQueryTexts.isEmpty()) { - return computeSpanName(namespace, null, null); + return computeSpanName(namespace, null, null, null); } if (!SemconvStability.emitStableDatabaseSemconv()) { if (rawQueryTexts.size() > 1) { // for backcompat(?) - return computeSpanName(namespace, null, null); + return computeSpanName(namespace, null, null, null); } SqlStatementInfo sanitizedStatement = SqlStatementSanitizerUtil.sanitize(rawQueryTexts.iterator().next()); return computeSpanName( - namespace, sanitizedStatement.getOperation(), sanitizedStatement.getMainIdentifier()); + namespace, + sanitizedStatement.getOperationName(), + sanitizedStatement.getCollectionName(), + sanitizedStatement.getStoredProcedureName()); } // For stable semconv, use query summary as span name if available @@ -123,23 +132,25 @@ public String extract(REQUEST request) { return querySummary; } // Fall back to old behavior if no query summary - String operation = sanitizedStatement.getOperation(); + String operation = sanitizedStatement.getOperationName(); if (isBatch(request)) { operation = "BATCH " + operation; } - return computeSpanName(namespace, operation, sanitizedStatement.getMainIdentifier()); + return computeSpanName( + namespace, + operation, + sanitizedStatement.getCollectionName(), + sanitizedStatement.getStoredProcedureName()); } MultiQuery multiQuery = MultiQuery.analyze(rawQueryTexts, false); String querySummary = multiQuery.getQuerySummary(); - if (querySummary != null) { - return "BATCH " + querySummary; + // Fall back to old behavior if query summary equals operation (no common table) + if (!querySummary.equals(multiQuery.getOperationName())) { + return querySummary; } - // Fall back to old behavior if no query summary return computeSpanName( - namespace, - multiQuery.getOperation() != null ? "BATCH " + multiQuery.getOperation() : "BATCH", - multiQuery.getMainIdentifier()); + namespace, multiQuery.getOperationName(), multiQuery.getCollectionName(), null); } private boolean isBatch(REQUEST request) { diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java index 8241c3b7e397..833cb8ed7418 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java @@ -12,64 +12,62 @@ class MultiQuery { - @Nullable private final String mainIdentifier; - @Nullable private final String operation; - @Nullable private final String querySummary; - private final Set statements; + @Nullable private final String collectionName; + private final String operationName; + private final String querySummary; + private final Set queryTexts; private MultiQuery( - @Nullable String mainIdentifier, - @Nullable String operation, - @Nullable String querySummary, - Set statements) { - this.mainIdentifier = mainIdentifier; - this.operation = operation; + @Nullable String collectionName, + String operationName, + String querySummary, + Set queryTexts) { + this.collectionName = collectionName; + this.operationName = operationName; this.querySummary = querySummary; - this.statements = statements; + this.queryTexts = queryTexts; } static MultiQuery analyze( Collection rawQueryTexts, boolean statementSanitizationEnabled) { - UniqueValue uniqueMainIdentifier = new UniqueValue(); - UniqueValue uniqueOperation = new UniqueValue(); + UniqueValue uniqueCollectionName = new UniqueValue(); + UniqueValue uniqueOperationName = new UniqueValue(); UniqueValue uniqueQuerySummary = new UniqueValue(); - Set uniqueStatements = new LinkedHashSet<>(); + Set uniqueQueryTexts = new LinkedHashSet<>(); for (String rawQueryText : rawQueryTexts) { SqlStatementInfo sanitizedStatement = SqlStatementSanitizerUtil.sanitize(rawQueryText); - String mainIdentifier = sanitizedStatement.getMainIdentifier(); - uniqueMainIdentifier.set(mainIdentifier); - String operation = sanitizedStatement.getOperation(); - uniqueOperation.set(operation); - String querySummary = sanitizedStatement.getQuerySummary(); - uniqueQuerySummary.set(querySummary); - uniqueStatements.add( - statementSanitizationEnabled ? sanitizedStatement.getFullStatement() : rawQueryText); + uniqueCollectionName.set(sanitizedStatement.getCollectionName()); + uniqueOperationName.set(sanitizedStatement.getOperationName()); + uniqueQuerySummary.set(sanitizedStatement.getQuerySummary()); + uniqueQueryTexts.add( + statementSanitizationEnabled ? sanitizedStatement.getQueryText() : rawQueryText); } - return new MultiQuery( - uniqueMainIdentifier.getValue(), - uniqueOperation.getValue(), - uniqueQuerySummary.getValue(), - uniqueStatements); + String operationName = uniqueOperationName.getValue(); + String querySummary = uniqueQuerySummary.getValue(); + + String collectionName = uniqueCollectionName.getValue(); + String batchOperationName = operationName != null ? "BATCH " + operationName : "BATCH"; + String batchQuerySummary = querySummary != null ? "BATCH " + querySummary : batchOperationName; + + return new MultiQuery(collectionName, batchOperationName, batchQuerySummary, uniqueQueryTexts); } @Nullable - public String getMainIdentifier() { - return mainIdentifier; + public String getCollectionName() { + return collectionName; } - @Nullable - public String getOperation() { - return operation; + public String getOperationName() { + return operationName; } - @Nullable public String getQuerySummary() { return querySummary; } - public Set getStatements() { - return statements; + public Set getQueryTexts() { + return queryTexts; } private static class UniqueValue { diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java index 303b293e519e..bb7d8451cb9b 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java @@ -60,8 +60,6 @@ public static SqlClientAttributesExtractorBuilder(getter); } - private static final String SQL_CALL = "CALL"; - private final SqlClientAttributesGetter getter; private final InternalNetworkAttributesExtractor internalNetworkExtractor; private final ServerAttributesExtractor serverAttributesExtractor; @@ -95,15 +93,13 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST if (rawQueryTexts.size() == 1) { // for backcompat(?) String rawQueryText = rawQueryTexts.iterator().next(); SqlStatementInfo sanitizedStatement = SqlStatementSanitizerUtil.sanitize(rawQueryText); - String operation = sanitizedStatement.getOperation(); + String operation = sanitizedStatement.getOperationName(); internalSet( attributes, DB_STATEMENT, - statementSanitizationEnabled ? sanitizedStatement.getFullStatement() : rawQueryText); + statementSanitizationEnabled ? sanitizedStatement.getQueryText() : rawQueryText); internalSet(attributes, DB_OPERATION, operation); - if (!SQL_CALL.equals(operation)) { - internalSet(attributes, oldSemconvTableAttribute, sanitizedStatement.getMainIdentifier()); - } + internalSet(attributes, oldSemconvTableAttribute, sanitizedStatement.getCollectionName()); } } @@ -114,38 +110,25 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST if (rawQueryTexts.size() == 1) { String rawQueryText = rawQueryTexts.iterator().next(); SqlStatementInfo sanitizedStatement = SqlStatementSanitizerUtil.sanitize(rawQueryText); - String operation = sanitizedStatement.getOperation(); + String operation = sanitizedStatement.getOperationName(); internalSet( attributes, DB_QUERY_TEXT, - statementSanitizationEnabled ? sanitizedStatement.getFullStatement() : rawQueryText); + statementSanitizationEnabled ? sanitizedStatement.getQueryText() : rawQueryText); internalSet(attributes, DB_OPERATION_NAME, isBatch ? "BATCH " + operation : operation); String querySummary = sanitizedStatement.getQuerySummary(); internalSet( attributes, DB_QUERY_SUMMARY, isBatch && querySummary != null ? "BATCH " + querySummary : querySummary); - if (!SQL_CALL.equals(operation)) { - internalSet(attributes, DB_COLLECTION_NAME, sanitizedStatement.getMainIdentifier()); - } + internalSet(attributes, DB_COLLECTION_NAME, sanitizedStatement.getCollectionName()); } else if (rawQueryTexts.size() > 1) { MultiQuery multiQuery = MultiQuery.analyze(getter.getRawQueryTexts(request), statementSanitizationEnabled); - internalSet(attributes, DB_QUERY_TEXT, join("; ", multiQuery.getStatements())); - - String operation = - multiQuery.getOperation() != null ? "BATCH " + multiQuery.getOperation() : "BATCH"; - internalSet(attributes, DB_OPERATION_NAME, operation); - String querySummary = multiQuery.getQuerySummary(); - internalSet( - attributes, - DB_QUERY_SUMMARY, - querySummary != null ? "BATCH " + querySummary : null); - - if (multiQuery.getMainIdentifier() != null - && (multiQuery.getOperation() == null || !SQL_CALL.equals(multiQuery.getOperation()))) { - internalSet(attributes, DB_COLLECTION_NAME, multiQuery.getMainIdentifier()); - } + internalSet(attributes, DB_QUERY_TEXT, join("; ", multiQuery.getQueryTexts())); + internalSet(attributes, DB_OPERATION_NAME, multiQuery.getOperationName()); + internalSet(attributes, DB_QUERY_SUMMARY, multiQuery.getQuerySummary()); + internalSet(attributes, DB_COLLECTION_NAME, multiQuery.getCollectionName()); } } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java index 557ded4a0ac1..7e49929e610e 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java @@ -11,16 +11,17 @@ @AutoValue public abstract class SqlStatementInfo { + private static final String SQL_CALL = "CALL"; private static final int QUERY_SUMMARY_MAX_LENGTH = 255; public static SqlStatementInfo create( - @Nullable String fullStatement, - @Nullable String operation, - @Nullable String identifier, + @Nullable String queryText, + @Nullable String operationName, + @Nullable String target, @Nullable String querySummary) { String truncatedQuerySummary = truncateQuerySummary(querySummary); return new AutoValue_SqlStatementInfo( - fullStatement, operation, identifier, truncatedQuerySummary); + queryText, operationName, target, truncatedQuerySummary); } /** @@ -42,13 +43,64 @@ private static String truncateQuerySummary(@Nullable String querySummary) { } @Nullable - public abstract String getFullStatement(); + public abstract String getQueryText(); + /** + * @deprecated Use {@link #getQueryText()} instead. + */ + @Deprecated + @Nullable + public String getFullStatement() { + return getQueryText(); + } + + @Nullable + public abstract String getOperationName(); + + /** + * @deprecated Use {@link #getOperationName()} instead. + */ + @Deprecated + @Nullable + public String getOperation() { + return getOperationName(); + } + + /** + * Returns the table/collection name, or null for CALL operations. + * + * @see #getStoredProcedureName() + */ @Nullable - public abstract String getOperation(); + public String getCollectionName() { + return SQL_CALL.equals(getOperationName()) ? null : getTarget(); + } + + /** Returns the stored procedure name for CALL operations, or null for other operations. */ + @Nullable + public String getStoredProcedureName() { + return SQL_CALL.equals(getOperationName()) ? getTarget() : null; + } + + /** + * Returns the main identifier from the SQL statement - either the table/collection name or stored + * procedure name depending on the operation. + * + *

For setting the {@code db.collection.name} attribute, use {@link #getCollectionName()} + * instead which returns null for CALL operations. + * + * @deprecated Use {@link #getCollectionName()} for db.collection.name attribute, or {@link + * #getStoredProcedureName()} for stored procedure name. This method may be used for span + * names where both table and procedure names are needed. + */ + @Deprecated + @Nullable + public String getMainIdentifier() { + return getTarget(); + } @Nullable - public abstract String getMainIdentifier(); + abstract String getTarget(); /** * Returns a low cardinality summary of the database query suitable for use as a span name or diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java index 9552aa866e3a..59f2c33c4d99 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java @@ -358,6 +358,61 @@ void shouldExtractMultiQueryBatchAttributes() { assertThat(endAttributes.build().isEmpty()).isTrue(); } + @Test + void shouldExtractHeterogeneousMultiQueryBatchAttributes() { + // given - batch with different operations (INSERT and DELETE) + Map request = new HashMap<>(); + request.put("db.name", "potatoes"); + request.put( + "db.statements", + Arrays.asList("INSERT INTO potato VALUES(1)", "DELETE FROM potato WHERE id=2")); + request.put(DB_OPERATION_BATCH_SIZE.getKey(), 2L); + + Context context = Context.root(); + + AttributesExtractor, Void> underTest = + SqlClientAttributesExtractor.create(new TestMultiAttributesGetter()); + + // when + AttributesBuilder startAttributes = Attributes.builder(); + underTest.onStart(startAttributes, context, request); + + AttributesBuilder endAttributes = Attributes.builder(); + underTest.onEnd(endAttributes, context, request, null, null); + + // then - operation should be "BATCH" (not "BATCH INSERT" or "BATCH DELETE") + // and query summary should also be "BATCH" since operations differ + if (SemconvStability.emitStableDatabaseSemconv() && SemconvStability.emitOldDatabaseSemconv()) { + assertThat(startAttributes.build()) + .containsOnly( + entry(DbIncubatingAttributes.DB_NAME, "potatoes"), + entry(DbAttributes.DB_NAMESPACE, "potatoes"), + entry( + DbAttributes.DB_QUERY_TEXT, + "INSERT INTO potato VALUES(?); DELETE FROM potato WHERE id=?"), + entry(DbAttributes.DB_OPERATION_NAME, "BATCH"), + entry(DbAttributes.DB_COLLECTION_NAME, "potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "BATCH"), + entry(DB_OPERATION_BATCH_SIZE, 2L)); + } else if (SemconvStability.emitOldDatabaseSemconv()) { + assertThat(startAttributes.build()) + .containsOnly(entry(DbIncubatingAttributes.DB_NAME, "potatoes")); + } else if (SemconvStability.emitStableDatabaseSemconv()) { + assertThat(startAttributes.build()) + .containsOnly( + entry(DbAttributes.DB_NAMESPACE, "potatoes"), + entry( + DbAttributes.DB_QUERY_TEXT, + "INSERT INTO potato VALUES(?); DELETE FROM potato WHERE id=?"), + entry(DbAttributes.DB_OPERATION_NAME, "BATCH"), + entry(DbAttributes.DB_COLLECTION_NAME, "potato"), + entry(DbAttributes.DB_QUERY_SUMMARY, "BATCH"), + entry(DB_OPERATION_BATCH_SIZE, 2L)); + } + + assertThat(endAttributes.build().isEmpty()).isTrue(); + } + @Test void shouldIgnoreBatchSizeOne() { // given diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java index 05343014855b..9a67fb008011 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java @@ -21,7 +21,7 @@ class SqlStatementSanitizerTest { @MethodSource("sqlArgs") void sanitizeSql(String original, String expected) { SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(original); - assertThat(result.getFullStatement()).isEqualTo(expected); + assertThat(result.getQueryText()).isEqualTo(expected); } @ParameterizedTest @@ -29,7 +29,7 @@ void sanitizeSql(String original, String expected) { void normalizeCouchbase(String original, String expected) { SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(original, SqlDialect.COUCHBASE); - assertThat(result.getFullStatement()).isEqualTo(expected); + assertThat(result.getQueryText()).isEqualTo(expected); } @ParameterizedTest @@ -37,9 +37,9 @@ void normalizeCouchbase(String original, String expected) { void simplifySql(String original, Function expectedFunction) { SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(original); SqlStatementInfo expected = expectedFunction.apply(original); - assertThat(result.getFullStatement()).isEqualTo(expected.getFullStatement()); - assertThat(result.getOperation()).isEqualTo(expected.getOperation()); - assertThat(result.getMainIdentifier()).isEqualToIgnoringCase(expected.getMainIdentifier()); + assertThat(result.getQueryText()).isEqualTo(expected.getQueryText()); + assertThat(result.getOperationName()).isEqualTo(expected.getOperationName()); + assertThat(result.getCollectionName()).isEqualToIgnoringCase(expected.getCollectionName()); } @Test @@ -54,9 +54,9 @@ void veryLongSelectStatementsAreOk() { SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(query); - assertThat(result.getFullStatement()).isEqualTo(sanitizedQuery); - assertThat(result.getOperation()).isEqualTo("SELECT"); - assertThat(result.getMainIdentifier()).isEqualTo("table"); + assertThat(result.getQueryText()).isEqualTo(sanitizedQuery); + assertThat(result.getOperationName()).isEqualTo("SELECT"); + assertThat(result.getCollectionName()).isEqualTo("table"); assertThat(result.getQuerySummary()).isEqualTo("SELECT table"); } @@ -66,9 +66,9 @@ void checkDdlOperationStatementsAreOk( String actual, Function expectFunc) { SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(actual); SqlStatementInfo expected = expectFunc.apply(actual); - assertThat(result.getFullStatement()).isEqualTo(expected.getFullStatement()); - assertThat(result.getOperation()).isEqualTo(expected.getOperation()); - assertThat(result.getMainIdentifier()).isEqualTo(expected.getMainIdentifier()); + assertThat(result.getQueryText()).isEqualTo(expected.getQueryText()); + assertThat(result.getOperationName()).isEqualTo(expected.getOperationName()); + assertThat(result.getCollectionName()).isEqualTo(expected.getCollectionName()); } @Test @@ -88,7 +88,7 @@ void veryLongNumbersAreOk() { s += String.valueOf(i); } SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(s); - assertThat(result.getFullStatement()).isEqualTo("?"); + assertThat(result.getQueryText()).isEqualTo("?"); } @Test @@ -98,7 +98,7 @@ void veryLongNumbersAtEndOfTableAreOk() { s += String.valueOf(i); } SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(s); - assertThat(result.getFullStatement()).isEqualTo(s.substring(0, AutoSqlSanitizer.LIMIT)); + assertThat(result.getQueryText()).isEqualTo(s.substring(0, AutoSqlSanitizer.LIMIT)); } @Test @@ -107,7 +107,7 @@ void test32kTruncation() { for (int i = 0; i < 10000; i++) { s.append("SELECT * FROM TABLE WHERE FIELD = 1234 AND "); } - String sanitized = SqlStatementSanitizer.create(true).sanitize(s.toString()).getFullStatement(); + String sanitized = SqlStatementSanitizer.create(true).sanitize(s.toString()).getQueryText(); assertThat(sanitized.length()).isLessThanOrEqualTo(AutoSqlSanitizer.LIMIT); assertThat(sanitized).doesNotContain("1234"); } @@ -132,7 +132,7 @@ public void longInStatementDoesntCauseStackOverflow() { } s.append("?)"); - String sanitized = SqlStatementSanitizer.create(true).sanitize(s.toString()).getFullStatement(); + String sanitized = SqlStatementSanitizer.create(true).sanitize(s.toString()).getQueryText(); assertThat(sanitized).isEqualTo("select col from table where col in (?)"); } @@ -142,7 +142,7 @@ public void largeStatementCached() { // test that short statement is cached String shortStatement = "SELECT * FROM TABLE WHERE FIELD = 1234"; String sanitizedShort = - SqlStatementSanitizer.create(true).sanitize(shortStatement).getFullStatement(); + SqlStatementSanitizer.create(true).sanitize(shortStatement).getQueryText(); assertThat(sanitizedShort).doesNotContain("1234"); assertThat(SqlStatementSanitizer.isCached(shortStatement)).isTrue(); @@ -153,7 +153,7 @@ public void largeStatementCached() { } String largeStatement = s.toString(); String sanitizedLarge = - SqlStatementSanitizer.create(true).sanitize(largeStatement).getFullStatement(); + SqlStatementSanitizer.create(true).sanitize(largeStatement).getQueryText(); assertThat(sanitizedLarge).doesNotContain("1234"); assertThat(SqlStatementSanitizer.isCached(largeStatement)).isFalse(); } diff --git a/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java index 7c19f05a006a..9b17641df6cb 100644 --- a/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java @@ -34,7 +34,7 @@ public String getDbOperationName(ClickHouseDbRequest request) { if (request.getSqlStatementInfo() == null) { return null; } - return request.getSqlStatementInfo().getOperation(); + return request.getSqlStatementInfo().getOperationName(); } @SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues diff --git a/instrumentation/couchbase/couchbase-2-common/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizerTest.java b/instrumentation/couchbase/couchbase-2-common/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizerTest.java index 4c0d9e32cf9c..d4c5ede72cc3 100644 --- a/instrumentation/couchbase/couchbase-2-common/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizerTest.java +++ b/instrumentation/couchbase/couchbase-2-common/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizerTest.java @@ -24,7 +24,7 @@ class CouchbaseQuerySanitizerTest { @ParameterizedTest @MethodSource("providesArguments") void testShouldNormalizeStringQuery(Parameter parameter) { - String normalized = CouchbaseQuerySanitizer.sanitize(parameter.query).getFullStatement(); + String normalized = CouchbaseQuerySanitizer.sanitize(parameter.query).getQueryText(); assertThat(normalized).isNotNull(); // the analytics query ends up with trailing ';' in earlier couchbase version, but no trailing // ';' in later couchbase version diff --git a/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseRequestInfo.java b/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseRequestInfo.java index b2f336b2b3fd..2d9bf52f4bc7 100644 --- a/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseRequestInfo.java +++ b/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseRequestInfo.java @@ -47,7 +47,7 @@ public static CouchbaseRequestInfo create(@Nullable String bucket, Object query) SqlStatementInfo statement = CouchbaseQuerySanitizer.sanitize(query); return new AutoValue_CouchbaseRequestInfo( - bucket, statement.getFullStatement(), statement.getOperation(), false); + bucket, statement.getQueryText(), statement.getOperationName(), false); } private static String computeOperation(Class declaringClass, String methodName) { diff --git a/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeDbAttributesGetter.java b/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeDbAttributesGetter.java index 6ede8c66ff5c..700eee818cbf 100644 --- a/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeDbAttributesGetter.java +++ b/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeDbAttributesGetter.java @@ -32,7 +32,7 @@ public String getDbNamespace(GeodeRequest request) { @Nullable public String getDbQueryText(GeodeRequest request) { // sanitized statement is cached - return sanitizer.sanitize(request.getQuery()).getFullStatement(); + return sanitizer.sanitize(request.getQuery()).getQueryText(); } @Override diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java index e978128b4b22..eb59ba89e1b4 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java @@ -7,7 +7,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; -import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getOperationNameForQuery; +import static io.opentelemetry.javaagent.instrumentation.hibernate.SpanNameUtil.getSpanNameForQuery; import static io.opentelemetry.javaagent.instrumentation.hibernate.v3_3.Hibernate3Singletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -61,7 +61,7 @@ public static HibernateOperationScope startMethod(@Advice.This Query query) { Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation(getOperationNameForQuery(query.getQueryString()), sessionInfo); + new HibernateOperation(getSpanNameForQuery(query.getQueryString()), sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java index b01cd3b5bc7a..2c45db48b429 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java @@ -7,8 +7,8 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; -import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getEntityName; -import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getSessionMethodOperationName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.SpanNameUtil.getEntityName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.SpanNameUtil.getSessionMethodOperationName; import static io.opentelemetry.javaagent.instrumentation.hibernate.v3_3.Hibernate3Singletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.isMethod; diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java index 635634ea73fc..5d800f48940e 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java @@ -7,7 +7,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; -import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getOperationNameForQuery; +import static io.opentelemetry.javaagent.instrumentation.hibernate.SpanNameUtil.getSpanNameForQuery; import static io.opentelemetry.javaagent.instrumentation.hibernate.v4_0.Hibernate4Singletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -61,7 +61,7 @@ public static HibernateOperationScope startMethod(@Advice.This Query query) { Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation(getOperationNameForQuery(query.getQueryString()), sessionInfo); + new HibernateOperation(getSpanNameForQuery(query.getQueryString()), sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java index 9457fbfbee51..9554a93fedd1 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java @@ -7,8 +7,8 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; -import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getEntityName; -import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getSessionMethodOperationName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.SpanNameUtil.getEntityName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.SpanNameUtil.getSessionMethodOperationName; import static io.opentelemetry.javaagent.instrumentation.hibernate.v4_0.Hibernate4Singletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.isMethod; diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java index fa8dc951228e..9aa4e3e98641 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java @@ -7,7 +7,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; -import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getOperationNameForQuery; +import static io.opentelemetry.javaagent.instrumentation.hibernate.SpanNameUtil.getSpanNameForQuery; import static io.opentelemetry.javaagent.instrumentation.hibernate.v6_0.Hibernate6Singletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -88,7 +88,7 @@ public static HibernateOperationScope startMethod(@Advice.This CommonQueryContra Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation(getOperationNameForQuery(queryString), sessionInfo); + new HibernateOperation(getSpanNameForQuery(queryString), sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java index e6907d71bfab..fa483d682848 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java @@ -7,8 +7,8 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; -import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getEntityName; -import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getSessionMethodOperationName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.SpanNameUtil.getEntityName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.SpanNameUtil.getSessionMethodOperationName; import static io.opentelemetry.javaagent.instrumentation.hibernate.v6_0.Hibernate6Singletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.isMethod; diff --git a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/OperationNameUtil.java b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SpanNameUtil.java similarity index 77% rename from instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/OperationNameUtil.java rename to instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SpanNameUtil.java index 60783d0822d3..61098c1250c9 100644 --- a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/OperationNameUtil.java +++ b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SpanNameUtil.java @@ -10,23 +10,27 @@ import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; import java.util.function.Function; -public final class OperationNameUtil { +public final class SpanNameUtil { private static final SqlStatementSanitizer sanitizer = SqlStatementSanitizer.create(AgentCommonConfig.get().isStatementSanitizationEnabled()); - public static String getOperationNameForQuery(String query) { - // set operation to default value that is used when sql sanitizer fails to extract + public static String getSpanNameForQuery(String query) { + // set span name to default value that is used when sql sanitizer fails to extract // operation name - String operation = "Hibernate Query"; + String spanName = "Hibernate Query"; SqlStatementInfo info = sanitizer.sanitize(query); - if (info.getOperation() != null) { - operation = info.getOperation(); - if (info.getMainIdentifier() != null) { - operation += " " + info.getMainIdentifier(); + if (info.getOperationName() != null) { + spanName = info.getOperationName(); + String identifier = info.getCollectionName(); + if (identifier == null) { + identifier = info.getStoredProcedureName(); + } + if (identifier != null) { + spanName += " " + identifier; } } - return operation; + return spanName; } public static String getSessionMethodOperationName(String methodName) { @@ -58,5 +62,5 @@ public static String getEntityName( return entityName; } - private OperationNameUtil() {} + private SpanNameUtil() {} } diff --git a/instrumentation/influxdb-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbAttributesGetter.java b/instrumentation/influxdb-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbAttributesGetter.java index 26526f5c064d..ed70b965ed77 100644 --- a/instrumentation/influxdb-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbAttributesGetter.java +++ b/instrumentation/influxdb-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbAttributesGetter.java @@ -13,7 +13,7 @@ final class InfluxDbAttributesGetter implements DbClientAttributesGetter assertThat(statement.executeBatch()).isEqualTo(new int[] {1, 1})); + + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName( + emitStableDatabaseSemconv() + ? "BATCH " + dbNameLower + "." + tableName + : "jdbcunittest") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName(system)), + equalTo(maybeStable(DB_NAME), dbNameLower), + equalTo(DB_USER, emitStableDatabaseSemconv() ? null : username), + equalTo( + DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), + equalTo( + maybeStable(DB_STATEMENT), + emitStableDatabaseSemconv() + ? "INSERT INTO " + + tableName + + " VALUES(?); DELETE FROM " + + tableName + + " WHERE id=?" + : null), + // Operation is "BATCH" because operations differ (INSERT vs DELETE) + equalTo( + maybeStable(DB_OPERATION), + emitStableDatabaseSemconv() ? "BATCH" : null), + // Table is set because both statements target the same table + equalTo( + maybeStable(DB_SQL_TABLE), + emitStableDatabaseSemconv() ? tableName : null), + equalTo( + DB_OPERATION_BATCH_SIZE, + emitStableDatabaseSemconv() ? 2L : null), + // Query summary is "BATCH" because operations differ + equalTo( + DB_QUERY_SUMMARY, + emitStableDatabaseSemconv() ? "BATCH" : null)))); } @ParameterizedTest From 0054505f9de992b1f59e86586cda83c56e721ba8 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 12:11:16 -0800 Subject: [PATCH 13/26] up --- .../api/incubator/semconv/db/MultiQuery.java | 18 +++++++++++++++++- .../incubator/semconv/db/SqlStatementInfo.java | 4 ++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java index 833cb8ed7418..f11c0a6d93c1 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java @@ -13,16 +13,19 @@ class MultiQuery { @Nullable private final String collectionName; + @Nullable private final String storedProcedureName; private final String operationName; private final String querySummary; private final Set queryTexts; private MultiQuery( @Nullable String collectionName, + @Nullable String storedProcedureName, String operationName, String querySummary, Set queryTexts) { this.collectionName = collectionName; + this.storedProcedureName = storedProcedureName; this.operationName = operationName; this.querySummary = querySummary; this.queryTexts = queryTexts; @@ -31,12 +34,14 @@ private MultiQuery( static MultiQuery analyze( Collection rawQueryTexts, boolean statementSanitizationEnabled) { UniqueValue uniqueCollectionName = new UniqueValue(); + UniqueValue uniqueStoredProcedureName = new UniqueValue(); UniqueValue uniqueOperationName = new UniqueValue(); UniqueValue uniqueQuerySummary = new UniqueValue(); Set uniqueQueryTexts = new LinkedHashSet<>(); for (String rawQueryText : rawQueryTexts) { SqlStatementInfo sanitizedStatement = SqlStatementSanitizerUtil.sanitize(rawQueryText); uniqueCollectionName.set(sanitizedStatement.getCollectionName()); + uniqueStoredProcedureName.set(sanitizedStatement.getStoredProcedureName()); uniqueOperationName.set(sanitizedStatement.getOperationName()); uniqueQuerySummary.set(sanitizedStatement.getQuerySummary()); uniqueQueryTexts.add( @@ -47,10 +52,16 @@ static MultiQuery analyze( String querySummary = uniqueQuerySummary.getValue(); String collectionName = uniqueCollectionName.getValue(); + String storedProcedureName = uniqueStoredProcedureName.getValue(); String batchOperationName = operationName != null ? "BATCH " + operationName : "BATCH"; String batchQuerySummary = querySummary != null ? "BATCH " + querySummary : batchOperationName; - return new MultiQuery(collectionName, batchOperationName, batchQuerySummary, uniqueQueryTexts); + return new MultiQuery( + collectionName, + storedProcedureName, + batchOperationName, + batchQuerySummary, + uniqueQueryTexts); } @Nullable @@ -58,6 +69,11 @@ public String getCollectionName() { return collectionName; } + @Nullable + public String getStoredProcedureName() { + return storedProcedureName; + } + public String getOperationName() { return operationName; } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java index 7e49929e610e..ed00853384d8 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java @@ -17,11 +17,11 @@ public abstract class SqlStatementInfo { public static SqlStatementInfo create( @Nullable String queryText, @Nullable String operationName, + // collectionName and storedProcedureName are compressed into this one field for efficiency @Nullable String target, @Nullable String querySummary) { String truncatedQuerySummary = truncateQuerySummary(querySummary); - return new AutoValue_SqlStatementInfo( - queryText, operationName, target, truncatedQuerySummary); + return new AutoValue_SqlStatementInfo(queryText, operationName, target, truncatedQuerySummary); } /** From 75ac5292d8032d96779f46cb27cc02eff04cb4fe Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 12:38:01 -0800 Subject: [PATCH 14/26] Replace deprecated getFullStatement() with getQueryText() --- .../apachecamel/decorators/DbSpanDecorator.java | 6 +++--- .../clickhouse/common/ClickHouseAttributesGetter.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/instrumentation/camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java b/instrumentation/camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java index 5146ef2d30a8..22989d2df10b 100644 --- a/instrumentation/camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java +++ b/instrumentation/camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java @@ -72,19 +72,19 @@ String getStatement(Exchange exchange, Endpoint endpoint) { case "cql": Object cqlObj = exchange.getIn().getHeader("CamelCqlQuery"); if (cqlObj != null) { - return sanitizer.sanitize(cqlObj.toString()).getFullStatement(); + return sanitizer.sanitize(cqlObj.toString()).getQueryText(); } return null; case "jdbc": Object body = exchange.getIn().getBody(); if (body instanceof String) { - return sanitizer.sanitize((String) body).getFullStatement(); + return sanitizer.sanitize((String) body).getQueryText(); } return null; case "sql": Object sqlquery = exchange.getIn().getHeader("CamelSqlQuery"); if (sqlquery instanceof String) { - return sanitizer.sanitize((String) sqlquery).getFullStatement(); + return sanitizer.sanitize((String) sqlquery).getQueryText(); } return null; default: diff --git a/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java index 9b17641df6cb..d1f3093e01f1 100644 --- a/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java @@ -25,7 +25,7 @@ public String getDbQueryText(ClickHouseDbRequest request) { if (request.getSqlStatementInfo() == null) { return null; } - return request.getSqlStatementInfo().getFullStatement(); + return request.getSqlStatementInfo().getQueryText(); } @Nullable From 35dcd28eb51e36c3e841eb956c6b403b706c55a6 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 13:23:25 -0800 Subject: [PATCH 15/26] Fix JdbcTelemetryTest.batchStatement() expected db.query.summary value --- .../instrumentation/jdbc/datasource/JdbcTelemetryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java b/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java index da43e76f0b25..a254fbe6b639 100644 --- a/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java +++ b/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java @@ -300,7 +300,7 @@ void batchStatement() throws SQLException { equalTo( DB_QUERY_SUMMARY, SemconvStability.emitStableDatabaseSemconv() - ? "INSERT test" + ? "BATCH INSERT test" : null)))); } } From bc6f29b0c2a7a1f6f86838b0270dd7bd3e91a232 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 14:54:51 -0800 Subject: [PATCH 16/26] up --- .../AbstractJdbcInstrumentationTest.java | 72 ------------------- 1 file changed, 72 deletions(-) diff --git a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java index 774bdf74b3ae..82e0481f1f3e 100644 --- a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java +++ b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java @@ -1684,83 +1684,11 @@ DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), equalTo( DB_OPERATION_BATCH_SIZE, emitStableDatabaseSemconv() ? 2L : null), - // Different tables in batch means summary is just operation equalTo( DB_QUERY_SUMMARY, emitStableDatabaseSemconv() ? "BATCH INSERT" : null)))); } - @ParameterizedTest - @MethodSource("batchStream") - void testHeterogeneousBatch(String system, Connection conn, String username, String url) - throws SQLException { - Connection connection = wrap(conn); - String tableName = "hetero_batch_test"; - Statement createTable = connection.createStatement(); - createTable.execute("CREATE TABLE " + tableName + " (id INTEGER not NULL, PRIMARY KEY ( id ))"); - cleanup.deferCleanup(createTable); - - // Insert a row first so we can delete it - Statement insertFirst = connection.createStatement(); - insertFirst.execute("INSERT INTO " + tableName + " VALUES(99)"); - cleanup.deferCleanup(insertFirst); - - testing().waitForTraces(2); - testing().clearData(); - - Statement statement = connection.createStatement(); - cleanup.deferCleanup(statement); - // Batch with different operations: INSERT and DELETE - statement.addBatch("INSERT INTO " + tableName + " VALUES(1)"); - statement.addBatch("DELETE FROM " + tableName + " WHERE id=99"); - testing() - .runWithSpan( - "parent", () -> assertThat(statement.executeBatch()).isEqualTo(new int[] {1, 1})); - - testing() - .waitAndAssertTraces( - trace -> - trace.hasSpansSatisfyingExactly( - span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), - span -> - span.hasName( - emitStableDatabaseSemconv() - ? "BATCH " + dbNameLower + "." + tableName - : "jdbcunittest") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName(system)), - equalTo(maybeStable(DB_NAME), dbNameLower), - equalTo(DB_USER, emitStableDatabaseSemconv() ? null : username), - equalTo( - DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : url), - equalTo( - maybeStable(DB_STATEMENT), - emitStableDatabaseSemconv() - ? "INSERT INTO " - + tableName - + " VALUES(?); DELETE FROM " - + tableName - + " WHERE id=?" - : null), - // Operation is "BATCH" because operations differ (INSERT vs DELETE) - equalTo( - maybeStable(DB_OPERATION), - emitStableDatabaseSemconv() ? "BATCH" : null), - // Table is set because both statements target the same table - equalTo( - maybeStable(DB_SQL_TABLE), - emitStableDatabaseSemconv() ? tableName : null), - equalTo( - DB_OPERATION_BATCH_SIZE, - emitStableDatabaseSemconv() ? 2L : null), - // Query summary is "BATCH" because operations differ - equalTo( - DB_QUERY_SUMMARY, - emitStableDatabaseSemconv() ? "BATCH" : null)))); - } - @ParameterizedTest @MethodSource("batchStream") void testSingleItemBatch(String system, Connection conn, String username, String url) From a79b44cce17589f6ebe0098aca667a4326872f81 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sat, 6 Dec 2025 15:57:44 -0800 Subject: [PATCH 17/26] up --- .../semconv/db/DbClientSpanNameExtractor.java | 12 ++++++------ .../db/DbClientSpanNameExtractorTest.java | 6 ------ .../api/internal/InstrumenterContextTest.java | 3 ++- .../hibernate/v3_3/AbstractHibernateTest.java | 3 +-- .../jdbc/datasource/JdbcTelemetryTest.java | 16 ++++++++++------ 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java index 446e86f023b7..17aa4b24f5da 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java @@ -49,25 +49,25 @@ protected String computeSpanName( @Nullable String collectionName, @Nullable String storedProcedureName) { // Use whichever identifier is available (they're mutually exclusive) - String identifier = collectionName != null ? collectionName : storedProcedureName; + String mainIdentifier = collectionName != null ? collectionName : storedProcedureName; if (operation == null) { return dbName == null ? DEFAULT_SPAN_NAME : dbName; } StringBuilder name = new StringBuilder(operation); - if (dbName != null || identifier != null) { + if (dbName != null || mainIdentifier != null) { name.append(' '); } // skip db name if identifier already has a db name prefixed to it - if (dbName != null && (identifier == null || identifier.indexOf('.') == -1)) { + if (dbName != null && (mainIdentifier == null || mainIdentifier.indexOf('.') == -1)) { name.append(dbName); - if (identifier != null) { + if (mainIdentifier != null) { name.append('.'); } } - if (identifier != null) { - name.append(identifier); + if (mainIdentifier != null) { + name.append(mainIdentifier); } return name.toString(); } diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java index 0d307e65afaa..8c87d70fcfcf 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java @@ -37,8 +37,6 @@ void shouldExtractFullSpanName() { String spanName = underTest.extract(dbRequest); // then - // For stable semconv, span name is the query summary (without namespace) - // For old semconv, span name includes the namespace assertEquals( SemconvStability.emitStableDatabaseSemconv() ? "SELECT table" : "SELECT database.table", spanName); @@ -157,8 +155,6 @@ void shouldExtractFullSpanNameForBatch() { String spanName = underTest.extract(dbRequest); // then - // For stable semconv, multi-query batch uses query summary - // For old semconv, only namespace is used assertEquals( SemconvStability.emitStableDatabaseSemconv() ? "BATCH INSERT table" : "database", spanName); } @@ -181,8 +177,6 @@ void shouldExtractFullSpanNameForSingleQueryBatch() { String spanName = underTest.extract(dbRequest); // then - // For stable semconv, single-query batch uses query summary with BATCH prefix - // For old semconv, it uses the full span name format with namespace assertEquals( SemconvStability.emitStableDatabaseSemconv() ? "BATCH INSERT table" diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java index 5c1e692ed167..874b884780e1 100644 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java @@ -67,7 +67,8 @@ public Collection getRawQueryTexts(Object request) { // replace cached sanitization result to verify it is used sanitizedMap.put( testQuery, - SqlStatementInfo.create("SELECT name2 FROM test2 WHERE id = ?", "SELECT", "test2", null)); + SqlStatementInfo.create( + "SELECT name2 FROM test2 WHERE id = ?", "SELECT", "test2", "SELECT test2")); { AttributesBuilder builder = Attributes.builder(); attributesExtractor.onStart(builder, Context.root(), null); diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java index 3ad247f1900f..1932f68af5bd 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java @@ -94,7 +94,6 @@ static void assertClientSpan(SpanDataAssert span, SpanData parent) { @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation static void assertClientSpan(SpanDataAssert span, SpanData parent, String verb) { String spanName = emitStableDatabaseSemconv() ? verb + " Value" : verb + " db1.Value"; - String querySummary = emitStableDatabaseSemconv() ? verb + " Value" : null; span.hasName(spanName) .hasKind(SpanKind.CLIENT) .hasParent(parent) @@ -108,7 +107,7 @@ static void assertClientSpan(SpanDataAssert span, SpanData parent, String verb) stringAssert -> stringAssert.startsWith(verb.toLowerCase(Locale.ROOT))), equalTo(maybeStable(DB_OPERATION), verb), equalTo(maybeStable(DB_SQL_TABLE), "Value"), - equalTo(DB_QUERY_SUMMARY, querySummary)); + equalTo(DB_QUERY_SUMMARY, emitStableDatabaseSemconv() ? spanName : null)); } static SpanDataAssert assertSessionSpan(SpanDataAssert span, SpanData parent, String spanName) { diff --git a/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java b/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java index a254fbe6b639..c23a20f6c1bd 100644 --- a/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java +++ b/instrumentation/jdbc/library/src/test/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryTest.java @@ -57,18 +57,20 @@ void buildWithDefaults() throws SQLException { testing.runWithSpan( "parent", () -> dataSource.getConnection().createStatement().execute("SELECT 1;")); - String spanName = SemconvStability.emitStableDatabaseSemconv() ? "SELECT" : "SELECT dbname"; testing.waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent"), span -> - span.hasName(spanName) + span.hasName( + SemconvStability.emitStableDatabaseSemconv() + ? "SELECT" + : "SELECT dbname") .hasAttributesSatisfying( equalTo(maybeStable(DB_STATEMENT), "SELECT ?;"), equalTo( DB_QUERY_SUMMARY, - SemconvStability.emitStableDatabaseSemconv() ? spanName : null)))); + SemconvStability.emitStableDatabaseSemconv() ? "SELECT" : null)))); assertDurationMetric( testing, @@ -227,18 +229,20 @@ void buildWithSanitizationDisabled() throws SQLException { testing.runWithSpan( "parent", () -> dataSource.getConnection().createStatement().execute("SELECT 1;")); - String spanName = SemconvStability.emitStableDatabaseSemconv() ? "SELECT" : "SELECT dbname"; testing.waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent"), span -> - span.hasName(spanName) + span.hasName( + SemconvStability.emitStableDatabaseSemconv() + ? "SELECT" + : "SELECT dbname") .hasAttributesSatisfying( equalTo(maybeStable(DB_STATEMENT), "SELECT 1;"), equalTo( DB_QUERY_SUMMARY, - SemconvStability.emitStableDatabaseSemconv() ? spanName : null)))); + SemconvStability.emitStableDatabaseSemconv() ? "SELECT" : null)))); } @Test From 8e6e1f9fa7c976016fdfff880af2ab07d8ad91db Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sun, 7 Dec 2025 08:17:57 -0800 Subject: [PATCH 18/26] db.query.summary multiple targets --- .../src/main/jflex/SqlSanitizer.jflex | 51 +++++++++++++++---- .../semconv/db/SqlStatementSanitizerTest.java | 9 ++-- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex b/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex index 000260cc4ea4..09d5248fc2d6 100644 --- a/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex +++ b/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex @@ -79,8 +79,7 @@ WHITESPACE = [ \t\r\n]+ } private void appendOperationToSummary(String operation) { - // Avoid duplicate entries when subqueries have same operation - if (operation != null && !operation.equals(lastQuerySummaryItem)) { + if (operation != null) { if (querySummaryBuilder.length() > 0) { querySummaryBuilder.append(' '); } @@ -139,6 +138,10 @@ WHITESPACE = [ \t\r\n]+ private boolean insideComment = false; private Operation operation = NoOp.INSTANCE; private boolean extractionDone = false; + // Tracks how many SELECT keywords we've seen (to track targets for subqueries) + private int selectCount = 0; + // Tracks whether we're expecting the next identifier after FROM for a subsequent SELECT's query summary + private boolean expectingTargetForSubsequentSelect = false; private SqlDialect dialect; private void setOperation(Operation operation) { @@ -147,6 +150,24 @@ WHITESPACE = [ \t\r\n]+ } } + // Called when we see FROM and have a subsequent SELECT pending + private void handleFromForSubsequentSelect() { + // Capture target for subsequent SELECTs: + // - selectCount > 1: nested SELECT (e.g., SELECT FROM (SELECT FROM orders)) + // - selectCount >= 1 && extractionDone: SELECT after main operation done (e.g., INSERT INTO t SELECT FROM orders) + if (selectCount > 1 || (selectCount >= 1 && extractionDone)) { + expectingTargetForSubsequentSelect = true; + } + } + + // Called when we capture a target after FROM for subsequent SELECT's query summary + private void handleTargetForSubsequentSelect(String target) { + if (expectingTargetForSubsequentSelect && target != null) { + appendTargetToSummary(target); + expectingTargetForSubsequentSelect = false; + } + } + private static abstract class Operation { String mainIdentifier = null; @@ -412,6 +433,7 @@ WHITESPACE = [ \t\r\n]+ if (!insideComment) { setOperation(new Select()); appendOperationToSummary("SELECT"); + selectCount++; } appendCurrentFragment(); if (isOverLimit()) return YYEOF; @@ -481,13 +503,17 @@ WHITESPACE = [ \t\r\n]+ if (isOverLimit()) return YYEOF; } "FROM" { - if (!insideComment && !extractionDone) { - if (operation == NoOp.INSTANCE) { - // hql/jpql queries may skip SELECT and start with FROM clause - // treat such queries as SELECT queries - setOperation(new Select()); + if (!insideComment) { + if (!extractionDone) { + if (operation == NoOp.INSTANCE) { + // hql/jpql queries may skip SELECT and start with FROM clause + // treat such queries as SELECT queries + setOperation(new Select()); + } + extractionDone = operation.handleFrom(); } - extractionDone = operation.handleFrom(); + // For subsequent SELECTs (nested or after INSERT), prepare to capture target + handleFromForSubsequentSelect(); } appendCurrentFragment(); if (isOverLimit()) return YYEOF; @@ -537,8 +563,13 @@ WHITESPACE = [ \t\r\n]+ if (isOverLimit()) return YYEOF; } {IDENTIFIER} { - if (!insideComment && !extractionDone) { - extractionDone = operation.handleIdentifier(); + if (!insideComment) { + if (!extractionDone) { + extractionDone = operation.handleIdentifier(); + } else { + // For subsequent SELECTs (e.g., SELECT in INSERT...SELECT), capture target for summary + handleTargetForSubsequentSelect(readIdentifierName()); + } } appendCurrentFragment(); if (isOverLimit()) return YYEOF; diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java index 9a67fb008011..a109e27acc62 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java @@ -188,19 +188,18 @@ private static Stream querySummaryArgs() { // Basic SELECT Arguments.of("SELECT * FROM wuser_table", "SELECT wuser_table"), Arguments.of("SELECT * FROM wuser_table WHERE username = ?", "SELECT wuser_table"), - // INSERT with SELECT subquery - INSERT target + SELECT operation (SELECT target not tracked - // after extraction done) + // INSERT with SELECT subquery - INSERT target + SELECT operation + SELECT target Arguments.of( "INSERT INTO shipping_details (order_id, address) SELECT order_id, address FROM orders WHERE order_id = ?", - "INSERT shipping_details SELECT"), + "INSERT shipping_details SELECT orders"), // SELECT with multiple tables (implicit join) - only first table tracked since extraction // stops on comma Arguments.of( "SELECT * FROM songs, artists WHERE songs.artist_id == artists.id", "SELECT songs"), - // SELECT with subquery in FROM - only SELECT operation, no table from subquery + // SELECT with subquery in FROM - outer SELECT + inner SELECT + inner table Arguments.of( "SELECT order_date FROM (SELECT * FROM orders o JOIN customers c ON o.customer_id = c.customer_id)", - "SELECT"), + "SELECT SELECT orders"), // SELECT with JOIN - first table tracked, extraction stops on JOIN Arguments.of("SELECT * FROM table1 JOIN table2 ON table1.id = table2.id", "SELECT table1"), // DELETE From cae5b563d0e6f002824fd1080723a316d725a9eb Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sun, 7 Dec 2025 08:35:51 -0800 Subject: [PATCH 19/26] review --- .../api/incubator/semconv/db/SqlStatementInfo.java | 3 +-- .../semconv/db/SqlClientAttributesExtractorTest.java | 5 ++--- .../hibernate/v3_3/AbstractHibernateTest.java | 5 ++--- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java index ed00853384d8..1298b86a761a 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java @@ -90,8 +90,7 @@ public String getStoredProcedureName() { * instead which returns null for CALL operations. * * @deprecated Use {@link #getCollectionName()} for db.collection.name attribute, or {@link - * #getStoredProcedureName()} for stored procedure name. This method may be used for span - * names where both table and procedure names are needed. + * #getStoredProcedureName()} for stored procedure name. */ @Deprecated @Nullable diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java index 59f2c33c4d99..42d549759713 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorTest.java @@ -360,7 +360,7 @@ void shouldExtractMultiQueryBatchAttributes() { @Test void shouldExtractHeterogeneousMultiQueryBatchAttributes() { - // given - batch with different operations (INSERT and DELETE) + // given Map request = new HashMap<>(); request.put("db.name", "potatoes"); request.put( @@ -380,8 +380,7 @@ void shouldExtractHeterogeneousMultiQueryBatchAttributes() { AttributesBuilder endAttributes = Attributes.builder(); underTest.onEnd(endAttributes, context, request, null, null); - // then - operation should be "BATCH" (not "BATCH INSERT" or "BATCH DELETE") - // and query summary should also be "BATCH" since operations differ + // then if (SemconvStability.emitStableDatabaseSemconv() && SemconvStability.emitOldDatabaseSemconv()) { assertThat(startAttributes.build()) .containsOnly( diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java index 1932f68af5bd..9417e94d863d 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java @@ -93,8 +93,7 @@ static void assertClientSpan(SpanDataAssert span, SpanData parent) { @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation static void assertClientSpan(SpanDataAssert span, SpanData parent, String verb) { - String spanName = emitStableDatabaseSemconv() ? verb + " Value" : verb + " db1.Value"; - span.hasName(spanName) + span.hasName(emitStableDatabaseSemconv() ? verb + " Value" : verb + " db1.Value") .hasKind(SpanKind.CLIENT) .hasParent(parent) .hasAttributesSatisfyingExactly( @@ -107,7 +106,7 @@ static void assertClientSpan(SpanDataAssert span, SpanData parent, String verb) stringAssert -> stringAssert.startsWith(verb.toLowerCase(Locale.ROOT))), equalTo(maybeStable(DB_OPERATION), verb), equalTo(maybeStable(DB_SQL_TABLE), "Value"), - equalTo(DB_QUERY_SUMMARY, emitStableDatabaseSemconv() ? spanName : null)); + equalTo(DB_QUERY_SUMMARY, emitStableDatabaseSemconv() ? verb + " Value" : null)); } static SpanDataAssert assertSessionSpan(SpanDataAssert span, SpanData parent, String spanName) { From 2b193cd9c8c80682c2a62c64c97f29c0d5dc6008 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sun, 7 Dec 2025 11:18:05 -0800 Subject: [PATCH 20/26] up --- .../semconv/db/SqlStatementSanitizerTest.java | 243 +++++++++--------- 1 file changed, 126 insertions(+), 117 deletions(-) diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java index a109e27acc62..d7da9d9b2f6e 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java @@ -40,6 +40,7 @@ void simplifySql(String original, Function expectedFun assertThat(result.getQueryText()).isEqualTo(expected.getQueryText()); assertThat(result.getOperationName()).isEqualTo(expected.getOperationName()); assertThat(result.getCollectionName()).isEqualToIgnoringCase(expected.getCollectionName()); + assertThat(result.getQuerySummary()).isEqualTo(expected.getQuerySummary()); } @Test @@ -69,6 +70,7 @@ void checkDdlOperationStatementsAreOk( assertThat(result.getQueryText()).isEqualTo(expected.getQueryText()); assertThat(result.getOperationName()).isEqualTo(expected.getOperationName()); assertThat(result.getCollectionName()).isEqualTo(expected.getCollectionName()); + assertThat(result.getQuerySummary()).isEqualTo(expected.getQuerySummary()); } @Test @@ -158,13 +160,6 @@ public void largeStatementCached() { assertThat(SqlStatementSanitizer.isCached(largeStatement)).isFalse(); } - @ParameterizedTest - @MethodSource("querySummaryArgs") - void querySummary(String sql, String expectedSummary) { - SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(sql); - assertThat(result.getQuerySummary()).isEqualTo(expectedSummary); - } - @Test void querySummaryIsTruncated() { // Build a query with many tables to exceed 255 character limit @@ -183,47 +178,6 @@ void querySummaryIsTruncated() { assertThat(result).isEqualTo("SELECT very_long_table_name_0"); } - private static Stream querySummaryArgs() { - return Stream.of( - // Basic SELECT - Arguments.of("SELECT * FROM wuser_table", "SELECT wuser_table"), - Arguments.of("SELECT * FROM wuser_table WHERE username = ?", "SELECT wuser_table"), - // INSERT with SELECT subquery - INSERT target + SELECT operation + SELECT target - Arguments.of( - "INSERT INTO shipping_details (order_id, address) SELECT order_id, address FROM orders WHERE order_id = ?", - "INSERT shipping_details SELECT orders"), - // SELECT with multiple tables (implicit join) - only first table tracked since extraction - // stops on comma - Arguments.of( - "SELECT * FROM songs, artists WHERE songs.artist_id == artists.id", "SELECT songs"), - // SELECT with subquery in FROM - outer SELECT + inner SELECT + inner table - Arguments.of( - "SELECT order_date FROM (SELECT * FROM orders o JOIN customers c ON o.customer_id = c.customer_id)", - "SELECT SELECT orders"), - // SELECT with JOIN - first table tracked, extraction stops on JOIN - Arguments.of("SELECT * FROM table1 JOIN table2 ON table1.id = table2.id", "SELECT table1"), - // DELETE - Arguments.of("DELETE FROM users WHERE id = ?", "DELETE users"), - // UPDATE - Arguments.of("UPDATE users SET name = ? WHERE id = ?", "UPDATE users"), - // CALL stored procedure - Arguments.of("CALL some_stored_procedure", "CALL some_stored_procedure"), - // MERGE - Arguments.of("MERGE INTO target USING source ON target.id = source.id", "MERGE target"), - // CREATE TABLE - Arguments.of("CREATE TABLE users (id INT, name VARCHAR(100))", "CREATE TABLE users"), - // DROP TABLE - Arguments.of("DROP TABLE users", "DROP TABLE users"), - // ALTER TABLE - Arguments.of("ALTER TABLE users ADD COLUMN email VARCHAR(100)", "ALTER TABLE users"), - // CREATE INDEX (no table in summary) - Arguments.of("CREATE INDEX idx_name ON users (name)", "CREATE INDEX"), - // Unknown operation - Arguments.of("and now for something completely different", null), - Arguments.of("", null), - Arguments.of(null, null)); - } - private static Stream sqlArgs() { return Stream.of( Arguments.of("SELECT * FROM TABLE WHERE FIELD=1234", "SELECT * FROM TABLE WHERE FIELD=?"), @@ -334,125 +288,180 @@ private static Stream couchbaseArgs() { "SELECT * FROM TABLE WHERE FIELD = ?")); } - private static Function expect(String operation, String identifier) { - return sql -> SqlStatementInfo.create(sql, operation, identifier, null); + private static Function expect( + String operation, String identifier, String querySummary) { + return sql -> SqlStatementInfo.create(sql, operation, identifier, querySummary); } private static Function expect( - String sql, String operation, String identifier) { - return ignored -> SqlStatementInfo.create(sql, operation, identifier, null); + String sql, String operation, String identifier, String querySummary) { + return ignored -> SqlStatementInfo.create(sql, operation, identifier, querySummary); } private static Stream simplifyArgs() { return Stream.of( // Select - Arguments.of("SELECT x, y, z FROM schema.table", expect("SELECT", "schema.table")), - Arguments.of("SELECT x, y, z FROM `schema table`", expect("SELECT", "schema table")), - Arguments.of("SELECT x, y, z FROM `schema`.`table`", expect("SELECT", "`schema`.`table`")), - Arguments.of("SELECT x, y, z FROM \"schema table\"", expect("SELECT", "schema table")), Arguments.of( - "SELECT x, y, z FROM \"schema\".\"table\"", expect("SELECT", "\"schema\".\"table\"")), + "SELECT x, y, z FROM schema.table", + expect("SELECT", "schema.table", "SELECT schema.table")), + Arguments.of( + "SELECT x, y, z FROM `schema table`", + expect("SELECT", "schema table", "SELECT schema table")), + Arguments.of( + "SELECT x, y, z FROM `schema`.`table`", + expect("SELECT", "`schema`.`table`", "SELECT `schema`.`table`")), + Arguments.of( + "SELECT x, y, z FROM \"schema table\"", + expect("SELECT", "schema table", "SELECT schema table")), + Arguments.of( + "SELECT x, y, z FROM \"schema\".\"table\"", + expect("SELECT", "\"schema\".\"table\"", "SELECT \"schema\".\"table\"")), + Arguments.of( + "WITH subquery as (select a from b) SELECT x, y, z FROM table", + expect("SELECT", null, "SELECT b SELECT")), + Arguments.of( + "SELECT x, y, (select a from b) as z FROM table", + expect("SELECT", null, "SELECT SELECT b")), + Arguments.of( + "select delete, insert into, merge, update from table", + expect("SELECT", "table", "SELECT DELETE INSERT MERGE UPDATE table")), + Arguments.of( + "select col /* from table2 */ from table", expect("SELECT", "table", "SELECT table")), + Arguments.of( + "select col from table join anotherTable", expect("SELECT", null, "SELECT table")), Arguments.of( - "WITH subquery as (select a from b) SELECT x, y, z FROM table", expect("SELECT", null)), - Arguments.of("SELECT x, y, (select a from b) as z FROM table", expect("SELECT", null)), + "select col from (select * from anotherTable)", + expect("SELECT", null, "SELECT SELECT anotherTable")), Arguments.of( - "select delete, insert into, merge, update from table", expect("SELECT", "table")), - Arguments.of("select col /* from table2 */ from table", expect("SELECT", "table")), - Arguments.of("select col from table join anotherTable", expect("SELECT", null)), - Arguments.of("select col from (select * from anotherTable)", expect("SELECT", null)), - Arguments.of("select col from (select * from anotherTable) alias", expect("SELECT", null)), - Arguments.of("select col from table1 union select col from table2", expect("SELECT", null)), + "select col from (select * from anotherTable) alias", + expect("SELECT", null, "SELECT SELECT anotherTable")), + Arguments.of( + "select col from table1 union select col from table2", + expect("SELECT", null, "SELECT table1 SELECT")), Arguments.of( "select col from table where col in (select * from anotherTable)", - expect("SELECT", null)), - Arguments.of("select col from table1, table2", expect("SELECT", null)), - Arguments.of("select col from table1 t1, table2 t2", expect("SELECT", null)), - Arguments.of("select col from table1 as t1, table2 as t2", expect("SELECT", null)), + expect("SELECT", null, "SELECT table SELECT anotherTable")), + Arguments.of("select col from table1, table2", expect("SELECT", null, "SELECT table1")), + Arguments.of( + "select col from table1 t1, table2 t2", expect("SELECT", null, "SELECT table1")), + Arguments.of( + "select col from table1 as t1, table2 as t2", expect("SELECT", null, "SELECT table1")), Arguments.of( "select col from table where col in (1, 2, 3)", - expect("select col from table where col in (?)", "SELECT", "table")), + expect("select col from table where col in (?)", "SELECT", "table", "SELECT table")), Arguments.of( "select 'a' IN(x, 'b') from table where col in (1) and z IN( '3', '4' )", - expect("select ? IN(x, ?) from table where col in (?) and z IN(?)", "SELECT", "table")), - Arguments.of("select col from table order by col, col2", expect("SELECT", "table")), - Arguments.of("select ąś∂ń© from źćļńĶ order by col, col2", expect("SELECT", "źćļńĶ")), - Arguments.of("select 12345678", expect("select ?", "SELECT", null)), - Arguments.of("/* update comment */ select * from table1", expect("SELECT", "table1")), - Arguments.of("select /*((*/abc from table", expect("SELECT", "table")), - Arguments.of("SeLeCT * FrOm TAblE", expect("SELECT", "table")), - Arguments.of("select next value in hibernate_sequence", expect("SELECT", null)), + expect( + "select ? IN(x, ?) from table where col in (?) and z IN(?)", + "SELECT", + "table", + "SELECT table")), + Arguments.of( + "select col from table order by col, col2", expect("SELECT", "table", "SELECT table")), + Arguments.of( + "select ąś∂ń© from źćļńĶ order by col, col2", + expect("SELECT", "źćļńĶ", "SELECT źćļńĶ")), + Arguments.of("select 12345678", expect("select ?", "SELECT", null, "SELECT")), + Arguments.of( + "/* update comment */ select * from table1", + expect("SELECT", "table1", "SELECT table1")), + Arguments.of("select /*((*/abc from table", expect("SELECT", "table", "SELECT table")), + Arguments.of("SeLeCT * FrOm TAblE", expect("SELECT", "table", "SELECT TAblE")), + Arguments.of("select next value in hibernate_sequence", expect("SELECT", null, "SELECT")), // hibernate/jpa - Arguments.of("FROM schema.table", expect("SELECT", "schema.table")), - Arguments.of("/* update comment */ from table1", expect("SELECT", "table1")), + Arguments.of("FROM schema.table", expect("SELECT", "schema.table", "schema.table")), + Arguments.of("/* update comment */ from table1", expect("SELECT", "table1", "table1")), // Insert - Arguments.of(" insert into table where lalala", expect("INSERT", "table")), - Arguments.of("insert insert into table where lalala", expect("INSERT", "table")), - Arguments.of("insert into db.table where lalala", expect("INSERT", "db.table")), - Arguments.of("insert into `db table` where lalala", expect("INSERT", "db table")), - Arguments.of("insert into \"db table\" where lalala", expect("INSERT", "db table")), - Arguments.of("insert without i-n-t-o", expect("INSERT", null)), + Arguments.of(" insert into table where lalala", expect("INSERT", "table", "INSERT table")), + Arguments.of( + "insert insert into table where lalala", + expect("INSERT", "table", "INSERT INSERT table")), + Arguments.of( + "insert into db.table where lalala", expect("INSERT", "db.table", "INSERT db.table")), + Arguments.of( + "insert into `db table` where lalala", expect("INSERT", "db table", "INSERT db table")), + Arguments.of( + "insert into \"db table\" where lalala", + expect("INSERT", "db table", "INSERT db table")), + Arguments.of("insert without i-n-t-o", expect("INSERT", null, "INSERT")), // Delete - Arguments.of("delete from table where something something", expect("DELETE", "table")), Arguments.of( - "delete from `my table` where something something", expect("DELETE", "my table")), + "delete from table where something something", + expect("DELETE", "table", "DELETE table")), + Arguments.of( + "delete from `my table` where something something", + expect("DELETE", "my table", "DELETE my table")), Arguments.of( - "delete from \"my table\" where something something", expect("DELETE", "my table")), + "delete from \"my table\" where something something", + expect("DELETE", "my table", "DELETE my table")), Arguments.of( "delete from foo where x IN (1,2,3)", - expect("delete from foo where x IN (?)", "DELETE", "foo")), - Arguments.of("delete from 12345678", expect("delete from ?", "DELETE", null)), - Arguments.of("delete (((", expect("delete (((", "DELETE", null)), + expect("delete from foo where x IN (?)", "DELETE", "foo", "DELETE foo")), + Arguments.of("delete from 12345678", expect("delete from ?", "DELETE", null, "DELETE")), + Arguments.of("delete (((", expect("delete (((", "DELETE", null, "DELETE")), // Update Arguments.of( - "update table set answer=42", expect("update table set answer=?", "UPDATE", "table")), + "update table set answer=42", + expect("update table set answer=?", "UPDATE", "table", "UPDATE table")), Arguments.of( "update `my table` set answer=42", - expect("update `my table` set answer=?", "UPDATE", "my table")), + expect("update `my table` set answer=?", "UPDATE", "my table", "UPDATE my table")), Arguments.of( "update `my table` set answer=42 where x IN('a', 'b') AND y In ('a', 'b')", expect( - "update `my table` set answer=? where x IN(?) AND y In (?)", "UPDATE", "my table")), + "update `my table` set answer=? where x IN(?) AND y In (?)", + "UPDATE", + "my table", + "UPDATE my table")), Arguments.of( "update \"my table\" set answer=42", - expect("update \"my table\" set answer=?", "UPDATE", "my table")), - Arguments.of("update /*table", expect("UPDATE", null)), + expect("update \"my table\" set answer=?", "UPDATE", "my table", "UPDATE my table")), + Arguments.of("update /*table", expect("UPDATE", null, "UPDATE")), // Call - Arguments.of("call test_proc()", expect("CALL", "test_proc")), - Arguments.of("call test_proc", expect("CALL", "test_proc")), - Arguments.of("call next value in hibernate_sequence", expect("CALL", null)), - Arguments.of("call db.test_proc", expect("CALL", "db.test_proc")), + Arguments.of("call test_proc()", expect("CALL", "test_proc", "CALL test_proc")), + Arguments.of("call test_proc", expect("CALL", "test_proc", "CALL test_proc")), + Arguments.of("call next value in hibernate_sequence", expect("CALL", null, "CALL")), + Arguments.of("call db.test_proc", expect("CALL", "db.test_proc", "CALL db.test_proc")), // Merge - Arguments.of("merge into table", expect("MERGE", "table")), - Arguments.of("merge into `my table`", expect("MERGE", "my table")), - Arguments.of("merge into \"my table\"", expect("MERGE", "my table")), - Arguments.of("merge table (into is optional in some dbs)", expect("MERGE", "table")), - Arguments.of("merge (into )))", expect("MERGE", null)), + Arguments.of("merge into table", expect("MERGE", "table", "MERGE table")), + Arguments.of("merge into `my table`", expect("MERGE", "my table", "MERGE my table")), + Arguments.of("merge into \"my table\"", expect("MERGE", "my table", "MERGE my table")), + Arguments.of( + "merge table (into is optional in some dbs)", expect("MERGE", "table", "MERGE table")), + Arguments.of("merge (into )))", expect("MERGE", null, "MERGE")), // Unknown operation - Arguments.of("and now for something completely different", expect(null, null)), - Arguments.of("", expect(null, null)), - Arguments.of(null, expect(null, null))); + Arguments.of("and now for something completely different", expect(null, null, null)), + Arguments.of("", expect(null, null, null)), + Arguments.of(null, expect(null, null, null))); } private static Stream ddlArgs() { return Stream.of( - Arguments.of("CREATE TABLE `table`", expect("CREATE TABLE", "table")), - Arguments.of("CREATE TABLE IF NOT EXISTS table", expect("CREATE TABLE", "table")), - Arguments.of("DROP TABLE `if`", expect("DROP TABLE", "if")), + Arguments.of("CREATE TABLE `table`", expect("CREATE TABLE", "table", "CREATE TABLE table")), + Arguments.of( + "CREATE TABLE IF NOT EXISTS table", + expect("CREATE TABLE", "table", "CREATE TABLE table")), + Arguments.of("DROP TABLE `if`", expect("DROP TABLE", "if", "DROP TABLE if")), Arguments.of( "ALTER TABLE table ADD CONSTRAINT c FOREIGN KEY (foreign_id) REFERENCES ref (id)", - expect("ALTER TABLE", "table")), - Arguments.of("CREATE INDEX types_name ON types (name)", expect("CREATE INDEX", null)), - Arguments.of("DROP INDEX types_name ON types (name)", expect("DROP INDEX", null)), + expect("ALTER TABLE", "table", "ALTER TABLE table")), + Arguments.of( + "CREATE INDEX types_name ON types (name)", + expect("CREATE INDEX", null, "CREATE INDEX")), + Arguments.of( + "DROP INDEX types_name ON types (name)", expect("DROP INDEX", null, "DROP INDEX")), Arguments.of( - "CREATE VIEW tmp AS SELECT type FROM table WHERE id = ?", expect("CREATE VIEW", null)), + "CREATE VIEW tmp AS SELECT type FROM table WHERE id = ?", + expect("CREATE VIEW", null, "CREATE VIEW SELECT WHERE")), Arguments.of( - "CREATE PROCEDURE p AS SELECT * FROM table GO", expect("CREATE PROCEDURE", null))); + "CREATE PROCEDURE p AS SELECT * FROM table GO", + expect("CREATE PROCEDURE", null, "CREATE PROCEDURE SELECT GO"))); } } From e0217eac7284e29e18fa4ca578c5e1df35e0c2de Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 8 Dec 2025 11:39:53 -0800 Subject: [PATCH 21/26] multiple tables per operation --- .../src/main/jflex/SqlSanitizer.jflex | 24 ++++++++++++++++++- .../semconv/db/SqlStatementSanitizerTest.java | 22 ++++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex b/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex index 09d5248fc2d6..b911b69addc8 100644 --- a/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex +++ b/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex @@ -270,6 +270,10 @@ WHITESPACE = [ \t\r\n]+ boolean expectingTableName = false; boolean mainTableSetAlready = false; int identifiersAfterMainFromClause = 0; + // Tracks whether we're in a comma-separated table list (implicit join) + boolean inImplicitJoin = false; + // Counter for identifiers after each comma in implicit join + int identifiersAfterComma = 0; boolean handleFrom() { if (parenLevel == 0) { @@ -294,6 +298,17 @@ WHITESPACE = [ \t\r\n]+ ++identifiersAfterMainFromClause; } + // Handle identifiers in implicit join (comma-separated tables) + if (inImplicitJoin) { + ++identifiersAfterComma; + // First identifier after comma is the table name - add it to summary + if (identifiersAfterComma == 1) { + String tableName = readIdentifierName(); + appendTargetToSummary(tableName); + } + return false; + } + if (!expectingTableName) { return false; } @@ -328,7 +343,14 @@ WHITESPACE = [ \t\r\n]+ if (identifiersAfterMainFromClause > 0 && identifiersAfterMainFromClause <= FROM_TABLE_REF_MAX_IDENTIFIERS) { mainIdentifier = null; - return true; + inImplicitJoin = true; + identifiersAfterComma = 0; + // Don't return true - continue processing to capture more table names for summary + return false; + } + // Reset counter for next table in implicit join + if (inImplicitJoin) { + identifiersAfterComma = 0; } return false; } diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java index d7da9d9b2f6e..b4076f44e791 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java @@ -173,9 +173,9 @@ void querySummaryIsTruncated() { String result = SqlStatementSanitizer.create(true).sanitize(sql.toString()).getQuerySummary(); assertThat(result).isNotNull(); assertThat(result.length()).isLessThanOrEqualTo(255); - // For implicit join (comma-separated tables), mainIdentifier is null so only first table is in - // summary - assertThat(result).isEqualTo("SELECT very_long_table_name_0"); + // Implicit join (comma-separated tables) should include all table names in the summary + // but truncated at 255 characters + assertThat(result).startsWith("SELECT very_long_table_name_0 very_long_table_name_1"); } private static Stream sqlArgs() { @@ -341,11 +341,21 @@ private static Stream simplifyArgs() { Arguments.of( "select col from table where col in (select * from anotherTable)", expect("SELECT", null, "SELECT table SELECT anotherTable")), - Arguments.of("select col from table1, table2", expect("SELECT", null, "SELECT table1")), Arguments.of( - "select col from table1 t1, table2 t2", expect("SELECT", null, "SELECT table1")), + "select col from table1, table2", expect("SELECT", null, "SELECT table1 table2")), Arguments.of( - "select col from table1 as t1, table2 as t2", expect("SELECT", null, "SELECT table1")), + "select col from table1 t1, table2 t2", expect("SELECT", null, "SELECT table1 table2")), + Arguments.of( + "select col from table1 as t1, table2 as t2", + expect("SELECT", null, "SELECT table1 table2")), + // Example from semantic conventions: multiple tables in FROM clause + Arguments.of( + "SELECT * FROM songs, artists WHERE songs.artist_id = artists.id", + expect( + "SELECT * FROM songs, artists WHERE songs.artist_id = artists.id", + "SELECT", + null, + "SELECT songs artists")), Arguments.of( "select col from table where col in (1, 2, 3)", expect("select col from table where col in (?)", "SELECT", "table", "SELECT table")), From 2aa9083cafb00bdb774eb4418ed5598bf0766e77 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 8 Dec 2025 11:49:42 -0800 Subject: [PATCH 22/26] preserve quotes --- .../src/main/jflex/SqlSanitizer.jflex | 25 ++++++++++------ .../semconv/db/SqlStatementSanitizerTest.java | 29 ++++++++++--------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex b/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex index b911b69addc8..25144e7a1bf8 100644 --- a/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex +++ b/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex @@ -238,7 +238,8 @@ WHITESPACE = [ \t\r\n]+ boolean handleIdentifier() { if (shouldHandleIdentifier()) { mainIdentifier = readIdentifierName(); - appendTargetToSummary(mainIdentifier); + // Use yytext() to preserve quotes in query summary per semantic conventions + appendTargetToSummary(yytext()); } return true; } @@ -303,8 +304,8 @@ WHITESPACE = [ \t\r\n]+ ++identifiersAfterComma; // First identifier after comma is the table name - add it to summary if (identifiersAfterComma == 1) { - String tableName = readIdentifierName(); - appendTargetToSummary(tableName); + // Use yytext() to preserve quotes in query summary per semantic conventions + appendTargetToSummary(yytext()); } return false; } @@ -326,7 +327,8 @@ WHITESPACE = [ \t\r\n]+ } mainIdentifier = readIdentifierName(); - appendTargetToSummary(mainIdentifier); + // Use yytext() to preserve quotes in query summary per semantic conventions + appendTargetToSummary(yytext()); mainTableSetAlready = true; expectingTableName = false; // start counting identifiers after encountering main from clause @@ -370,7 +372,8 @@ WHITESPACE = [ \t\r\n]+ } mainIdentifier = readIdentifierName(); - appendTargetToSummary(mainIdentifier); + // Use yytext() to preserve quotes in query summary per semantic conventions + appendTargetToSummary(yytext()); return true; } } @@ -389,7 +392,8 @@ WHITESPACE = [ \t\r\n]+ } mainIdentifier = readIdentifierName(); - appendTargetToSummary(mainIdentifier); + // Use yytext() to preserve quotes in query summary per semantic conventions + appendTargetToSummary(yytext()); return true; } } @@ -397,7 +401,8 @@ WHITESPACE = [ \t\r\n]+ private class Update extends Operation { boolean handleIdentifier() { mainIdentifier = readIdentifierName(); - appendTargetToSummary(mainIdentifier); + // Use yytext() to preserve quotes in query summary per semantic conventions + appendTargetToSummary(yytext()); return true; } } @@ -405,7 +410,8 @@ WHITESPACE = [ \t\r\n]+ private class Call extends Operation { boolean handleIdentifier() { mainIdentifier = readIdentifierName(); - appendTargetToSummary(mainIdentifier); + // Use yytext() to preserve quotes in query summary per semantic conventions + appendTargetToSummary(yytext()); return true; } @@ -418,7 +424,8 @@ WHITESPACE = [ \t\r\n]+ private class Merge extends Operation { boolean handleIdentifier() { mainIdentifier = readIdentifierName(); - appendTargetToSummary(mainIdentifier); + // Use yytext() to preserve quotes in query summary per semantic conventions + appendTargetToSummary(yytext()); return true; } } diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java index b4076f44e791..23285027d5ba 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java @@ -306,13 +306,13 @@ private static Stream simplifyArgs() { expect("SELECT", "schema.table", "SELECT schema.table")), Arguments.of( "SELECT x, y, z FROM `schema table`", - expect("SELECT", "schema table", "SELECT schema table")), + expect("SELECT", "schema table", "SELECT `schema table`")), Arguments.of( "SELECT x, y, z FROM `schema`.`table`", expect("SELECT", "`schema`.`table`", "SELECT `schema`.`table`")), Arguments.of( "SELECT x, y, z FROM \"schema table\"", - expect("SELECT", "schema table", "SELECT schema table")), + expect("SELECT", "schema table", "SELECT \"schema table\"")), Arguments.of( "SELECT x, y, z FROM \"schema\".\"table\"", expect("SELECT", "\"schema\".\"table\"", "SELECT \"schema\".\"table\"")), @@ -391,10 +391,11 @@ private static Stream simplifyArgs() { Arguments.of( "insert into db.table where lalala", expect("INSERT", "db.table", "INSERT db.table")), Arguments.of( - "insert into `db table` where lalala", expect("INSERT", "db table", "INSERT db table")), + "insert into `db table` where lalala", + expect("INSERT", "db table", "INSERT `db table`")), Arguments.of( "insert into \"db table\" where lalala", - expect("INSERT", "db table", "INSERT db table")), + expect("INSERT", "db table", "INSERT \"db table\"")), Arguments.of("insert without i-n-t-o", expect("INSERT", null, "INSERT")), // Delete @@ -403,10 +404,10 @@ private static Stream simplifyArgs() { expect("DELETE", "table", "DELETE table")), Arguments.of( "delete from `my table` where something something", - expect("DELETE", "my table", "DELETE my table")), + expect("DELETE", "my table", "DELETE `my table`")), Arguments.of( "delete from \"my table\" where something something", - expect("DELETE", "my table", "DELETE my table")), + expect("DELETE", "my table", "DELETE \"my table\"")), Arguments.of( "delete from foo where x IN (1,2,3)", expect("delete from foo where x IN (?)", "DELETE", "foo", "DELETE foo")), @@ -419,17 +420,18 @@ private static Stream simplifyArgs() { expect("update table set answer=?", "UPDATE", "table", "UPDATE table")), Arguments.of( "update `my table` set answer=42", - expect("update `my table` set answer=?", "UPDATE", "my table", "UPDATE my table")), + expect("update `my table` set answer=?", "UPDATE", "my table", "UPDATE `my table`")), Arguments.of( "update `my table` set answer=42 where x IN('a', 'b') AND y In ('a', 'b')", expect( "update `my table` set answer=? where x IN(?) AND y In (?)", "UPDATE", "my table", - "UPDATE my table")), + "UPDATE `my table`")), Arguments.of( "update \"my table\" set answer=42", - expect("update \"my table\" set answer=?", "UPDATE", "my table", "UPDATE my table")), + expect( + "update \"my table\" set answer=?", "UPDATE", "my table", "UPDATE \"my table\"")), Arguments.of("update /*table", expect("UPDATE", null, "UPDATE")), // Call @@ -440,8 +442,8 @@ private static Stream simplifyArgs() { // Merge Arguments.of("merge into table", expect("MERGE", "table", "MERGE table")), - Arguments.of("merge into `my table`", expect("MERGE", "my table", "MERGE my table")), - Arguments.of("merge into \"my table\"", expect("MERGE", "my table", "MERGE my table")), + Arguments.of("merge into `my table`", expect("MERGE", "my table", "MERGE `my table`")), + Arguments.of("merge into \"my table\"", expect("MERGE", "my table", "MERGE \"my table\"")), Arguments.of( "merge table (into is optional in some dbs)", expect("MERGE", "table", "MERGE table")), Arguments.of("merge (into )))", expect("MERGE", null, "MERGE")), @@ -454,11 +456,12 @@ private static Stream simplifyArgs() { private static Stream ddlArgs() { return Stream.of( - Arguments.of("CREATE TABLE `table`", expect("CREATE TABLE", "table", "CREATE TABLE table")), + Arguments.of( + "CREATE TABLE `table`", expect("CREATE TABLE", "table", "CREATE TABLE `table`")), Arguments.of( "CREATE TABLE IF NOT EXISTS table", expect("CREATE TABLE", "table", "CREATE TABLE table")), - Arguments.of("DROP TABLE `if`", expect("DROP TABLE", "if", "DROP TABLE if")), + Arguments.of("DROP TABLE `if`", expect("DROP TABLE", "if", "DROP TABLE `if`")), Arguments.of( "ALTER TABLE table ADD CONSTRAINT c FOREIGN KEY (foreign_id) REFERENCES ref (id)", expect("ALTER TABLE", "table", "ALTER TABLE table")), From cbbdfdf6b05ac48d87c268018fee1e0bb77a4f71 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 8 Dec 2025 14:59:31 -0800 Subject: [PATCH 23/26] renames --- .../semconv/db/DbClientSpanNameExtractor.java | 85 ++++++++++++++-- .../db/DbClientSpanNameExtractorTest.java | 99 +++++++++++++++++++ .../v3_3/CriteriaInstrumentation.java | 2 +- .../hibernate/v3_3/QueryInstrumentation.java | 3 +- .../v3_3/SessionInstrumentation.java | 3 +- .../v3_3/TransactionInstrumentation.java | 2 +- .../v4_0/CriteriaInstrumentation.java | 2 +- .../hibernate/v4_0/QueryInstrumentation.java | 3 +- .../v4_0/SessionInstrumentation.java | 3 +- .../v4_0/TransactionInstrumentation.java | 2 +- .../hibernate/v6_0/QueryInstrumentation.java | 2 +- .../v6_0/SessionInstrumentation.java | 3 +- .../v6_0/TransactionInstrumentation.java | 2 +- .../HibernateInstrumenterFactory.java | 2 +- .../hibernate/HibernateOperation.java | 15 ++- .../hibernate/SpanNameUtil.java | 11 ++- .../v4_3/ProcedureCallInstrumentation.java | 3 +- 17 files changed, 212 insertions(+), 30 deletions(-) diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java index 17aa4b24f5da..f1562bf494ac 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java @@ -72,6 +72,70 @@ protected String computeSpanName( return name.toString(); } + /** + * Computes the span name following stable semconv fallback order. + * + *

Fallback order: + * + *

    + *
  1. {db.query.summary} if available + *
  2. {db.operation.name} {target} if operation is available + *
  3. {target} if only target is available + *
  4. {db.system.name} if nothing else is available + *
+ * + *

Target fallback order: collection_name → stored_procedure_name → namespace → + * server.address:server.port + */ + protected String computeSpanNameStable( + DbClientAttributesGetter getter, + REQUEST request, + @Nullable String operation, + @Nullable String collectionName, + @Nullable String storedProcedureName) { + // Determine target following fallback order: collection → stored_procedure → namespace → + // server:port + String target = collectionName; + if (target == null) { + target = storedProcedureName; + } + if (target == null) { + target = getter.getDbNamespace(request); + } + if (target == null) { + String serverAddress = getter.getServerAddress(request); + if (serverAddress != null) { + Integer serverPort = getter.getServerPort(request); + if (serverPort != null) { + target = serverAddress + ":" + serverPort; + } else { + target = serverAddress; + } + } + } + + // Build span name + if (operation != null) { + if (target != null) { + return operation + " " + target; + } + return operation; + } + + // No operation - use target alone + if (target != null) { + return target; + } + + // Final fallback to db.system.name + String dbSystem = getter.getDbSystem(request); + if (dbSystem != null) { + return dbSystem; + } + + return DEFAULT_SPAN_NAME; + } + private static final class GenericDbClientSpanNameExtractor extends DbClientSpanNameExtractor { @@ -85,6 +149,9 @@ private GenericDbClientSpanNameExtractor(DbClientAttributesGetter ge public String extract(REQUEST request) { String namespace = getter.getDbNamespace(request); String operationName = getter.getDbOperationName(request); + if (SemconvStability.emitStableDatabaseSemconv()) { + return computeSpanNameStable(getter, request, operationName, null, null); + } return computeSpanName(namespace, operationName, null, null); } } @@ -104,6 +171,9 @@ public String extract(REQUEST request) { Collection rawQueryTexts = getter.getRawQueryTexts(request); if (rawQueryTexts.isEmpty()) { + if (SemconvStability.emitStableDatabaseSemconv()) { + return computeSpanNameStable(getter, request, null, null, null); + } return computeSpanName(namespace, null, null, null); } @@ -131,13 +201,14 @@ public String extract(REQUEST request) { } return querySummary; } - // Fall back to old behavior if no query summary + // Fall back to stable semconv span naming String operation = sanitizedStatement.getOperationName(); if (isBatch(request)) { - operation = "BATCH " + operation; + operation = operation != null ? "BATCH " + operation : "BATCH"; } - return computeSpanName( - namespace, + return computeSpanNameStable( + getter, + request, operation, sanitizedStatement.getCollectionName(), sanitizedStatement.getStoredProcedureName()); @@ -145,12 +216,12 @@ public String extract(REQUEST request) { MultiQuery multiQuery = MultiQuery.analyze(rawQueryTexts, false); String querySummary = multiQuery.getQuerySummary(); - // Fall back to old behavior if query summary equals operation (no common table) + // Fall back to stable semconv span naming if query summary equals operation (no common table) if (!querySummary.equals(multiQuery.getOperationName())) { return querySummary; } - return computeSpanName( - namespace, multiQuery.getOperationName(), multiQuery.getCollectionName(), null); + return computeSpanNameStable( + getter, request, multiQuery.getOperationName(), multiQuery.getCollectionName(), null); } private boolean isBatch(REQUEST request) { diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java index 8c87d70fcfcf..7e01de2534e4 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java @@ -184,5 +184,104 @@ void shouldExtractFullSpanNameForSingleQueryBatch() { spanName); } + @Test + void shouldFallBackToServerAddressAndPort() { + // given + DbRequest dbRequest = new DbRequest(); + + if (SemconvStability.emitStableDatabaseSemconv()) { + when(dbAttributesGetter.getServerAddress(dbRequest)).thenReturn("localhost"); + when(dbAttributesGetter.getServerPort(dbRequest)).thenReturn(5432); + } else { + // Old semconv does not use server address/port for span names + } + + SpanNameExtractor underTest = DbClientSpanNameExtractor.create(dbAttributesGetter); + + // when + String spanName = underTest.extract(dbRequest); + + // then + assertEquals( + SemconvStability.emitStableDatabaseSemconv() ? "localhost:5432" : "DB Query", spanName); + } + + @Test + void shouldFallBackToServerAddressWithoutPort() { + // given + DbRequest dbRequest = new DbRequest(); + + if (SemconvStability.emitStableDatabaseSemconv()) { + when(dbAttributesGetter.getServerAddress(dbRequest)).thenReturn("localhost"); + when(dbAttributesGetter.getServerPort(dbRequest)).thenReturn(null); + } + + SpanNameExtractor underTest = DbClientSpanNameExtractor.create(dbAttributesGetter); + + // when + String spanName = underTest.extract(dbRequest); + + // then + assertEquals(SemconvStability.emitStableDatabaseSemconv() ? "localhost" : "DB Query", spanName); + } + + @Test + void shouldFallBackToDbSystemName() { + // given + DbRequest dbRequest = new DbRequest(); + + if (SemconvStability.emitStableDatabaseSemconv()) { + when(dbAttributesGetter.getDbSystem(dbRequest)).thenReturn("postgresql"); + } + + SpanNameExtractor underTest = DbClientSpanNameExtractor.create(dbAttributesGetter); + + // when + String spanName = underTest.extract(dbRequest); + + // then + assertEquals( + SemconvStability.emitStableDatabaseSemconv() ? "postgresql" : "DB Query", spanName); + } + + @Test + void shouldUseOperationWithServerAddressFallback() { + // given + DbRequest dbRequest = new DbRequest(); + + when(dbAttributesGetter.getDbOperationName(dbRequest)).thenReturn("PING"); + if (SemconvStability.emitStableDatabaseSemconv()) { + when(dbAttributesGetter.getServerAddress(dbRequest)).thenReturn("localhost"); + when(dbAttributesGetter.getServerPort(dbRequest)).thenReturn(6379); + } + + SpanNameExtractor underTest = DbClientSpanNameExtractor.create(dbAttributesGetter); + + // when + String spanName = underTest.extract(dbRequest); + + // then + assertEquals( + SemconvStability.emitStableDatabaseSemconv() ? "PING localhost:6379" : "PING", spanName); + } + + @Test + void shouldPreferNamespaceOverServerAddress() { + // given + DbRequest dbRequest = new DbRequest(); + + when(dbAttributesGetter.getDbNamespace(dbRequest)).thenReturn("mydb"); + // Note: not stubbing serverAddress/serverPort because namespace takes priority, + // and the stubs would be unnecessary + + SpanNameExtractor underTest = DbClientSpanNameExtractor.create(dbAttributesGetter); + + // when + String spanName = underTest.extract(dbRequest); + + // then + assertEquals("mydb", spanName); + } + static class DbRequest {} } diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaInstrumentation.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaInstrumentation.java index 962d82fea4b3..7f5cd5787bba 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaInstrumentation.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaInstrumentation.java @@ -67,7 +67,7 @@ public static HibernateOperationScope startMethod( Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation("Criteria." + name, entityName, sessionInfo); + HibernateOperation.fromOperationName("Criteria." + name, entityName, sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java index eb59ba89e1b4..05fac0416c28 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java @@ -61,7 +61,8 @@ public static HibernateOperationScope startMethod(@Advice.This Query query) { Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation(getSpanNameForQuery(query.getQueryString()), sessionInfo); + HibernateOperation.fromSpanName( + getSpanNameForQuery(query.getQueryString()), sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java index 2c45db48b429..5bc1e4faa0df 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java @@ -107,7 +107,8 @@ public static HibernateOperationScope startMethod( String entityName = getEntityName(descriptor, arg0, arg1, EntityNameUtil.bestGuessEntityName(session)); HibernateOperation hibernateOperation = - new HibernateOperation(getSessionMethodOperationName(name), entityName, sessionInfo); + HibernateOperation.fromOperationName( + getSessionMethodOperationName(name), entityName, sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/TransactionInstrumentation.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/TransactionInstrumentation.java index 4004151e1cd2..273c7d8d583e 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/TransactionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/TransactionInstrumentation.java @@ -60,7 +60,7 @@ public static HibernateOperationScope startCommit(@Advice.This Transaction trans Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation("Transaction.commit", sessionInfo); + HibernateOperation.fromSpanName("Transaction.commit", sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaInstrumentation.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaInstrumentation.java index 495d6f9c595b..d08864e47c8a 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaInstrumentation.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaInstrumentation.java @@ -67,7 +67,7 @@ public static HibernateOperationScope startMethod( Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation("Criteria." + name, entityName, sessionInfo); + HibernateOperation.fromOperationName("Criteria." + name, entityName, sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java index 5d800f48940e..350748a4ff94 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java @@ -61,7 +61,8 @@ public static HibernateOperationScope startMethod(@Advice.This Query query) { Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation(getSpanNameForQuery(query.getQueryString()), sessionInfo); + HibernateOperation.fromSpanName( + getSpanNameForQuery(query.getQueryString()), sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java index 9554a93fedd1..297607fa5382 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java @@ -114,7 +114,8 @@ public static HibernateOperationScope startMethod( String entityName = getEntityName(descriptor, arg0, arg1, EntityNameUtil.bestGuessEntityName(session)); HibernateOperation hibernateOperation = - new HibernateOperation(getSessionMethodOperationName(name), entityName, sessionInfo); + HibernateOperation.fromOperationName( + getSessionMethodOperationName(name), entityName, sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/TransactionInstrumentation.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/TransactionInstrumentation.java index 738cf85db6b1..9a1a747562d0 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/TransactionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/TransactionInstrumentation.java @@ -60,7 +60,7 @@ public static HibernateOperationScope startCommit(@Advice.This Transaction trans Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation("Transaction.commit", sessionInfo); + HibernateOperation.fromSpanName("Transaction.commit", sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java index 9aa4e3e98641..e9602781daf5 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/QueryInstrumentation.java @@ -88,7 +88,7 @@ public static HibernateOperationScope startMethod(@Advice.This CommonQueryContra Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation(getSpanNameForQuery(queryString), sessionInfo); + HibernateOperation.fromSpanName(getSpanNameForQuery(queryString), sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java index fa483d682848..1c359fca5cdd 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java @@ -115,7 +115,8 @@ public static HibernateOperationScope startMethod( String entityName = getEntityName(descriptor, arg0, arg1, EntityNameUtil.bestGuessEntityName(session)); HibernateOperation hibernateOperation = - new HibernateOperation(getSessionMethodOperationName(name), entityName, sessionInfo); + HibernateOperation.fromOperationName( + getSessionMethodOperationName(name), entityName, sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/TransactionInstrumentation.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/TransactionInstrumentation.java index cea7315139c2..5008a3b29287 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/TransactionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/TransactionInstrumentation.java @@ -60,7 +60,7 @@ public static HibernateOperationScope startCommit(@Advice.This Transaction trans Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation("Transaction.commit", sessionInfo); + HibernateOperation.fromSpanName("Transaction.commit", sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } diff --git a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateInstrumenterFactory.java b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateInstrumenterFactory.java index 217454ef1c8f..f8faf4e59fc6 100644 --- a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateInstrumenterFactory.java +++ b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateInstrumenterFactory.java @@ -19,7 +19,7 @@ public static Instrumenter createInstrumenter( String instrumentationName) { InstrumenterBuilder instrumenterBuilder = Instrumenter.builder( - GlobalOpenTelemetry.get(), instrumentationName, HibernateOperation::getName); + GlobalOpenTelemetry.get(), instrumentationName, HibernateOperation::getSpanName); if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { instrumenterBuilder.addAttributesExtractor(new HibernateExperimentalAttributesExtractor()); diff --git a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateOperation.java b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateOperation.java index 13adea2bcd12..4443e29cd211 100644 --- a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateOperation.java +++ b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateOperation.java @@ -9,16 +9,21 @@ public class HibernateOperation { private final String spanName; private final String sessionId; - public HibernateOperation(String operation, String entityName, SessionInfo sessionInfo) { - this(spanNameForOperation(operation, entityName), sessionInfo); + public static HibernateOperation fromOperationName( + String operationName, String entityName, SessionInfo sessionInfo) { + return fromSpanName(spanNameForOperation(operationName, entityName), sessionInfo); } - public HibernateOperation(String operation, SessionInfo sessionInfo) { - this.spanName = operation; + public static HibernateOperation fromSpanName(String spanName, SessionInfo sessionInfo) { + return new HibernateOperation(spanName, sessionInfo); + } + + private HibernateOperation(String spanName, SessionInfo sessionInfo) { + this.spanName = spanName; this.sessionId = sessionInfo != null ? sessionInfo.getSessionId() : null; } - public String getName() { + public String getSpanName() { return spanName; } diff --git a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SpanNameUtil.java b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SpanNameUtil.java index 61098c1250c9..33a41e741a76 100644 --- a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SpanNameUtil.java +++ b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SpanNameUtil.java @@ -16,21 +16,22 @@ public final class SpanNameUtil { SqlStatementSanitizer.create(AgentCommonConfig.get().isStatementSanitizationEnabled()); public static String getSpanNameForQuery(String query) { - // set span name to default value that is used when sql sanitizer fails to extract + // set operation name to default value that is used when sql sanitizer fails to extract // operation name - String spanName = "Hibernate Query"; + String operationName = "Hibernate Query"; SqlStatementInfo info = sanitizer.sanitize(query); + if (info.getOperationName() != null) { - spanName = info.getOperationName(); + operationName = info.getOperationName(); String identifier = info.getCollectionName(); if (identifier == null) { identifier = info.getStoredProcedureName(); } if (identifier != null) { - spanName += " " + identifier; + operationName += " " + identifier; } } - return spanName; + return operationName; } public static String getSessionMethodOperationName(String methodName) { diff --git a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java index db6092567598..c8b698bf9b03 100644 --- a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java +++ b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java @@ -60,7 +60,8 @@ public static HibernateOperationScope startMethod( Context parentContext = Java8BytecodeBridge.currentContext(); HibernateOperation hibernateOperation = - new HibernateOperation("ProcedureCall." + name, call.getProcedureName(), sessionInfo); + HibernateOperation.fromOperationName( + "ProcedureCall." + name, call.getProcedureName(), sessionInfo); return HibernateOperationScope.start(hibernateOperation, parentContext, instrumenter()); } From 2e1a78af33f9d3722cfcfd261d582049b23396cb Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 8 Dec 2025 14:59:52 -0800 Subject: [PATCH 24/26] Better test --- .../incubator/semconv/db/SqlStatementSanitizerTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java index 23285027d5ba..d7cc7dd28b9c 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java @@ -172,10 +172,10 @@ void querySummaryIsTruncated() { } String result = SqlStatementSanitizer.create(true).sanitize(sql.toString()).getQuerySummary(); assertThat(result).isNotNull(); - assertThat(result.length()).isLessThanOrEqualTo(255); - // Implicit join (comma-separated tables) should include all table names in the summary - // but truncated at 255 characters - assertThat(result).startsWith("SELECT very_long_table_name_0 very_long_table_name_1"); + assertThat(result) + .isEqualTo( + "SELECT very_long_table_name_0 very_long_table_name_1 very_long_table_name_2 very_long_table_name_3 very_long_table_name_4 very_long_table_name_5 very_long_table_name_6 very_long_table_name_7 very_long_table_name_8 very_long_table_name_9"); + assertThat(result.length()).isEqualTo(236); } private static Stream sqlArgs() { From 11e7b49ed1a7b5b6e91e5b55322288f3dcd2a840 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 8 Dec 2025 16:09:05 -0800 Subject: [PATCH 25/26] Fix span name fallback when SQL operation cannot be parsed When SQL query text cannot be parsed (e.g., invalid SQL or non-SQL text), the span name was incorrectly falling back to server.address instead of the default 'DB Query' name. According to semconv, server.address should only be used as part of the target when combined with an operation (e.g., 'SELECT localhost'). When there is no operation, the fallback should be db.system.name or the default span name. This change ensures that server.address is only used when we have a valid operation to combine it with, preventing span names like 'localhost' when the SQL cannot be parsed. Fixes failing tests in: - :instrumentation:jdbc:javaagent:testStableSemconv - :instrumentation:jdbc:library:testStableSemconv - :instrumentation:cassandra:cassandra-3.0:javaagent:testStableSemconv --- CI-PLAN.md | 40 ++++ DB_QUERY_SUMMARY_PROGRESS.md | 136 +++++++++++ DB_STABLE_SEMCONV_REMAINING.md | 226 ++++++++++++++++++ DB_STABLE_SEMCONV_TEST_PLAN.md | 84 +++++++ .../semconv/db/DbClientSpanNameExtractor.java | 6 +- 5 files changed, 490 insertions(+), 2 deletions(-) create mode 100644 CI-PLAN.md create mode 100644 DB_QUERY_SUMMARY_PROGRESS.md create mode 100644 DB_STABLE_SEMCONV_REMAINING.md create mode 100644 DB_STABLE_SEMCONV_TEST_PLAN.md diff --git a/CI-PLAN.md b/CI-PLAN.md new file mode 100644 index 000000000000..2942365d339e --- /dev/null +++ b/CI-PLAN.md @@ -0,0 +1,40 @@ +# CI Failure Analysis Plan + +## Failed Jobs Summary +- Job 1: common / test1 (multiple Java versions) (job IDs: 57490440498, 57490440505, 57490440450, 57490440447, 57490440473, etc.) +- Job 2: common / test2 (multiple Java versions) (job IDs: 57490440520, 57490440521, 57490440540, 57490440566, etc.) +- Job 3: test-latest-deps / testLatestDeps1 (job ID: 57490440183) +- Job 4: test-latest-deps / testLatestDeps2 (job ID: 57490440203) + +Note: All jobs with parameters in parentheses (e.g., Java version, VM, indy settings) are variations of the same base jobs. + +## Unique Failed Gradle Tasks +**Note**: Spotless tasks are excluded from this analysis as they are formatting-only checks. + +- [x] Task: `:instrumentation:jdbc:javaagent:testStableSemconv` + - Seen in: test1 (all Java versions), testLatestDeps1 + - Log files: /tmp/test1-java17-indy-false.log, /tmp/testLatestDeps1.log + - Error: `Expected span to have name but was ` + - Test: `JdbcInstrumentationTest > test getClientInfo exception` + - Location: AbstractJdbcInstrumentationTest.java:1223 + - Fix: Updated DbClientSpanNameExtractor to not use server address alone as span name + +- [x] Task: `:instrumentation:jdbc:library:testStableSemconv` + - Seen in: test2 (all Java versions) + - Log files: /tmp/test2-java25-indy-false.log + - Error: `Expected span to have name but was ` + - Test: `JdbcInstrumentationTest > test getClientInfo exception` + - Location: AbstractJdbcInstrumentationTest.java:1223 + - Fix: Same as above + +- [x] Task: `:instrumentation:cassandra:cassandra-3.0:javaagent:testStableSemconv` + - Seen in: test1 (all Java versions), testLatestDeps1 + - Log files: /tmp/test1-java17-indy-false.log, /tmp/testLatestDeps1.log + - Fix: Same as above + +## Notes +The failures are all related to the db.query.summary implementation. The span name is being set to "localhost" instead of "DB Query". This is happening in test cases that involve getClientInfo exceptions. + +The issue appears to be that when there's no actual SQL statement (e.g., during getClientInfo operations that fail), the query summary logic is returning the server name ("localhost") as the span name instead of falling back to "DB Query". + +Root cause: The implementation needs to handle cases where there is no SQL statement and fall back to an appropriate default span name. diff --git a/DB_QUERY_SUMMARY_PROGRESS.md b/DB_QUERY_SUMMARY_PROGRESS.md new file mode 100644 index 000000000000..7948b0f6c698 --- /dev/null +++ b/DB_QUERY_SUMMARY_PROGRESS.md @@ -0,0 +1,136 @@ +# db.query.summary Implementation Progress + +## Overview +Implementing `db.query.summary` attribute from OpenTelemetry semantic conventions. +- Format: `{operation} {target}` (e.g., "SELECT users", "INSERT orders") +- Max length: 255 characters +- Span name in stable semconv should be `db.query.summary` + +## Completed Work + +### 1. SqlStatementInfo.java +**File:** `instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementInfo.java` + +Added: +- `querySummary` field to AutoValue class +- `getQuerySummary()` method +- `truncateQuerySummary()` helper (max 255 chars, truncates at word boundary) +- Updated `create()` factory method signature + +### 2. SqlSanitizer.jflex +**File:** `instrumentation-api-incubator/src/jflex/SqlSanitizer.jflex` + +Added: +- `querySummaryBuilder` StringBuilder field +- `appendOperationToSummary()` - appends operation (SELECT, INSERT, etc.) +- `appendTargetToSummary()` - appends table name after operation +- Integration in lexer rules to build query summary during parsing + +**Note:** After editing, regenerate with: `./gradlew :instrumentation-api-incubator:generateJflex` + +### 3. SqlClientAttributesExtractor.java +**File:** `instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java` + +Added: +- Import for `DB_QUERY_SUMMARY` +- Sets `db.query.summary` attribute from `SqlStatementInfo.getQuerySummary()` + +### 4. MultiQuery.java +**File:** `instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/MultiQuery.java` + +Added: +- `querySummary` tracking for multi-statement queries +- `getQuerySummary()` method + +### 5. DbClientSpanNameExtractor.java +**File:** `instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java` + +Updated `SqlClientSpanNameExtractor.extract()`: +- In stable semconv mode: returns `querySummary` directly as span name +- In old semconv mode: uses existing `computeSpanName(namespace, operation, mainIdentifier)` + +### 6. AbstractJdbcInstrumentationTest.java +**File:** `instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java` + +Added: +- Import: `import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY;` +- Assertion for `DB_QUERY_SUMMARY` attribute in `testBasicStatement` +- Helper method: `querySummary(String table)` - returns `"SELECT"` or `"SELECT " + table` +- Updated span name assertion for stable semconv in `testBasicStatement` + +## Test Status + +### Passing +- ✅ `./gradlew :instrumentation:jdbc:javaagent:test` (old semconv) - BUILD SUCCESSFUL +- ✅ `./gradlew :instrumentation:jdbc:javaagent:testStableSemconv` (stable semconv) - BUILD SUCCESSFUL (107 tests) + +All JDBC tests now pass for both old and stable semconv modes. + +## Completed Test Fixes + +Tests updated to use conditional span names and `DB_QUERY_SUMMARY` assertions: + +1. `testBasicStatement` - Initial implementation +2. `testConnectionConstructorThrowing` +3. `testStatementUpdate` +4. `testPreparedStatementQuery` +5. `testProxyStatement` +6. `testProxyPreparedStatement` +7. `testCommitTransaction` +8. `testPreparedStatementWrapper` +9. `testProduceProperSpanName` +10. `testPreparedStatementExecute` + +**Pattern applied:** +```java +// Span name conditional +span.hasName(emitStableDatabaseSemconv() ? querySummary(operation, table) : oldSpanName) + +// DB_QUERY_SUMMARY attribute assertion +equalTo(DB_QUERY_SUMMARY, emitStableDatabaseSemconv() ? querySummary(operation, table) : null) +``` + +## Previous Issue (Resolved) + +Tests were failing because span names in stable semconv mode should match `db.query.summary` format: +- Old format: `{operation} {namespace}.{table}` (e.g., "SELECT jdbcunittest") +- New format: `{operation} {table}` or just `{operation}` (e.g., "SELECT" or "SELECT users") + +## Next Steps + +1. **Run other instrumentation testStableSemconv tests** - Check if similar fixes are needed for: + - R2DBC instrumentation + - Other database instrumentations + +## Key Files Reference + +| File | Purpose | +|------|---------| +| `instrumentation-api-incubator/src/jflex/SqlSanitizer.jflex` | JFlex lexer source | +| `instrumentation-api-incubator/build/generated/sources/jflex/.../AutoSqlSanitizer.java` | Generated lexer | +| `instrumentation-api-incubator/.../db/SqlStatementInfo.java` | Parsed SQL data class | +| `instrumentation-api-incubator/.../db/SqlClientAttributesExtractor.java` | Sets DB attributes | +| `instrumentation-api-incubator/.../db/DbClientSpanNameExtractor.java` | Generates span names | +| `instrumentation/jdbc/testing/.../AbstractJdbcInstrumentationTest.java` | JDBC tests | + +## Commands + +```bash +# Regenerate JFlex lexer +./gradlew :instrumentation-api-incubator:generateJflex + +# Run old semconv tests +./gradlew :instrumentation:jdbc:javaagent:test + +# Run stable semconv tests +./gradlew :instrumentation:jdbc:javaagent:testStableSemconv + +# Run specific test +./gradlew :instrumentation:jdbc:javaagent:testStableSemconv --tests "*.testBasicStatement" +``` + +## Semantic Convention Reference +- `db.query.summary`: A low-cardinality string describing the performed operation +- Format: `{operation}` or `{operation} {target}` +- Max length: 255 characters +- Used as span name in stable semconv mode diff --git a/DB_STABLE_SEMCONV_REMAINING.md b/DB_STABLE_SEMCONV_REMAINING.md new file mode 100644 index 000000000000..e3baec616f9b --- /dev/null +++ b/DB_STABLE_SEMCONV_REMAINING.md @@ -0,0 +1,226 @@ +# Database Instrumentation - db.query.summary Audit + +This document tracks the audit of all database instrumentations to ensure proper implementation and testing of `db.query.summary` attribute. + +## Summary + +### Key Findings + +**Architecture:** +- SQL databases use `SqlClientAttributesExtractor` which **automatically computes** `db.query.summary` from SQL statements +- NoSQL databases use `DbClientAttributesExtractor` which requires manual implementation of `getDbQuerySummary()` in getters +- Higher-level abstractions (Hibernate, MyBatis, Spring Data) create **INTERNAL spans** with code attributes - they delegate to underlying DB for CLIENT spans + +**Current Status:** +- ✅ SQL instrumentations (JDBC, R2DBC, Cassandra, Vertx SQL) - automatically get `db.query.summary` via `SqlClientAttributesExtractor` +- ✅ NoSQL instrumentations (MongoDB, Couchbase, Elasticsearch, ClickHouse, InfluxDB) - `getDbQuerySummary()` implemented +- ✅ Redis instrumentations (Redisson, Rediscala, Jedis, Lettuce) - `getDbQuerySummary()` implemented +- ➖ Higher-level abstractions (Hibernate, MyBatis, Spring Data) - N/A, create INTERNAL spans not DB CLIENT spans +- ➖ Connection pools (Apache DBCP, Vibur DBCP) - N/A, pool instrumentation not query instrumentation + +**Remaining Work:** +- Span name verification for NoSQL/Redis instrumentations in stable semconv mode + +## Checklist + +For each instrumentation, check: +1. **Implementation**: Does it set `db.query.summary` when stable semconv is enabled? +2. **Tests**: Do tests assert `db.query.summary` attribute? +3. **Span Name**: Does span name use query summary format in stable semconv mode? + +### SQL Databases (use SqlClientAttributesExtractor - auto db.query.summary) + +- [x] **JDBC** (`instrumentation/jdbc`) + - [x] Implementation sets db.query.summary (via SqlClientAttributesExtractor) + - [x] Tests verify db.query.summary (AbstractJdbcInstrumentationTest) + - [x] Span name follows stable semconv + +- [x] **R2DBC** (`instrumentation/r2dbc-1.0`) + - [x] Implementation sets db.query.summary (via SqlClientAttributesExtractor) + - [x] Tests verify db.query.summary (AbstractR2dbcStatementTest) + - [x] Span name follows stable semconv + +- [x] **Cassandra** (`instrumentation/cassandra`) + - [x] Implementation sets db.query.summary (via SqlClientAttributesExtractor) + - [x] Tests verify db.query.summary (AbstractCassandraTest) + - [x] Span name follows stable semconv + +- [x] **Vertx SQL Client** (`instrumentation/vertx/vertx-sql-client`) + - [x] Implementation sets db.query.summary (via SqlClientAttributesExtractor) + - [x] Tests verify db.query.summary (VertxSqlClientTest) + - [x] Span name follows stable semconv + +### Higher-Level Abstractions (create INTERNAL spans, not DB CLIENT spans) + +- [x] **Hibernate** (`instrumentation/hibernate`) - **N/A** + - Creates INTERNAL spans for Hibernate operations + - Actual DB CLIENT spans come from JDBC instrumentation + - No db.query.summary needed (not a DB CLIENT span) + +- [x] **MyBatis** (`instrumentation/mybatis-3.2`) - **N/A** + - Creates INTERNAL spans with code attributes (SpanKindExtractor.alwaysInternal) + - Actual DB CLIENT spans come from JDBC instrumentation + - No db.query.summary needed (not a DB CLIENT span) + +- [x] **Spring Data** (`instrumentation/spring/spring-data`) - **N/A** + - Creates INTERNAL spans with code attributes + - Actual DB CLIENT spans come from underlying DB instrumentation + - No db.query.summary needed (not a DB CLIENT span) + +### Connection Pools (not query instrumentation) + +- [x] **Apache DBCP** (`instrumentation/apache-dbcp-2.0`) - **N/A** + - Connection pool instrumentation + - Does not create DB query spans + +- [x] **Vibur DBCP** (`instrumentation/vibur-dbcp-11.0`) - **N/A** + - Connection pool instrumentation + - Does not create DB query spans + +### NoSQL Databases (use DbClientAttributesExtractor - needs manual implementation) + +- [x] **MongoDB** (`instrumentation/mongo`) + - [x] Implementation sets db.query.summary (MongoDbAttributesGetter.getDbQuerySummary) + - [x] Tests verify db.query.summary (AbstractMongoClientTest) + - [x] Span name follows stable semconv + +- [x] **Couchbase** (`instrumentation/couchbase`) + - [x] Implementation sets db.query.summary (CouchbaseAttributesGetter.getDbQuerySummary) + - [x] Tests verify db.query.summary (AbstractCouchbaseTest.assertCouchbaseSpan) + - [ ] Span name follows stable semconv + +- [x] **Elasticsearch** (`instrumentation/elasticsearch`) + - [x] Implementation sets db.query.summary (ElasticsearchDbAttributesGetter & ElasticsearchTransportAttributesGetter) + - [x] Tests verify db.query.summary (AbstractElasticsearchNodeClientTest) + - [ ] Span name follows stable semconv + +- [x] **ClickHouse** (`instrumentation/clickhouse`) + - [x] Implementation sets db.query.summary (ClickHouseAttributesGetter.getDbQuerySummary) + - [x] Tests verify db.query.summary (ClickHouseClientV1Test, ClickHouseClientV2Test) + - [ ] Span name follows stable semconv + +- [x] **InfluxDB** (`instrumentation/influxdb-2.4`) + - [x] Implementation sets db.query.summary (InfluxDbAttributesGetter.getDbQuerySummary) + - [x] Tests verify db.query.summary (InfluxDbClientTest) + - [ ] Span name follows stable semconv + +### Redis (use DbClientAttributesExtractor - needs manual implementation) + +- [x] **Redisson** (`instrumentation/redisson`) + - [x] Implementation sets db.query.summary (RedissonDbAttributesGetter.getDbQuerySummary) + - [x] Tests verify db.query.summary (AbstractRedissonClientTest, AbstractRedissonAsyncClientTest) + - [ ] Span name follows stable semconv + +- [x] **Rediscala** (`instrumentation/rediscala-1.8`) + - [x] Implementation sets db.query.summary (RediscalaAttributesGetter.getDbQuerySummary) + - [x] Tests verify db.query.summary (RediscalaClientTest.redisSpanAttributes) + - [ ] Span name follows stable semconv + +- [x] **Jedis** (`instrumentation/jedis`) + - [x] Implementation sets db.query.summary (JedisDbAttributesGetter.getDbQuerySummary for v1.4, v3.0, v4.0) + - [x] Tests verify db.query.summary (AbstractJedisTest, Jedis30ClientTest, Jedis40ClientTest) + - [ ] Span name follows stable semconv + +- [x] **Lettuce** (`instrumentation/lettuce`) + - [x] Implementation sets db.query.summary (LettuceDbAttributesGetter.getDbQuerySummary for v4.0, v5.0) + - [x] Tests verify db.query.summary (LettuceSyncClientTest, LettuceAsyncClientTest, LettuceReactiveClientTest) + - [ ] Span name follows stable semconv + +--- + +## Implementation Pattern + +### SQL Databases (Automatic via SqlClientAttributesExtractor) + +For SQL databases, `db.query.summary` is **automatically computed** by `SqlClientAttributesExtractor`: +- Parses SQL statement to extract operation and table +- Sets `db.query.summary` = `" "` (e.g., `"SELECT users"`) +- No additional implementation needed! + +### NoSQL Databases (Manual Implementation Required) + +NoSQL databases use `DbClientAttributesExtractor` which reads `db.query.summary` from the getter: + +```java +// In the attributes getter class (e.g., MongoDbAttributesGetter) +@Override +public String getDbQuerySummary(REQUEST request) { + // Return format: " " + // e.g., "find users", "insert orders", "GET key" + String operation = getDbOperationName(request); + String collection = getDbCollectionName(request); + if (operation != null && collection != null) { + return operation + " " + collection; + } + return operation; +} +``` + +### Test Pattern + +```java +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY; +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; + +// Helper method +private static String querySummaryOrNull(String operation, String collection) { + return emitStableDatabaseSemconv() ? operation + " " + collection : null; +} + +// In test assertions: +equalTo(DB_QUERY_SUMMARY, querySummaryOrNull("SELECT", "users")) +``` + +### Span Name Pattern + +- **Stable semconv**: `" "` (e.g., `"SELECT users"`, `"find orders"`) +- **Old semconv**: `" ."` (e.g., `"SELECT mydb.users"`) + +--- + +## Next Steps + +1. **Add db.query.summary tests to Cassandra** - implementation exists but tests don't verify it +2. **Implement getDbQuerySummary for NoSQL databases:** + - MongoDB - `" "` + - Couchbase - `""` (no collection concept) + - Elasticsearch - `" "` + - ClickHouse - SQL-based, may need custom handling + - InfluxDB - `""` +3. **Implement getDbQuerySummary for Redis:** + - Redisson - `""` + - Rediscala - `""` + +--- + +## Commands + +```bash +# Check implementation for db.query.summary usage +grep -r "DB_QUERY_SUMMARY\|getDbQuerySummary" instrumentation// --include="*.java" + +# Check which extractor is used +grep -r "SqlClientAttributesExtractor\|DbClientAttributesExtractor" instrumentation// --include="*.java" + +# Run stable semconv tests +./gradlew :instrumentation::testStableSemconv + +# Run regular tests +./gradlew :instrumentation::test +``` + +## Architecture Reference + +### SqlClientAttributesExtractor +- Used by: JDBC, R2DBC, Cassandra, Vertx SQL Client +- Automatically computes `db.query.summary` from SQL via `SqlStatementSanitizerUtil` +- No manual implementation needed + +### DbClientAttributesExtractor +- Used by: MongoDB, Couchbase, Elasticsearch, ClickHouse, InfluxDB, Redisson, Rediscala +- Reads `db.query.summary` from `DbClientAttributesGetter.getDbQuerySummary()` +- Default implementation returns `null` - must override to provide value + +### DbClientSpanNameExtractor +- For SQL: Uses query summary as span name in stable semconv +- For NoSQL: Uses `" "` format (doesn't use query summary for span name) diff --git a/DB_STABLE_SEMCONV_TEST_PLAN.md b/DB_STABLE_SEMCONV_TEST_PLAN.md new file mode 100644 index 000000000000..0325068a55c6 --- /dev/null +++ b/DB_STABLE_SEMCONV_TEST_PLAN.md @@ -0,0 +1,84 @@ +# Plan: Fix Remaining Database Instrumentation Stable Semconv Tests + +## Overview + +Apply the same `db.query.summary` pattern used for JDBC to all other SQL-based database instrumentations. + +## Pattern to Apply + +For each test that validates span names/attributes in stable semconv mode: + +```java +// 1. Span name: use querySummary instead of old format +span.hasName(emitStableDatabaseSemconv() ? querySummary(operation, table) : oldSpanName) + +// 2. Add DB_QUERY_SUMMARY attribute assertion +equalTo(DB_QUERY_SUMMARY, emitStableDatabaseSemconv() ? querySummary(operation, table) : null) +``` + +## Instrumentations to Check + +### 1. R2DBC (Reactive Relational Database Connectivity) +- **Location:** `instrumentation/r2dbc-1.0/` +- **Test command:** `./gradlew :instrumentation:r2dbc-1.0:javaagent:testStableSemconv --fail-fast` +- **Expected changes:** Similar to JDBC - update span name expectations and add `DB_QUERY_SUMMARY` assertions + +### 2. Hibernate +- **Location:** `instrumentation/hibernate/` +- **Test command:** `./gradlew :instrumentation:hibernate:hibernate-6.0:javaagent:testStableSemconv --fail-fast` +- **Note:** May generate SQL queries internally - check if tests validate SQL spans + +### 3. jOOQ +- **Location:** `instrumentation/jooq-3.0/` +- **Test command:** `./gradlew :instrumentation:jooq-3.0:javaagent:testStableSemconv --fail-fast` + +### 4. Spring Data +- **Location:** `instrumentation/spring/spring-data/` +- **Test command:** `./gradlew :instrumentation:spring:spring-data:spring-data-3.0:testing:testStableSemconv --fail-fast` + +### 5. MyBatis +- **Location:** `instrumentation/mybatis-3.2/` +- **Test command:** `./gradlew :instrumentation:mybatis-3.2:javaagent:testStableSemconv --fail-fast` + +### 6. Vertx SQL Client +- **Location:** `instrumentation/vertx/` +- **Test command:** `./gradlew :instrumentation:vertx:vertx-sql-client-4.0:javaagent:testStableSemconv --fail-fast` + +### 7. OpenTelemetry Extension Annotations (with SQL) +- **Location:** `instrumentation/opentelemetry-extension-annotations-1.0/` + +## Execution Steps + +1. **Run each test suite** with `--fail-fast` to identify failures +2. **For each failing test:** + - Add `querySummary()` helper if not present + - Update span name assertion to be conditional on `emitStableDatabaseSemconv()` + - Add `DB_QUERY_SUMMARY` attribute assertion +3. **Verify both modes pass:** + - `./gradlew ::test` (old semconv) + - `./gradlew ::testStableSemconv` (stable semconv) + +## Quick Discovery Command + +Find all `testStableSemconv` tasks: +```bash +./gradlew tasks --all | grep -i "testStableSemconv" +``` + +Or check which modules have stable semconv test configurations: +```bash +grep -r "testStableSemconv" --include="*.gradle.kts" instrumentation/ +``` + +## Priority Order + +1. **R2DBC** - Most similar to JDBC, likely same patterns +2. **Vertx SQL Client** - Direct SQL execution +3. **jOOQ** - SQL generation library +4. **Hibernate/Spring Data/MyBatis** - ORM layers (may just use underlying JDBC spans) + +## Notes + +- Some instrumentations may not have their own SQL span tests if they rely on JDBC instrumentation underneath +- Focus on instrumentations that directly parse/execute SQL and emit their own spans +- The `querySummary()` helper may need to be added to each test class (or a shared test utility) diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java index f1562bf494ac..22f7ec1a124d 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java @@ -102,7 +102,9 @@ protected String computeSpanNameStable( if (target == null) { target = getter.getDbNamespace(request); } - if (target == null) { + // Only use server address as target if we have an operation or meaningful target already + // Don't use server address alone as it makes a poor span name + if (target == null && operation != null) { String serverAddress = getter.getServerAddress(request); if (serverAddress != null) { Integer serverPort = getter.getServerPort(request); @@ -122,7 +124,7 @@ protected String computeSpanNameStable( return operation; } - // No operation - use target alone + // No operation - use target alone only if it's a meaningful target (not server address) if (target != null) { return target; } From 69592b0cf50f2a5b0963403301d0f4ed8761004c Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 8 Dec 2025 16:13:22 -0800 Subject: [PATCH 26/26] Use db.system.name as span name fallback per stable semconv Per the stable database semantic conventions, when neither operation nor target are available, the span name should be db.system.name. Updated the test to expect 'other_sql' (the db.system.name value) for stable semconv, while keeping 'DB Query' for old semconv to maintain backward compatibility. --- CI-PLAN.md | 10 +++++++--- .../semconv/db/DbClientSpanNameExtractor.java | 9 ++------- .../jdbc/testing/AbstractJdbcInstrumentationTest.java | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CI-PLAN.md b/CI-PLAN.md index 2942365d339e..cd7270a70ccb 100644 --- a/CI-PLAN.md +++ b/CI-PLAN.md @@ -33,8 +33,12 @@ Note: All jobs with parameters in parentheses (e.g., Java version, VM, indy sett - Fix: Same as above ## Notes -The failures are all related to the db.query.summary implementation. The span name is being set to "localhost" instead of "DB Query". This is happening in test cases that involve getClientInfo exceptions. +The failures were all related to the db.query.summary implementation. The span name was being set to "localhost" instead of "DB Query" when SQL could not be parsed. -The issue appears to be that when there's no actual SQL statement (e.g., during getClientInfo operations that fail), the query summary logic is returning the server name ("localhost") as the span name instead of falling back to "DB Query". +**Root cause**: When SQL statement cannot be parsed (e.g., "testing 123" which is not valid SQL), the sanitizer returns null for operation, collection, and querySummary. The span name logic was incorrectly falling back to server.address as the span name. -Root cause: The implementation needs to handle cases where there is no SQL statement and fall back to an appropriate default span name. +**Solution**: According to OpenTelemetry semantic conventions for database spans, `server.address` should only be used as part of the `{target}` when combined with an operation (e.g., "SELECT localhost"). When there is no operation, the fallback should be `db.system.name` or the default "DB Query" span name. + +**Fix applied**: Updated `DbClientSpanNameExtractor.computeSpanNameStable()` to only use server.address when an operation is available. When no operation exists, it now properly falls back to db.system.name or "DB Query". + +**Commit**: 11e7b49ed1 diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java index 22f7ec1a124d..34fe40403ee5 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java @@ -129,13 +129,8 @@ protected String computeSpanNameStable( return target; } - // Final fallback to db.system.name - String dbSystem = getter.getDbSystem(request); - if (dbSystem != null) { - return dbSystem; - } - - return DEFAULT_SPAN_NAME; + // Final fallback to db.system.name (required attribute per spec) + return getter.getDbSystem(request); } private static final class GenericDbClientSpanNameExtractor diff --git a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java index 82e0481f1f3e..792f96b736fc 100644 --- a/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java +++ b/instrumentation/jdbc/testing/src/main/java/io/opentelemetry/instrumentation/jdbc/testing/AbstractJdbcInstrumentationTest.java @@ -1220,7 +1220,7 @@ void testGetClientInfoException(String query) throws SQLException { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), span -> - span.hasName("DB Query") + span.hasName(emitStableDatabaseSemconv() ? "other_sql" : "DB Query") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly(