diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java index abf1485fd3c4..41cc5eda6662 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java @@ -34,6 +34,7 @@ public final class CommonConfig { private final Set knownHttpRequestMethods; private final EnduserConfig enduserConfig; private final boolean statementSanitizationEnabled; + private final boolean statementSanitizationAnsiQuotes; private final boolean sqlCommenterEnabled; private final boolean emitExperimentalHttpClientTelemetry; private final boolean emitExperimentalHttpServerTelemetry; @@ -88,6 +89,8 @@ public CommonConfig(InstrumentationConfig config) { new ArrayList<>(HttpConstants.KNOWN_METHODS))); statementSanitizationEnabled = config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true); + statementSanitizationAnsiQuotes = + config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.ansi-quotes", false); sqlCommenterEnabled = config.getBoolean( "otel.instrumentation.common.experimental.db-sqlcommenter.enabled", false); @@ -142,6 +145,10 @@ public boolean isStatementSanitizationEnabled() { return statementSanitizationEnabled; } + public boolean isStatementSanitizationAnsiQuotes() { + return statementSanitizationAnsiQuotes; + } + public boolean isSqlCommenterEnabled() { return sqlCommenterEnabled; } 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..9db78df3f61f 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 @@ -36,7 +36,21 @@ public static SpanNameExtractor create( */ public static SpanNameExtractor create( SqlClientAttributesGetter getter) { - return new SqlClientSpanNameExtractor<>(getter); + return create(getter, SqlDialect.DEFAULT); + } + + /** + * Returns a {@link SpanNameExtractor} that constructs the span name according to DB semantic + * conventions: {@code .}. + * + * @see SqlStatementInfo#getOperation() used to extract {@code }. + * @see DbClientAttributesGetter#getDbNamespace(Object) used to extract {@code }. + * @see SqlStatementInfo#getMainIdentifier() used to extract {@code } or stored + * procedure name. + */ + public static SpanNameExtractor create( + SqlClientAttributesGetter getter, SqlDialect sqlDialect) { + return new SqlClientSpanNameExtractor<>(getter, sqlDialect.ansiQuotes()); } private static final String DEFAULT_SPAN_NAME = "DB Query"; @@ -87,14 +101,19 @@ private static final class SqlClientSpanNameExtractor extends DbClientSpanNameExtractor { private final SqlClientAttributesGetter getter; + private final boolean ansiQuotes; - private SqlClientSpanNameExtractor(SqlClientAttributesGetter getter) { + private SqlClientSpanNameExtractor( + SqlClientAttributesGetter getter, boolean ansiQuotes) { this.getter = getter; + this.ansiQuotes = ansiQuotes; } @Override public String extract(REQUEST request) { String namespace = getter.getDbNamespace(request); + SqlDialect dialect = + SqlStatementSanitizerUtil.getDialect(getter.getDbSystem(request), ansiQuotes); Collection rawQueryTexts = getter.getRawQueryTexts(request); if (rawQueryTexts.isEmpty()) { @@ -106,14 +125,14 @@ public String extract(REQUEST request) { return computeSpanName(namespace, null, null); } SqlStatementInfo sanitizedStatement = - SqlStatementSanitizerUtil.sanitize(rawQueryTexts.iterator().next()); + SqlStatementSanitizerUtil.sanitize(rawQueryTexts.iterator().next(), dialect); return computeSpanName( namespace, sanitizedStatement.getOperation(), sanitizedStatement.getMainIdentifier()); } if (rawQueryTexts.size() == 1) { SqlStatementInfo sanitizedStatement = - SqlStatementSanitizerUtil.sanitize(rawQueryTexts.iterator().next()); + SqlStatementSanitizerUtil.sanitize(rawQueryTexts.iterator().next(), dialect); String operation = sanitizedStatement.getOperation(); if (isBatch(request)) { operation = "BATCH " + operation; @@ -121,7 +140,7 @@ public String extract(REQUEST request) { return computeSpanName(namespace, operation, sanitizedStatement.getMainIdentifier()); } - MultiQuery multiQuery = MultiQuery.analyze(rawQueryTexts, false); + MultiQuery multiQuery = MultiQuery.analyze(rawQueryTexts, dialect, false); 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..deaccbbd825e 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 @@ -24,12 +24,13 @@ private MultiQuery( } static MultiQuery analyze( - Collection rawQueryTexts, boolean statementSanitizationEnabled) { + Collection rawQueryTexts, SqlDialect dialect, boolean statementSanitizationEnabled) { UniqueValue uniqueMainIdentifier = new UniqueValue(); UniqueValue uniqueOperation = new UniqueValue(); Set uniqueStatements = new LinkedHashSet<>(); for (String rawQueryText : rawQueryTexts) { - SqlStatementInfo sanitizedStatement = SqlStatementSanitizerUtil.sanitize(rawQueryText); + SqlStatementInfo sanitizedStatement = + SqlStatementSanitizerUtil.sanitize(rawQueryText, dialect); String mainIdentifier = sanitizedStatement.getMainIdentifier(); uniqueMainIdentifier.set(mainIdentifier); String operation = sanitizedStatement.getOperation(); 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..016ed7cdf573 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 @@ -67,17 +67,20 @@ public static SqlClientAttributesExtractorBuilder oldSemconvTableAttribute; private final boolean statementSanitizationEnabled; private final boolean captureQueryParameters; + private final boolean statementSanitizationAnsiQuotes; SqlClientAttributesExtractor( SqlClientAttributesGetter getter, AttributeKey oldSemconvTableAttribute, boolean statementSanitizationEnabled, + boolean statementSanitizationAnsiQuotes, boolean captureQueryParameters) { this.getter = getter; this.oldSemconvTableAttribute = oldSemconvTableAttribute; // capturing query parameters disables statement sanitization this.statementSanitizationEnabled = !captureQueryParameters && statementSanitizationEnabled; this.captureQueryParameters = captureQueryParameters; + this.statementSanitizationAnsiQuotes = statementSanitizationAnsiQuotes; internalNetworkExtractor = new InternalNetworkAttributesExtractor<>(getter, true, false); serverAttributesExtractor = ServerAttributesExtractor.create(getter); } @@ -86,6 +89,9 @@ public static SqlClientAttributesExtractorBuilder rawQueryTexts = getter.getRawQueryTexts(request); + SqlDialect dialect = + SqlStatementSanitizerUtil.getDialect( + getter.getDbSystem(request), statementSanitizationAnsiQuotes); Long batchSize = getter.getBatchSize(request); boolean isBatch = batchSize != null && batchSize > 1; @@ -93,7 +99,8 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST if (SemconvStability.emitOldDatabaseSemconv()) { if (rawQueryTexts.size() == 1) { // for backcompat(?) String rawQueryText = rawQueryTexts.iterator().next(); - SqlStatementInfo sanitizedStatement = SqlStatementSanitizerUtil.sanitize(rawQueryText); + SqlStatementInfo sanitizedStatement = + SqlStatementSanitizerUtil.sanitize(rawQueryText, dialect); String operation = sanitizedStatement.getOperation(); internalSet( attributes, @@ -112,7 +119,8 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST } if (rawQueryTexts.size() == 1) { String rawQueryText = rawQueryTexts.iterator().next(); - SqlStatementInfo sanitizedStatement = SqlStatementSanitizerUtil.sanitize(rawQueryText); + SqlStatementInfo sanitizedStatement = + SqlStatementSanitizerUtil.sanitize(rawQueryText, dialect); String operation = sanitizedStatement.getOperation(); internalSet( attributes, @@ -124,7 +132,8 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST } } else if (rawQueryTexts.size() > 1) { MultiQuery multiQuery = - MultiQuery.analyze(getter.getRawQueryTexts(request), statementSanitizationEnabled); + MultiQuery.analyze( + getter.getRawQueryTexts(request), dialect, statementSanitizationEnabled); internalSet(attributes, DB_QUERY_TEXT, join("; ", multiQuery.getStatements())); String operation = diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorBuilder.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorBuilder.java index 7eefed9cf825..b66010fbcbb8 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorBuilder.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorBuilder.java @@ -20,6 +20,7 @@ public final class SqlClientAttributesExtractorBuilder { final SqlClientAttributesGetter getter; AttributeKey oldSemconvTableAttribute = DB_SQL_TABLE; boolean statementSanitizationEnabled = true; + boolean setStatementSanitizationAnsiQuotes = false; boolean captureQueryParameters = false; SqlClientAttributesExtractorBuilder(SqlClientAttributesGetter getter) { @@ -49,6 +50,19 @@ public SqlClientAttributesExtractorBuilder setStatementSaniti return this; } + /** + * Sets whether the SQL sanitizer should treat double-quoted fragments as string literals or + * identifiers. By default, double quotes are used for string literals. When the sanitizer is able + * to detect that the used database does not support double-quoted string literals then this flag + * will be automatically switched. + */ + @CanIgnoreReturnValue + public SqlClientAttributesExtractorBuilder + setSetStatementSanitizationAnsiQuotes(boolean setStatementSanitizationAnsiQuotes) { + this.setStatementSanitizationAnsiQuotes = setStatementSanitizationAnsiQuotes; + return this; + } + /** * Sets whether the query parameters should be captured as span attributes named {@code * db.query.parameter.}. Enabling this option disables the statement sanitization. Disabled @@ -70,6 +84,10 @@ public SqlClientAttributesExtractorBuilder setCaptureQueryPar */ public AttributesExtractor build() { return new SqlClientAttributesExtractor<>( - getter, oldSemconvTableAttribute, statementSanitizationEnabled, captureQueryParameters); + getter, + oldSemconvTableAttribute, + statementSanitizationEnabled, + setStatementSanitizationAnsiQuotes, + captureQueryParameters); } } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlDialect.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlDialect.java index 173e399379aa..b1061b28ef3f 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlDialect.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlDialect.java @@ -7,7 +7,26 @@ /** Enumeration of sql dialects that are handled differently by {@link SqlStatementSanitizer}. */ public enum SqlDialect { - DEFAULT, - // couchbase uses double quotes for string literals - COUCHBASE + DEFAULT(false), // double quotes for string literals + ANSI_QUOTES(true), // double quotes for identifiers, single quotes for string literals + + CASSANDRA(true), + CLICKHOUSE(true), + COUCHBASE(false), + GEODE(true), + INFLUXDB(true); + + private final boolean ansiQuotes; + + SqlDialect(boolean ansiQuotes) { + this.ansiQuotes = ansiQuotes; + } + + /** + * Returns {@code true} if the dialect uses double quotes for identifiers (ANSI SQL standard), + * {@code false} if double quotes are used for string literals. + */ + boolean ansiQuotes() { + return ansiQuotes; + } } 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..ac92f8fc105c 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 @@ -53,7 +53,7 @@ public SqlStatementInfo sanitize(@Nullable String statement, SqlDialect dialect) private static SqlStatementInfo sanitizeImpl(String statement, SqlDialect dialect) { supportability.incrementCounter(SQL_STATEMENT_SANITIZER_CACHE_MISS); - return AutoSqlSanitizer.sanitize(statement, dialect); + return AutoSqlSanitizer.sanitize(statement, dialect.ansiQuotes()); } // visible for tests diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerUtil.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerUtil.java index fc82d4d0b485..8556916d447a 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerUtil.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerUtil.java @@ -5,10 +5,15 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.db; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlStatementSanitizer.CacheKey; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.internal.InstrumenterContext; +import io.opentelemetry.semconv.DbAttributes; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** * Helper class for sanitizing sql that keeps sanitization results in {@link InstrumenterContext} so @@ -16,11 +21,29 @@ */ class SqlStatementSanitizerUtil { private static final SqlStatementSanitizer sanitizer = SqlStatementSanitizer.create(true); + private static final Set dbsWithAnsiQuotes = + new HashSet<>( + Arrays.asList( + DbAttributes.DbSystemNameValues.POSTGRESQL, + "oracle", + "h2", + "hsqldb", + "db2", + "derby", + "hanadb")); - static SqlStatementInfo sanitize(String queryText) { - Map map = + static SqlStatementInfo sanitize(String queryText, SqlDialect dialect) { + Map map = InstrumenterContext.computeIfAbsent("sanitized-sql-map", unused -> new HashMap<>()); - return map.computeIfAbsent(queryText, sanitizer::sanitize); + return map.computeIfAbsent( + CacheKey.create(queryText, dialect), + key -> sanitizer.sanitize(key.getStatement(), key.getDialect())); + } + + static SqlDialect getDialect(String dbSystem, boolean ansiQuotes) { + return ansiQuotes || dbsWithAnsiQuotes.contains(dbSystem) + ? SqlDialect.ANSI_QUOTES + : SqlDialect.DEFAULT; } private SqlStatementSanitizerUtil() {} diff --git a/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex b/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex index 83a59fc0abc9..0e01eda253db 100644 --- a/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex +++ b/instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex @@ -39,9 +39,9 @@ POSTGRE_PARAM_MARKER = "$"[0-9]* WHITESPACE = [ \t\r\n]+ %{ - static SqlStatementInfo sanitize(String statement, SqlDialect dialect) { + static SqlStatementInfo sanitize(String statement, boolean ansiQuotes) { AutoSqlSanitizer sanitizer = new AutoSqlSanitizer(new java.io.StringReader(statement)); - sanitizer.dialect = dialect; + sanitizer.ansiQuotes = ansiQuotes; try { while (!sanitizer.yyatEOF()) { int token = sanitizer.yylex(); @@ -114,7 +114,7 @@ WHITESPACE = [ \t\r\n]+ private boolean insideComment = false; private Operation operation = NoOp.INSTANCE; private boolean extractionDone = false; - private SqlDialect dialect; + private boolean ansiQuotes; // whether double quotes are used for quoting identifiers or string literals private void setOperation(Operation operation) { if (this.operation == NoOp.INSTANCE) { @@ -534,13 +534,13 @@ WHITESPACE = [ \t\r\n]+ } {DOUBLE_QUOTED_STR} { - if (dialect == SqlDialect.COUCHBASE) { - builder.append('?'); - } else { + if (ansiQuotes) { if (!insideComment && !extractionDone) { extractionDone = operation.handleIdentifier(); } appendCurrentFragment(); + } else { + builder.append('?'); } 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 7e296de36c7c..05321eacbd39 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 @@ -20,7 +20,8 @@ class SqlStatementSanitizerTest { @ParameterizedTest @MethodSource("sqlArgs") void sanitizeSql(String original, String expected) { - SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(original); + SqlStatementInfo result = + SqlStatementSanitizer.create(true).sanitize(original, SqlDialect.ANSI_QUOTES); assertThat(result.getFullStatement()).isEqualTo(expected); } @@ -35,7 +36,19 @@ void normalizeCouchbase(String original, String expected) { @ParameterizedTest @MethodSource("simplifyArgs") void simplifySql(String original, Function expectedFunction) { - SqlStatementInfo result = SqlStatementSanitizer.create(true).sanitize(original); + SqlStatementInfo result = + SqlStatementSanitizer.create(true).sanitize(original, SqlDialect.ANSI_QUOTES); + SqlStatementInfo expected = expectedFunction.apply(original); + assertThat(result.getFullStatement()).isEqualTo(expected.getFullStatement()); + assertThat(result.getOperation()).isEqualTo(expected.getOperation()); + assertThat(result.getMainIdentifier()).isEqualToIgnoringCase(expected.getMainIdentifier()); + } + + @ParameterizedTest + @MethodSource("simplifyDefaultArgs") + void simplifySqlDefault(String original, Function expectedFunction) { + SqlStatementInfo result = + SqlStatementSanitizer.create(true).sanitize(original, SqlDialect.DEFAULT); SqlStatementInfo expected = expectedFunction.apply(original); assertThat(result.getFullStatement()).isEqualTo(expected.getFullStatement()); assertThat(result.getOperation()).isEqualTo(expected.getOperation()); @@ -372,6 +385,19 @@ private static Stream simplifyArgs() { Arguments.of(null, expect(null, null))); } + private static Stream simplifyDefaultArgs() { + return Stream.of( + 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( + "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"))); + } + private static Stream ddlArgs() { return Stream.of( Arguments.of("CREATE TABLE `table`", expect("CREATE TABLE", "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 7aa8897b7911..c38419b78efc 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 @@ -60,14 +60,15 @@ public Collection getRawQueryTexts(Object request) { assertThat(spanNameExtractor.extract(null)).isEqualTo("SELECT test"); // verify that sanitized statement was cached, see SqlStatementSanitizerUtil assertThat(InstrumenterContext.get()).containsKey("sanitized-sql-map"); - Map sanitizedMap = - (Map) InstrumenterContext.get().get("sanitized-sql-map"); - assertThat(sanitizedMap).containsKey(testQuery); + Map sanitizedMap = + (Map) InstrumenterContext.get().get("sanitized-sql-map"); + assertThat(sanitizedMap).hasSize(1); + Object key = sanitizedMap.keySet().iterator().next(); + assertThat(key.toString()).contains(testQuery); // replace cached sanitization result to verify it is used sanitizedMap.put( - testQuery, - SqlStatementInfo.create("SELECT name2 FROM test2 WHERE id = ?", "SELECT", "test2")); + key, SqlStatementInfo.create("SELECT name2 FROM test2 WHERE id = ?", "SELECT", "test2")); { AttributesBuilder builder = Attributes.builder(); attributesExtractor.onStart(builder, Context.root(), null); 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..07f6e594bfd5 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 @@ -23,6 +23,8 @@ package io.opentelemetry.javaagent.instrumentation.apachecamel.decorators; +import static io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlDialect.CASSANDRA; + import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlStatementSanitizer; import io.opentelemetry.instrumentation.api.internal.SemconvStability; @@ -72,7 +74,7 @@ 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(), CASSANDRA).getFullStatement(); } return null; case "jdbc": diff --git a/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraSingletons.java b/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraSingletons.java index 12bbc464d562..2441b7d508f1 100644 --- a/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraSingletons.java +++ b/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraSingletons.java @@ -10,6 +10,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlDialect; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; @@ -30,7 +31,7 @@ public final class CassandraSingletons { Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, - DbClientSpanNameExtractor.create(attributesGetter)) + DbClientSpanNameExtractor.create(attributesGetter, SqlDialect.CASSANDRA)) .addAttributesExtractor( SqlClientAttributesExtractor.builder(attributesGetter) .setTableAttribute(DbIncubatingAttributes.DB_CASSANDRA_TABLE) diff --git a/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraSingletons.java b/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraSingletons.java index cb59a22ccc54..12c49546288d 100644 --- a/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraSingletons.java +++ b/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraSingletons.java @@ -10,6 +10,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlDialect; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; @@ -29,7 +30,7 @@ public final class CassandraSingletons { Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, - DbClientSpanNameExtractor.create(attributesGetter)) + DbClientSpanNameExtractor.create(attributesGetter, SqlDialect.CASSANDRA)) .addAttributesExtractor( SqlClientAttributesExtractor.builder(attributesGetter) .setTableAttribute(DbIncubatingAttributes.DB_CASSANDRA_TABLE) diff --git a/instrumentation/cassandra/cassandra-4.4/library/src/main/java/io/opentelemetry/instrumentation/cassandra/v4_4/CassandraTelemetryBuilder.java b/instrumentation/cassandra/cassandra-4.4/library/src/main/java/io/opentelemetry/instrumentation/cassandra/v4_4/CassandraTelemetryBuilder.java index d43ac21ce29f..0d98a83a496a 100644 --- a/instrumentation/cassandra/cassandra-4.4/library/src/main/java/io/opentelemetry/instrumentation/cassandra/v4_4/CassandraTelemetryBuilder.java +++ b/instrumentation/cassandra/cassandra-4.4/library/src/main/java/io/opentelemetry/instrumentation/cassandra/v4_4/CassandraTelemetryBuilder.java @@ -12,6 +12,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlDialect; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; @@ -56,7 +57,9 @@ protected Instrumenter createInstrumenter( CassandraSqlAttributesGetter attributesGetter = new CassandraSqlAttributesGetter(); return Instrumenter.builder( - openTelemetry, INSTRUMENTATION_NAME, DbClientSpanNameExtractor.create(attributesGetter)) + openTelemetry, + INSTRUMENTATION_NAME, + DbClientSpanNameExtractor.create(attributesGetter, SqlDialect.CASSANDRA)) .addAttributesExtractor( SqlClientAttributesExtractor.builder(attributesGetter) .setTableAttribute(DB_CASSANDRA_TABLE) diff --git a/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseDbRequest.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseDbRequest.java index 35753981cb9b..f7ddaa8e0237 100644 --- a/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseDbRequest.java +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseDbRequest.java @@ -5,6 +5,8 @@ package io.opentelemetry.javaagent.instrumentation.clickhouse.common; +import static io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlDialect.CLICKHOUSE; + import com.google.auto.value.AutoValue; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlStatementInfo; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlStatementSanitizer; @@ -19,7 +21,8 @@ public abstract class ClickHouseDbRequest { public static ClickHouseDbRequest create( @Nullable String host, @Nullable Integer port, @Nullable String dbName, String sql) { - return new AutoValue_ClickHouseDbRequest(host, port, dbName, sanitizer.sanitize(sql)); + return new AutoValue_ClickHouseDbRequest( + host, port, dbName, sanitizer.sanitize(sql, CLICKHOUSE)); } @Nullable 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..8836e0b45bfd 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 @@ -5,6 +5,8 @@ package io.opentelemetry.javaagent.instrumentation.geode; +import static io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlDialect.GEODE; + import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlStatementSanitizer; import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; @@ -32,7 +34,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(), GEODE).getFullStatement(); } @Override diff --git a/instrumentation/influxdb-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbRequest.java b/instrumentation/influxdb-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbRequest.java index 560993c266d4..1325ff030e53 100644 --- a/instrumentation/influxdb-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbRequest.java +++ b/instrumentation/influxdb-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbRequest.java @@ -5,6 +5,8 @@ package io.opentelemetry.javaagent.instrumentation.influxdb.v2_4; +import static io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlDialect.INFLUXDB; + import com.google.auto.value.AutoValue; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlStatementInfo; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlStatementSanitizer; @@ -19,7 +21,8 @@ public abstract class InfluxDbRequest { public static InfluxDbRequest create( String host, int port, String dbName, String operation, String sql) { - return new AutoValue_InfluxDbRequest(host, port, dbName, operation, sanitizer.sanitize(sql)); + return new AutoValue_InfluxDbRequest( + host, port, dbName, operation, sanitizer.sanitize(sql, INFLUXDB)); } public abstract String getHost(); diff --git a/instrumentation/jdbc/README.md b/instrumentation/jdbc/README.md index e95fcced9c00..8f7e7d5e07c5 100644 --- a/instrumentation/jdbc/README.md +++ b/instrumentation/jdbc/README.md @@ -3,6 +3,7 @@ | System property | Type | Default | Description | |-------------------------------------------------------------------|---------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `otel.instrumentation.jdbc.statement-sanitizer.enabled` | Boolean | `true` | Enables the DB statement sanitization. | +| `otel.instrumentation.jdbc.statement-sanitizer.ansi-quotes` | Boolean | `false` | Sets whether the SQL sanitizer should treat double-quoted fragments as string literals or identifiers. By default, double quotes are used for string literals. When the sanitizer is able to detect that the used database does not support double-quoted string literals then this flag will be automatically switched. | | `otel.instrumentation.jdbc.experimental.capture-query-parameters` | Boolean | `false` | Enable the capture of query parameters as span attributes. Enabling this option disables the statement sanitization.

WARNING: captured query parameters may contain sensitive information such as passwords, personally identifiable information or protected health info. | | `otel.instrumentation.jdbc.experimental.transaction.enabled` | Boolean | `false` | Enables experimental instrumentation to create spans for COMMIT and ROLLBACK operations. | | `otel.instrumentation.jdbc.experimental.sqlcommenter.enabled` | Boolean | `false` | Enables augmenting queries with a comment containing the tracing information. See [sqlcommenter](https://google.github.io/sqlcommenter/) for more info. WARNING: augmenting queries with tracing context will make query texts unique, which may have adverse impact on database performance. Consult with database experts before enabling. | diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java index 30bfbfffc9d5..47a8e650d2e9 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java +++ b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java @@ -54,6 +54,10 @@ public final class JdbcSingletons { .getBoolean( "otel.instrumentation.jdbc.statement-sanitizer.enabled", AgentCommonConfig.get().isStatementSanitizationEnabled()), + AgentInstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.jdbc.statement-sanitizer.ansi-quotes", + AgentCommonConfig.get().isStatementSanitizationAnsiQuotes()), CAPTURE_QUERY_PARAMETERS); TRANSACTION_INSTRUMENTER = diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryBuilder.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryBuilder.java index 4ca2dbd9158c..f9cca62f18f5 100644 --- a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryBuilder.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/datasource/JdbcTelemetryBuilder.java @@ -25,6 +25,7 @@ public final class JdbcTelemetryBuilder { private boolean statementSanitizationEnabled = true; private boolean transactionInstrumenterEnabled = false; private boolean captureQueryParameters = false; + private boolean statementSanitizationAnsiQuotes = false; private final SqlCommenterBuilder sqlCommenterBuilder = SqlCommenter.builder(); static { @@ -56,6 +57,19 @@ public JdbcTelemetryBuilder setStatementSanitizationEnabled(boolean enabled) { return this; } + /** + * Sets whether the SQL sanitizer should treat double-quoted fragments as string literals or + * identifiers. By default, double quotes are used for string literals. When the sanitizer is able + * to detect that the used database does not support double-quoted string literals then this flag + * will be automatically switched. + */ + @CanIgnoreReturnValue + public JdbcTelemetryBuilder setStatementSanitizationAnsiQuotes( + boolean statementSanitizationAnsiQuotes) { + this.statementSanitizationAnsiQuotes = statementSanitizationAnsiQuotes; + return this; + } + /** Configures whether spans are created for JDBC Transactions. Disabled by default. */ @CanIgnoreReturnValue public JdbcTelemetryBuilder setTransactionInstrumenterEnabled(boolean enabled) { @@ -86,6 +100,7 @@ public JdbcTelemetry build() { openTelemetry, statementInstrumenterEnabled, statementSanitizationEnabled, + statementSanitizationAnsiQuotes, captureQueryParameters); Instrumenter transactionInstrumenter = JdbcInstrumenterFactory.createTransactionInstrumenter( diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcInstrumenterFactory.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcInstrumenterFactory.java index 9748181d713e..4a6819665e2f 100644 --- a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcInstrumenterFactory.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcInstrumenterFactory.java @@ -13,6 +13,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlDialect; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; @@ -46,6 +47,8 @@ static Instrumenter createStatementInstrumenter( true, ConfigPropertiesUtil.getBoolean( "otel.instrumentation.common.db-statement-sanitizer.enabled", true), + ConfigPropertiesUtil.getBoolean( + "otel.instrumentation.common.db-statement-sanitizer.ansi-quotes", false), captureQueryParameters); } @@ -53,9 +56,15 @@ public static Instrumenter createStatementInstrumenter( OpenTelemetry openTelemetry, boolean enabled, boolean statementSanitizationEnabled, + boolean statementSanitizationAnsiQuotes, boolean captureQueryParameters) { return createStatementInstrumenter( - openTelemetry, emptyList(), enabled, statementSanitizationEnabled, captureQueryParameters); + openTelemetry, + emptyList(), + enabled, + statementSanitizationEnabled, + statementSanitizationAnsiQuotes, + captureQueryParameters); } public static Instrumenter createStatementInstrumenter( @@ -63,14 +72,18 @@ public static Instrumenter createStatementInstrumenter( List> extractors, boolean enabled, boolean statementSanitizationEnabled, + boolean statementSanitizationAnsiQuotes, boolean captureQueryParameters) { return Instrumenter.builder( openTelemetry, INSTRUMENTATION_NAME, - DbClientSpanNameExtractor.create(JdbcAttributesGetter.INSTANCE)) + DbClientSpanNameExtractor.create( + JdbcAttributesGetter.INSTANCE, + statementSanitizationAnsiQuotes ? SqlDialect.ANSI_QUOTES : SqlDialect.DEFAULT)) .addAttributesExtractor( SqlClientAttributesExtractor.builder(JdbcAttributesGetter.INSTANCE) .setStatementSanitizationEnabled(statementSanitizationEnabled) + .setSetStatementSanitizationAnsiQuotes(statementSanitizationAnsiQuotes) .setCaptureQueryParameters(captureQueryParameters) .build()) .addAttributesExtractors(extractors) diff --git a/instrumentation/r2dbc-1.0/README.md b/instrumentation/r2dbc-1.0/README.md index 0dff7bc88db9..6ac5e34b0377 100644 --- a/instrumentation/r2dbc-1.0/README.md +++ b/instrumentation/r2dbc-1.0/README.md @@ -3,4 +3,5 @@ | System property | Type | Default | Description | |----------------------------------------------------------------|---------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `otel.instrumentation.r2dbc.statement-sanitizer.enabled` | Boolean | `true` | Enables the DB statement sanitization. | +| `otel.instrumentation.r2dbc.statement-sanitizer.ansi-quotes` | Boolean | `false` | Sets whether the SQL sanitizer should treat double-quoted fragments as string literals or identifiers. By default, double quotes are used for string literals. When the sanitizer is able to detect that the used database does not support double-quoted string literals then this flag will be automatically switched. | | `otel.instrumentation.r2dbc.experimental.sqlcommenter.enabled` | Boolean | `false` | Enables augmenting queries with a comment containing the tracing information. See [sqlcommenter](https://google.github.io/sqlcommenter/) for more info. WARNING: augmenting queries with tracing context will make query texts unique, which may have adverse impact on database performance. Consult with database experts before enabling. | diff --git a/instrumentation/r2dbc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/r2dbc/v1_0/R2dbcSingletons.java b/instrumentation/r2dbc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/r2dbc/v1_0/R2dbcSingletons.java index 3ac92e48a5f8..a6f663da75de 100644 --- a/instrumentation/r2dbc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/r2dbc/v1_0/R2dbcSingletons.java +++ b/instrumentation/r2dbc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/r2dbc/v1_0/R2dbcSingletons.java @@ -27,6 +27,11 @@ public final class R2dbcSingletons { .getBoolean( "otel.instrumentation.r2dbc.statement-sanitizer.enabled", AgentCommonConfig.get().isStatementSanitizationEnabled())) + .setStatementSanitizationAnsiQuotes( + AgentInstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.r2dbc.statement-sanitizer.ansi-quotes", + AgentCommonConfig.get().isStatementSanitizationAnsiQuotes())) .addAttributesExtractor( PeerServiceAttributesExtractor.create( R2dbcSqlAttributesGetter.INSTANCE, diff --git a/instrumentation/r2dbc-1.0/library/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/R2dbcTelemetryBuilder.java b/instrumentation/r2dbc-1.0/library/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/R2dbcTelemetryBuilder.java index a7567ac76345..5fe47f49032a 100644 --- a/instrumentation/r2dbc-1.0/library/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/R2dbcTelemetryBuilder.java +++ b/instrumentation/r2dbc-1.0/library/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/R2dbcTelemetryBuilder.java @@ -21,6 +21,7 @@ public final class R2dbcTelemetryBuilder { private final R2dbcInstrumenterBuilder instrumenterBuilder; private boolean statementSanitizationEnabled = true; + private boolean statementSanitizationAnsiQuotes; private UnaryOperator> spanNameExtractorCustomizer = UnaryOperator.identity(); private final SqlCommenterBuilder sqlCommenterBuilder = SqlCommenter.builder(); @@ -51,6 +52,19 @@ public R2dbcTelemetryBuilder setStatementSanitizationEnabled(boolean enabled) { return this; } + /** + * Sets whether the SQL sanitizer should treat double-quoted fragments as string literals or + * identifiers. By default, double quotes are used for string literals. When the sanitizer is able + * to detect that the used database does not support double-quoted string literals then this flag + * will be automatically switched. + */ + @CanIgnoreReturnValue + public R2dbcTelemetryBuilder setStatementSanitizationAnsiQuotes( + boolean statementSanitizationAnsiQuotes) { + this.statementSanitizationAnsiQuotes = statementSanitizationAnsiQuotes; + return this; + } + /** * Sets custom {@link SpanNameExtractor} via transform function. * @@ -79,7 +93,10 @@ public R2dbcTelemetryBuilder setSpanNameExtractorCustomizer( */ public R2dbcTelemetry build() { return new R2dbcTelemetry( - instrumenterBuilder.build(spanNameExtractorCustomizer, statementSanitizationEnabled), + instrumenterBuilder.build( + spanNameExtractorCustomizer, + statementSanitizationEnabled, + statementSanitizationAnsiQuotes), sqlCommenterBuilder.build()); } } diff --git a/instrumentation/r2dbc-1.0/library/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/internal/R2dbcInstrumenterBuilder.java b/instrumentation/r2dbc-1.0/library/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/internal/R2dbcInstrumenterBuilder.java index 6c5354efbf95..09e5349b0a43 100644 --- a/instrumentation/r2dbc-1.0/library/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/internal/R2dbcInstrumenterBuilder.java +++ b/instrumentation/r2dbc-1.0/library/src/main/java/io/opentelemetry/instrumentation/r2dbc/v1_0/internal/R2dbcInstrumenterBuilder.java @@ -10,6 +10,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlDialect; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; @@ -44,16 +45,20 @@ public R2dbcInstrumenterBuilder addAttributesExtractor( public Instrumenter build( UnaryOperator> spanNameExtractorTransformer, - boolean statementSanitizationEnabled) { + boolean statementSanitizationEnabled, + boolean ansiQuotes) { SpanNameExtractor spanNameExtractor = spanNameExtractorTransformer.apply( - DbClientSpanNameExtractor.create(R2dbcSqlAttributesGetter.INSTANCE)); + DbClientSpanNameExtractor.create( + R2dbcSqlAttributesGetter.INSTANCE, + ansiQuotes ? SqlDialect.ANSI_QUOTES : SqlDialect.DEFAULT)); return Instrumenter.builder( openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor) .addAttributesExtractor( SqlClientAttributesExtractor.builder(R2dbcSqlAttributesGetter.INSTANCE) .setStatementSanitizationEnabled(statementSanitizationEnabled) + .setSetStatementSanitizationAnsiQuotes(ansiQuotes) .build()) .addAttributesExtractors(additionalExtractors) .addOperationMetrics(DbClientMetrics.get()) diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java index 41e8b6809492..dbf84d9d91a6 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java @@ -67,6 +67,9 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { .setStatementSanitizationEnabled( InstrumentationConfigUtil.isStatementSanitizationEnabled( config, "otel.instrumentation.jdbc.statement-sanitizer.enabled")) + .setStatementSanitizationAnsiQuotes( + InstrumentationConfigUtil.isStatementSanitizationEnabled( + config, "otel.instrumentation.jdbc.statement-sanitizer.ansi-quotes")) .setCaptureQueryParameters( config.getBoolean( "otel.instrumentation.jdbc.experimental.capture-query-parameters", false)) diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java index 09849deef13b..abfe313b3d21 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java @@ -37,6 +37,10 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { InstrumentationConfigUtil.isStatementSanitizationEnabled( configProvider.getObject(), "otel.instrumentation.r2dbc.statement-sanitizer.enabled")) + .setStatementSanitizationAnsiQuotes( + InstrumentationConfigUtil.isStatementSanitizationEnabled( + configProvider.getObject(), + "otel.instrumentation.r2dbc.statement-sanitizer.ansi-quotes")) .build() .wrapConnectionFactory(connectionFactory, getConnectionFactoryOptions(connectionFactory)); } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java index 683529a18b29..6d41c9b87adc 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java @@ -45,4 +45,11 @@ public static boolean isStatementSanitizationEnabled(InstrumentationConfig confi return config.getBoolean( key, config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true)); } + + public static boolean isStatementSanitizationAnsiQuotes( + InstrumentationConfig config, String key) { + return config.getBoolean( + key, + config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.ansi-quotes", false)); + } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index a0365fa167a0..dfb3fc0c2737 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -284,6 +284,12 @@ "description": "Enables the DB statement sanitization.", "defaultValue": true }, + { + "name": "otel.instrumentation.common.db-statement-sanitizer.ansi-quotes", + "type": "java.lang.Boolean", + "description": "Sets whether the SQL sanitizer should treat double-quoted fragments as string literals or identifiers. By default, double quotes are used for string literals. When the sanitizer is able to detect that the used database does not support double-quoted string literals then this flag will be automatically switched.", + "defaultValue": false + }, { "name": "otel.instrumentation.common.default-enabled", "type": "java.lang.Boolean", @@ -357,6 +363,12 @@ "description": "Enables the DB statement sanitization.", "defaultValue": true }, + { + "name": "otel.instrumentation.jdbc.statement-sanitizer.ansi-quotes", + "type": "java.lang.Boolean", + "description": "Sets whether the SQL sanitizer should treat double-quoted fragments as string literals or identifiers. By default, double quotes are used for string literals. When the sanitizer is able to detect that the used database does not support double-quoted string literals then this flag will be automatically switched.", + "defaultValue": false + }, { "name": "otel.instrumentation.jdbc.experimental.capture-query-parameters", "type": "java.lang.Boolean", @@ -518,6 +530,12 @@ "description": "Enables the DB statement sanitization.", "defaultValue": true }, + { + "name": "otel.instrumentation.r2dbc.statement-sanitizer.ansi-quotes", + "type": "java.lang.Boolean", + "description": "Sets whether the SQL sanitizer should treat double-quoted fragments as string literals or identifiers. By default, double quotes are used for string literals. When the sanitizer is able to detect that the used database does not support double-quoted string literals then this flag will be automatically switched.", + "defaultValue": false + }, { "name": "otel.instrumentation.runtime-telemetry.capture-gc-cause", "type": "java.lang.Boolean", diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/sql/VertxSqlInstrumenterFactory.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/sql/VertxSqlInstrumenterFactory.java index d21b69444cd3..3668978211bb 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/sql/VertxSqlInstrumenterFactory.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/sql/VertxSqlInstrumenterFactory.java @@ -9,6 +9,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlDialect; import io.opentelemetry.instrumentation.api.incubator.semconv.net.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; @@ -21,8 +22,11 @@ public final class VertxSqlInstrumenterFactory { public static Instrumenter createInstrumenter( String instrumentationName) { + boolean ansiQuotes = AgentCommonConfig.get().isStatementSanitizationAnsiQuotes(); SpanNameExtractor spanNameExtractor = - DbClientSpanNameExtractor.create(VertxSqlClientAttributesGetter.INSTANCE); + DbClientSpanNameExtractor.create( + VertxSqlClientAttributesGetter.INSTANCE, + ansiQuotes ? SqlDialect.ANSI_QUOTES : SqlDialect.DEFAULT); InstrumenterBuilder builder = Instrumenter.builder( @@ -31,6 +35,7 @@ public static Instrumenter createInstrumenter( SqlClientAttributesExtractor.builder(VertxSqlClientAttributesGetter.INSTANCE) .setStatementSanitizationEnabled( AgentCommonConfig.get().isStatementSanitizationEnabled()) + .setSetStatementSanitizationAnsiQuotes(ansiQuotes) .build()) .addAttributesExtractor( ServerAttributesExtractor.create(VertxSqlClientNetAttributesGetter.INSTANCE))