Explorar o código

InstrumentationConfig part 4: DB sanitization (#6317)

* InstrumentationConfig part 4: DB sanitization

* fix couchbase unit tests

* code review comments

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
Mateusz Rzeszutek %!s(int64=2) %!d(string=hai) anos
pai
achega
8d1ba17d29
Modificáronse 28 ficheiros con 278 adicións e 190 borrados
  1. 0 18
      instrumentation-api-semconv/build.gradle.kts
  2. 89 85
      instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizer.java
  3. 13 6
      instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizer.java
  4. 0 21
      instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/StatementSanitizationConfig.java
  5. 4 2
      instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/DbClientSpanNameExtractor.java
  6. 6 3
      instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractor.java
  7. 15 1
      instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractorBuilder.java
  8. 3 3
      instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizerTest.groovy
  9. 9 9
      instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizerTest.groovy
  10. 0 18
      instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/db/StatementSanitizationConfigTest.java
  11. 7 3
      instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java
  12. 3 0
      instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraSingletons.java
  13. 3 0
      instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraSingletons.java
  14. 1 0
      instrumentation/couchbase/couchbase-2-common/javaagent-unit-tests/build.gradle.kts
  15. 6 1
      instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizer.java
  16. 5 1
      instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeDbAttributesGetter.java
  17. 5 1
      instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/OperationNameUtil.java
  18. 5 1
      instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java
  19. 7 1
      instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcSingletons.java
  20. 5 1
      instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisDbAttributesGetter.java
  21. 5 1
      instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisRequest.java
  22. 5 1
      instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisRequest.java
  23. 5 1
      instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceDbAttributesGetter.java
  24. 13 3
      instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetry.java
  25. 39 0
      instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetryBuilder.java
  26. 13 8
      instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/OpenTelemetryTracing.java
  27. 5 1
      instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonRequest.java
  28. 7 0
      javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/CommonConfig.java

+ 0 - 18
instrumentation-api-semconv/build.gradle.kts

@@ -38,22 +38,4 @@ tasks {
   sourcesJar {
     dependsOn("generateJflex")
   }
-
-  val testStatementSanitizerConfig by registering(Test::class) {
-    filter {
-      includeTestsMatching("StatementSanitizationConfigTest")
-    }
-    include("**/StatementSanitizationConfigTest.*")
-    jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=false")
-  }
-
-  test {
-    filter {
-      excludeTestsMatching("StatementSanitizationConfigTest")
-    }
-  }
-
-  check {
-    dependsOn(testStatementSanitizerConfig)
-  }
 }

+ 89 - 85
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizer.java

@@ -8,10 +8,6 @@ package io.opentelemetry.instrumentation.api.db;
 import static java.util.Arrays.asList;
 import static java.util.Collections.unmodifiableMap;
 
-import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.CommandAndNumArgs;
-import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.Eval;
-import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.KeepAllArgs;
-import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.MultiKeyValue;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.List;
@@ -353,8 +349,18 @@ public final class RedisCommandSanitizer {
     SANITIZERS = unmodifiableMap(sanitizers);
   }
 
-  public static String sanitize(String command, List<?> args) {
-    if (!StatementSanitizationConfig.isStatementSanitizationEnabled()) {
+  public static RedisCommandSanitizer create(boolean statementSanitizationEnabled) {
+    return new RedisCommandSanitizer(statementSanitizationEnabled);
+  }
+
+  private final boolean statementSanitizationEnabled;
+
+  private RedisCommandSanitizer(boolean statementSanitizationEnabled) {
+    this.statementSanitizationEnabled = statementSanitizationEnabled;
+  }
+
+  public String sanitize(String command, List<?> args) {
+    if (!statementSanitizationEnabled) {
       return KeepAllArgs.INSTANCE.sanitize(command, args);
     }
     return SANITIZERS
@@ -362,107 +368,105 @@ public final class RedisCommandSanitizer {
         .sanitize(command, args);
   }
 
-  public interface CommandSanitizer {
+  interface CommandSanitizer {
     String sanitize(String command, List<?> args);
+  }
 
-    static String argToString(Object arg) {
-      if (arg instanceof byte[]) {
-        return new String((byte[]) arg, StandardCharsets.UTF_8);
-      } else {
-        return String.valueOf(arg);
+  enum KeepAllArgs implements CommandSanitizer {
+    INSTANCE;
+
+    @Override
+    public String sanitize(String command, List<?> args) {
+      StringBuilder sanitized = new StringBuilder(command);
+      for (Object arg : args) {
+        sanitized.append(" ").append(argToString(arg));
       }
+      return sanitized.toString();
     }
+  }
 
-    enum KeepAllArgs implements CommandSanitizer {
-      INSTANCE;
+  // keeps only a chosen number of arguments
+  // example for num=2: CMD arg1 arg2 ? ?
+  static final class CommandAndNumArgs implements CommandSanitizer {
+    private final int numOfArgsToKeep;
 
-      @Override
-      public String sanitize(String command, List<?> args) {
-        StringBuilder sanitized = new StringBuilder(command);
-        for (Object arg : args) {
-          sanitized.append(" ").append(argToString(arg));
-        }
-        return sanitized.toString();
-      }
+    CommandAndNumArgs(int numOfArgsToKeep) {
+      this.numOfArgsToKeep = numOfArgsToKeep;
     }
 
-    // keeps only a chosen number of arguments
-    // example for num=2: CMD arg1 arg2 ? ?
-    class CommandAndNumArgs implements CommandSanitizer {
-      private final int numOfArgsToKeep;
-
-      public CommandAndNumArgs(int numOfArgsToKeep) {
-        this.numOfArgsToKeep = numOfArgsToKeep;
+    @Override
+    public String sanitize(String command, List<?> args) {
+      StringBuilder sanitized = new StringBuilder(command);
+      for (int i = 0; i < numOfArgsToKeep && i < args.size(); ++i) {
+        sanitized.append(" ").append(argToString(args.get(i)));
       }
-
-      @Override
-      public String sanitize(String command, List<?> args) {
-        StringBuilder sanitized = new StringBuilder(command);
-        for (int i = 0; i < numOfArgsToKeep && i < args.size(); ++i) {
-          sanitized.append(" ").append(argToString(args.get(i)));
-        }
-        for (int i = numOfArgsToKeep; i < args.size(); ++i) {
-          sanitized.append(" ?");
-        }
-        return sanitized.toString();
+      for (int i = numOfArgsToKeep; i < args.size(); ++i) {
+        sanitized.append(" ?");
       }
+      return sanitized.toString();
     }
+  }
 
-    // keeps only chosen number of arguments and then every second one
-    // example for num=2: CMD arg1 arg2 key1 ? key2 ?
-    class MultiKeyValue implements CommandSanitizer {
-      private final int numOfArgsBeforeKeyValue;
+  // keeps only chosen number of arguments and then every second one
+  // example for num=2: CMD arg1 arg2 key1 ? key2 ?
+  static final class MultiKeyValue implements CommandSanitizer {
+    private final int numOfArgsBeforeKeyValue;
 
-      public MultiKeyValue(int numOfArgsBeforeKeyValue) {
-        this.numOfArgsBeforeKeyValue = numOfArgsBeforeKeyValue;
-      }
+    MultiKeyValue(int numOfArgsBeforeKeyValue) {
+      this.numOfArgsBeforeKeyValue = numOfArgsBeforeKeyValue;
+    }
 
-      @Override
-      public String sanitize(String command, List<?> args) {
-        StringBuilder sanitized = new StringBuilder(command);
-        // append all "initial" arguments before key-value pairs start
-        for (int i = 0; i < numOfArgsBeforeKeyValue && i < args.size(); ++i) {
-          sanitized.append(" ").append(argToString(args.get(i)));
-        }
+    @Override
+    public String sanitize(String command, List<?> args) {
+      StringBuilder sanitized = new StringBuilder(command);
+      // append all "initial" arguments before key-value pairs start
+      for (int i = 0; i < numOfArgsBeforeKeyValue && i < args.size(); ++i) {
+        sanitized.append(" ").append(argToString(args.get(i)));
+      }
 
-        // loop over keys only
-        for (int i = numOfArgsBeforeKeyValue; i < args.size(); i += 2) {
-          sanitized.append(" ").append(argToString(args.get(i))).append(" ?");
-        }
-        return sanitized.toString();
+      // loop over keys only
+      for (int i = numOfArgsBeforeKeyValue; i < args.size(); i += 2) {
+        sanitized.append(" ").append(argToString(args.get(i))).append(" ?");
       }
+      return sanitized.toString();
     }
+  }
 
-    enum Eval implements CommandSanitizer {
-      INSTANCE;
-
-      @Override
-      public String sanitize(String command, List<?> args) {
-        StringBuilder sanitized = new StringBuilder(command);
-
-        // get the number of keys passed from the command itself (second arg)
-        int numberOfKeys = 0;
-        if (args.size() > 2) {
-          try {
-            numberOfKeys = Integer.parseInt(argToString(args.get(1)));
-          } catch (NumberFormatException ignored) {
-            // Ignore
-          }
-        }
+  enum Eval implements CommandSanitizer {
+    INSTANCE;
 
-        int i = 0;
-        // log the script, number of keys and all keys
-        for (; i < (numberOfKeys + 2) && i < args.size(); ++i) {
-          sanitized.append(" ").append(argToString(args.get(i)));
-        }
-        // mask the rest
-        for (; i < args.size(); ++i) {
-          sanitized.append(" ?");
+    @Override
+    public String sanitize(String command, List<?> args) {
+      StringBuilder sanitized = new StringBuilder(command);
+
+      // get the number of keys passed from the command itself (second arg)
+      int numberOfKeys = 0;
+      if (args.size() > 2) {
+        try {
+          numberOfKeys = Integer.parseInt(argToString(args.get(1)));
+        } catch (NumberFormatException ignored) {
+          // Ignore
         }
-        return sanitized.toString();
       }
+
+      int i = 0;
+      // log the script, number of keys and all keys
+      for (; i < (numberOfKeys + 2) && i < args.size(); ++i) {
+        sanitized.append(" ").append(argToString(args.get(i)));
+      }
+      // mask the rest
+      for (; i < args.size(); ++i) {
+        sanitized.append(" ?");
+      }
+      return sanitized.toString();
     }
   }
 
-  private RedisCommandSanitizer() {}
+  static String argToString(Object arg) {
+    if (arg instanceof byte[]) {
+      return new String((byte[]) arg, StandardCharsets.UTF_8);
+    } else {
+      return String.valueOf(arg);
+    }
+  }
 }

+ 13 - 6
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizer.java

@@ -5,7 +5,6 @@
 
 package io.opentelemetry.instrumentation.api.db;
 
-import static io.opentelemetry.instrumentation.api.db.StatementSanitizationConfig.isStatementSanitizationEnabled;
 import static io.opentelemetry.instrumentation.api.internal.SupportabilityMetrics.CounterNames.SQL_STATEMENT_SANITIZER_CACHE_MISS;
 
 import com.google.auto.value.AutoValue;
@@ -23,12 +22,22 @@ public final class SqlStatementSanitizer {
   private static final Cache<CacheKey, SqlStatementInfo> sqlToStatementInfoCache =
       Cache.bounded(1000);
 
-  public static SqlStatementInfo sanitize(@Nullable String statement) {
+  public static SqlStatementSanitizer create(boolean statementSanitizationEnabled) {
+    return new SqlStatementSanitizer(statementSanitizationEnabled);
+  }
+
+  private final boolean statementSanitizationEnabled;
+
+  private SqlStatementSanitizer(boolean statementSanitizationEnabled) {
+    this.statementSanitizationEnabled = statementSanitizationEnabled;
+  }
+
+  public SqlStatementInfo sanitize(@Nullable String statement) {
     return sanitize(statement, SqlDialect.DEFAULT);
   }
 
-  public static SqlStatementInfo sanitize(@Nullable String statement, SqlDialect dialect) {
-    if (!isStatementSanitizationEnabled() || statement == null) {
+  public SqlStatementInfo sanitize(@Nullable String statement, SqlDialect dialect) {
+    if (!statementSanitizationEnabled || statement == null) {
       return SqlStatementInfo.create(statement, null, null);
     }
     return sqlToStatementInfoCache.computeIfAbsent(
@@ -50,6 +59,4 @@ public final class SqlStatementSanitizer {
 
     abstract SqlDialect getDialect();
   }
-
-  private SqlStatementSanitizer() {}
 }

+ 0 - 21
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/StatementSanitizationConfig.java

@@ -1,21 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.instrumentation.api.db;
-
-import io.opentelemetry.instrumentation.api.config.Config;
-
-/** DB statement sanitization is always enabled by default, you have to manually disable it. */
-final class StatementSanitizationConfig {
-
-  private static final boolean STATEMENT_SANITIZATION_ENABLED =
-      Config.get().getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true);
-
-  static boolean isStatementSanitizationEnabled() {
-    return STATEMENT_SANITIZATION_ENABLED;
-  }
-
-  private StatementSanitizationConfig() {}
-}

+ 4 - 2
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/DbClientSpanNameExtractor.java

@@ -82,6 +82,9 @@ public abstract class DbClientSpanNameExtractor<REQUEST> implements SpanNameExtr
   private static final class SqlClientSpanNameExtractor<REQUEST>
       extends DbClientSpanNameExtractor<REQUEST> {
 
+    // a dedicated sanitizer just for extracting the operation and table name
+    private static final SqlStatementSanitizer sanitizer = SqlStatementSanitizer.create(true);
+
     private final SqlClientAttributesGetter<REQUEST> getter;
 
     private SqlClientSpanNameExtractor(SqlClientAttributesGetter<REQUEST> getter) {
@@ -91,8 +94,7 @@ public abstract class DbClientSpanNameExtractor<REQUEST> implements SpanNameExtr
     @Override
     public String extract(REQUEST request) {
       String dbName = getter.name(request);
-      SqlStatementInfo sanitizedStatement =
-          SqlStatementSanitizer.sanitize(getter.rawStatement(request));
+      SqlStatementInfo sanitizedStatement = sanitizer.sanitize(getter.rawStatement(request));
       return computeSpanName(
           dbName, sanitizedStatement.getOperation(), sanitizedStatement.getTable());
     }

+ 6 - 3
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractor.java

@@ -44,19 +44,22 @@ public final class SqlClientAttributesExtractor<REQUEST, RESPONSE>
   }
 
   private final AttributeKey<String> dbTableAttribute;
+  private final SqlStatementSanitizer sanitizer;
 
   SqlClientAttributesExtractor(
-      SqlClientAttributesGetter<REQUEST> getter, AttributeKey<String> dbTableAttribute) {
+      SqlClientAttributesGetter<REQUEST> getter,
+      AttributeKey<String> dbTableAttribute,
+      SqlStatementSanitizer sanitizer) {
     super(getter);
     this.dbTableAttribute = dbTableAttribute;
+    this.sanitizer = sanitizer;
   }
 
   @Override
   public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) {
     super.onStart(attributes, parentContext, request);
 
-    SqlStatementInfo sanitizedStatement =
-        SqlStatementSanitizer.sanitize(getter.rawStatement(request));
+    SqlStatementInfo sanitizedStatement = sanitizer.sanitize(getter.rawStatement(request));
     internalSet(attributes, SemanticAttributes.DB_STATEMENT, sanitizedStatement.getFullStatement());
     internalSet(attributes, SemanticAttributes.DB_OPERATION, sanitizedStatement.getOperation());
     internalSet(attributes, dbTableAttribute, sanitizedStatement.getTable());

+ 15 - 1
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractorBuilder.java

@@ -8,6 +8,7 @@ package io.opentelemetry.instrumentation.api.instrumenter.db;
 import static java.util.Objects.requireNonNull;
 
 import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.instrumentation.api.db.SqlStatementSanitizer;
 import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
 
 /** A builder of {@link SqlClientAttributesExtractor}. */
@@ -15,6 +16,7 @@ public final class SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> {
 
   final SqlClientAttributesGetter<REQUEST> getter;
   AttributeKey<String> dbTableAttribute = SemanticAttributes.DB_SQL_TABLE;
+  boolean statementSanitizationEnabled = true;
 
   SqlClientAttributesExtractorBuilder(SqlClientAttributesGetter<REQUEST> getter) {
     this.getter = getter;
@@ -34,11 +36,23 @@ public final class SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> {
     return this;
   }
 
+  /**
+   * Sets whether the {@code db.statement} attribute extracted by the constructed {@link
+   * SqlClientAttributesExtractor} should be sanitized. If set to {@code true}, all parameters that
+   * can potentially contain sensitive information will be masked. Enabled by default.
+   */
+  public SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> setStatementSanitizationEnabled(
+      boolean statementSanitizationEnabled) {
+    this.statementSanitizationEnabled = statementSanitizationEnabled;
+    return this;
+  }
+
   /**
    * Returns a new {@link SqlClientAttributesExtractor} with the settings of this {@link
    * SqlClientAttributesExtractorBuilder}.
    */
   public SqlClientAttributesExtractor<REQUEST, RESPONSE> build() {
-    return new SqlClientAttributesExtractor<>(getter, dbTableAttribute);
+    return new SqlClientAttributesExtractor<>(
+        getter, dbTableAttribute, SqlStatementSanitizer.create(statementSanitizationEnabled));
   }
 }

+ 3 - 3
instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizerTest.groovy

@@ -12,7 +12,7 @@ class RedisCommandSanitizerTest extends Specification {
   @Unroll
   def "should sanitize #expected"() {
     when:
-    def sanitized = RedisCommandSanitizer.sanitize(command, args)
+    def sanitized = RedisCommandSanitizer.create(true).sanitize(command, args)
 
     then:
     sanitized == expected
@@ -90,7 +90,7 @@ class RedisCommandSanitizerTest extends Specification {
     def args = ["arg1", "arg 2"]
 
     when:
-    def sanitized = RedisCommandSanitizer.sanitize(command, args)
+    def sanitized = RedisCommandSanitizer.create(true).sanitize(command, args)
 
     then:
     sanitized == command + " " + args.join(" ")
@@ -140,7 +140,7 @@ class RedisCommandSanitizerTest extends Specification {
 
   def "should mask all arguments of an unknown command"() {
     when:
-    def sanitized = RedisCommandSanitizer.sanitize("NEWAUTH", ["password", "secret"])
+    def sanitized = RedisCommandSanitizer.create(true).sanitize("NEWAUTH", ["password", "secret"])
 
     then:
     sanitized == "NEWAUTH ? ?"

+ 9 - 9
instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizerTest.groovy

@@ -12,7 +12,7 @@ class SqlStatementSanitizerTest extends Specification {
 
   def "normalize #originalSql"() {
     setup:
-    def actualSanitized = SqlStatementSanitizer.sanitize(originalSql)
+    def actualSanitized = SqlStatementSanitizer.create(true).sanitize(originalSql)
 
     expect:
     actualSanitized.getFullStatement() == sanitizedSql
@@ -79,7 +79,7 @@ class SqlStatementSanitizerTest extends Specification {
 
   def "normalize couchbase #originalSql"() {
     setup:
-    def actualSanitized = SqlStatementSanitizer.sanitize(originalSql, SqlDialect.COUCHBASE)
+    def actualSanitized = SqlStatementSanitizer.create(true).sanitize(originalSql, SqlDialect.COUCHBASE)
 
     expect:
     actualSanitized.getFullStatement() == sanitizedSql
@@ -99,7 +99,7 @@ class SqlStatementSanitizerTest extends Specification {
   @Unroll
   def "should simplify #sql"() {
     expect:
-    SqlStatementSanitizer.sanitize(sql) == expected
+    SqlStatementSanitizer.create(true).sanitize(sql) == expected
 
     where:
     sql                                                               | expected
@@ -169,14 +169,14 @@ class SqlStatementSanitizerTest extends Specification {
 
     expect:
     def sanitizedQuery = query.replace('=123', '=?').substring(0, AutoSqlSanitizer.LIMIT)
-    SqlStatementSanitizer.sanitize(query) == SqlStatementInfo.create(sanitizedQuery, "SELECT", "table")
+    SqlStatementSanitizer.create(true).sanitize(query) == SqlStatementInfo.create(sanitizedQuery, "SELECT", "table")
   }
 
   def "lots and lots of ticks don't cause stack overflow or long runtimes"() {
     setup:
     String s = "'"
     for (int i = 0; i < 10000; i++) {
-      assert SqlStatementSanitizer.sanitize(s) != null
+      assert SqlStatementSanitizer.create(true).sanitize(s) != null
       s += "'"
     }
   }
@@ -187,7 +187,7 @@ class SqlStatementSanitizerTest extends Specification {
     for (int i = 0; i < 10000; i++) {
       s += String.valueOf(i)
     }
-    assert "?" == SqlStatementSanitizer.sanitize(s).getFullStatement()
+    assert "?" == SqlStatementSanitizer.create(true).sanitize(s).getFullStatement()
   }
 
   def "very long numbers at end of table name don't cause problem"() {
@@ -196,7 +196,7 @@ class SqlStatementSanitizerTest extends Specification {
     for (int i = 0; i < 10000; i++) {
       s += String.valueOf(i)
     }
-    assert s.substring(0, AutoSqlSanitizer.LIMIT) == SqlStatementSanitizer.sanitize(s).getFullStatement()
+    assert s.substring(0, AutoSqlSanitizer.LIMIT) == SqlStatementSanitizer.create(true).sanitize(s).getFullStatement()
   }
 
   def "test 32k truncation"() {
@@ -205,7 +205,7 @@ class SqlStatementSanitizerTest extends Specification {
     for (int i = 0; i < 10000; i++) {
       s.append("SELECT * FROM TABLE WHERE FIELD = 1234 AND ")
     }
-    String sanitized = SqlStatementSanitizer.sanitize(s.toString()).getFullStatement()
+    String sanitized = SqlStatementSanitizer.create(true).sanitize(s.toString()).getFullStatement()
     System.out.println(sanitized.length())
     assert sanitized.length() <= AutoSqlSanitizer.LIMIT
     assert !sanitized.contains("1234")
@@ -219,7 +219,7 @@ class SqlStatementSanitizerTest extends Specification {
       for (int c = 0; c < 1000; c++) {
         sb.append((char) r.nextInt((int) Character.MAX_VALUE))
       }
-      SqlStatementSanitizer.sanitize(sb.toString())
+      SqlStatementSanitizer.create(true).sanitize(sb.toString())
     }
   }
 }

+ 0 - 18
instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/db/StatementSanitizationConfigTest.java

@@ -1,18 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.instrumentation.api.db;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-
-import org.junit.jupiter.api.Test;
-
-public class StatementSanitizationConfigTest {
-
-  @Test
-  void shouldGetFalse() {
-    assertFalse(StatementSanitizationConfig.isStatementSanitizationEnabled());
-  }
-}

+ 7 - 3
instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java

@@ -25,6 +25,7 @@ package io.opentelemetry.javaagent.instrumentation.apachecamel.decorators;
 
 import io.opentelemetry.api.common.AttributesBuilder;
 import io.opentelemetry.instrumentation.api.db.SqlStatementSanitizer;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
 import io.opentelemetry.javaagent.instrumentation.apachecamel.CamelDirection;
 import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
 import java.net.URI;
@@ -34,6 +35,9 @@ import org.apache.camel.Exchange;
 
 class DbSpanDecorator extends BaseSpanDecorator {
 
+  private static final SqlStatementSanitizer sanitizer =
+      SqlStatementSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled());
+
   private final String component;
   private final String system;
 
@@ -65,19 +69,19 @@ class DbSpanDecorator extends BaseSpanDecorator {
       case "cql":
         Object cqlObj = exchange.getIn().getHeader("CamelCqlQuery");
         if (cqlObj != null) {
-          return SqlStatementSanitizer.sanitize(cqlObj.toString()).getFullStatement();
+          return sanitizer.sanitize(cqlObj.toString()).getFullStatement();
         }
         return null;
       case "jdbc":
         Object body = exchange.getIn().getBody();
         if (body instanceof String) {
-          return SqlStatementSanitizer.sanitize((String) body).getFullStatement();
+          return sanitizer.sanitize((String) body).getFullStatement();
         }
         return null;
       case "sql":
         Object sqlquery = exchange.getIn().getHeader("CamelSqlQuery");
         if (sqlquery instanceof String) {
-          return SqlStatementSanitizer.sanitize((String) sqlquery).getFullStatement();
+          return sanitizer.sanitize((String) sqlquery).getFullStatement();
         }
         return null;
       default:

+ 3 - 0
instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraSingletons.java

@@ -12,6 +12,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
 import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor;
 import io.opentelemetry.instrumentation.api.instrumenter.db.SqlClientAttributesExtractor;
 import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
 import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
 
 public final class CassandraSingletons {
@@ -32,6 +33,8 @@ public final class CassandraSingletons {
             .addAttributesExtractor(
                 SqlClientAttributesExtractor.builder(attributesGetter)
                     .setTableAttribute(SemanticAttributes.DB_CASSANDRA_TABLE)
+                    .setStatementSanitizationEnabled(
+                        CommonConfig.get().isStatementSanitizationEnabled())
                     .build())
             .addAttributesExtractor(
                 NetClientAttributesExtractor.create(new CassandraNetAttributesGetter()))

+ 3 - 0
instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraSingletons.java

@@ -12,6 +12,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
 import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor;
 import io.opentelemetry.instrumentation.api.instrumenter.db.SqlClientAttributesExtractor;
 import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
 import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
 
 public final class CassandraSingletons {
@@ -31,6 +32,8 @@ public final class CassandraSingletons {
             .addAttributesExtractor(
                 SqlClientAttributesExtractor.builder(attributesGetter)
                     .setTableAttribute(SemanticAttributes.DB_CASSANDRA_TABLE)
+                    .setStatementSanitizationEnabled(
+                        CommonConfig.get().isStatementSanitizationEnabled())
                     .build())
             .addAttributesExtractor(
                 NetClientAttributesExtractor.create(new CassandraNetAttributesGetter()))

+ 1 - 0
instrumentation/couchbase/couchbase-2-common/javaagent-unit-tests/build.gradle.kts

@@ -7,6 +7,7 @@ dependencies {
   testImplementation("org.spockframework:spock-core")
 
   testImplementation(project(":instrumentation-api-semconv"))
+  testImplementation(project(":javaagent-extension-api"))
   testImplementation(project(":instrumentation:couchbase:couchbase-2-common:javaagent"))
   testImplementation("com.couchbase.client:java-client:2.5.0")
 }

+ 6 - 1
instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizer.java

@@ -8,12 +8,17 @@ package io.opentelemetry.javaagent.instrumentation.couchbase.v2_0;
 import io.opentelemetry.instrumentation.api.db.SqlDialect;
 import io.opentelemetry.instrumentation.api.db.SqlStatementInfo;
 import io.opentelemetry.instrumentation.api.db.SqlStatementSanitizer;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import javax.annotation.Nullable;
 
 public final class CouchbaseQuerySanitizer {
+
+  private static final SqlStatementSanitizer sanitizer =
+      SqlStatementSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled());
+
   @Nullable private static final Class<?> QUERY_CLASS;
   @Nullable private static final Class<?> STATEMENT_CLASS;
   @Nullable private static final Class<?> N1QL_QUERY_CLASS;
@@ -116,7 +121,7 @@ public final class CouchbaseQuerySanitizer {
   }
 
   private static SqlStatementInfo sanitizeString(String query) {
-    return SqlStatementSanitizer.sanitize(query, SqlDialect.COUCHBASE);
+    return sanitizer.sanitize(query, SqlDialect.COUCHBASE);
   }
 
   private CouchbaseQuerySanitizer() {}

+ 5 - 1
instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeDbAttributesGetter.java

@@ -7,11 +7,15 @@ package io.opentelemetry.javaagent.instrumentation.geode;
 
 import io.opentelemetry.instrumentation.api.db.SqlStatementSanitizer;
 import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesGetter;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
 import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
 import javax.annotation.Nullable;
 
 final class GeodeDbAttributesGetter implements DbClientAttributesGetter<GeodeRequest> {
 
+  private static final SqlStatementSanitizer sanitizer =
+      SqlStatementSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled());
+
   @Override
   public String system(GeodeRequest request) {
     return SemanticAttributes.DbSystemValues.GEODE;
@@ -38,7 +42,7 @@ final class GeodeDbAttributesGetter implements DbClientAttributesGetter<GeodeReq
   @Nullable
   public String statement(GeodeRequest request) {
     // sanitized statement is cached
-    return SqlStatementSanitizer.sanitize(request.getQuery()).getFullStatement();
+    return sanitizer.sanitize(request.getQuery()).getFullStatement();
   }
 
   @Override

+ 5 - 1
instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/OperationNameUtil.java

@@ -7,15 +7,19 @@ package io.opentelemetry.javaagent.instrumentation.hibernate;
 
 import io.opentelemetry.instrumentation.api.db.SqlStatementInfo;
 import io.opentelemetry.instrumentation.api.db.SqlStatementSanitizer;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
 import java.util.function.Function;
 
 public final class OperationNameUtil {
 
+  private static final SqlStatementSanitizer sanitizer =
+      SqlStatementSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled());
+
   public static String getOperationNameForQuery(String query) {
     // set operation to default value that is used when sql sanitizer fails to extract
     // operation name
     String operation = "Hibernate Query";
-    SqlStatementInfo info = SqlStatementSanitizer.sanitize(query);
+    SqlStatementInfo info = sanitizer.sanitize(query);
     if (info.getOperation() != null) {
       operation = info.getOperation();
       if (info.getTable() != null) {

+ 5 - 1
instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java

@@ -31,7 +31,11 @@ public final class JdbcSingletons {
                 GlobalOpenTelemetry.get(),
                 INSTRUMENTATION_NAME,
                 DbClientSpanNameExtractor.create(dbAttributesGetter))
-            .addAttributesExtractor(SqlClientAttributesExtractor.create(dbAttributesGetter))
+            .addAttributesExtractor(
+                SqlClientAttributesExtractor.builder(dbAttributesGetter)
+                    .setStatementSanitizationEnabled(
+                        CommonConfig.get().isStatementSanitizationEnabled())
+                    .build())
             .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter))
             .addAttributesExtractor(
                 PeerServiceAttributesExtractor.create(

+ 7 - 1
instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcSingletons.java

@@ -11,6 +11,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
 import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor;
 import io.opentelemetry.instrumentation.api.instrumenter.db.SqlClientAttributesExtractor;
 import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor;
+import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil;
 
 /**
  * This class is internal and is hence not for public use. Its APIs are unstable and can change at
@@ -30,7 +31,12 @@ public final class JdbcSingletons {
                 GlobalOpenTelemetry.get(),
                 INSTRUMENTATION_NAME,
                 DbClientSpanNameExtractor.create(dbAttributesGetter))
-            .addAttributesExtractor(SqlClientAttributesExtractor.create(dbAttributesGetter))
+            .addAttributesExtractor(
+                SqlClientAttributesExtractor.builder(dbAttributesGetter)
+                    .setStatementSanitizationEnabled(
+                        ConfigPropertiesUtil.getBoolean(
+                            "otel.instrumentation.common.db-statement-sanitizer.enabled", true))
+                    .build())
             .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter))
             .newInstrumenter(SpanKindExtractor.alwaysClient());
   }

+ 5 - 1
instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisDbAttributesGetter.java

@@ -7,11 +7,15 @@ package io.opentelemetry.javaagent.instrumentation.jedis.v1_4;
 
 import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer;
 import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesGetter;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
 import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
 import javax.annotation.Nullable;
 
 final class JedisDbAttributesGetter implements DbClientAttributesGetter<JedisRequest> {
 
+  private static final RedisCommandSanitizer sanitizer =
+      RedisCommandSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled());
+
   @Override
   public String system(JedisRequest request) {
     return SemanticAttributes.DbSystemValues.REDIS;
@@ -35,7 +39,7 @@ final class JedisDbAttributesGetter implements DbClientAttributesGetter<JedisReq
 
   @Override
   public String statement(JedisRequest request) {
-    return RedisCommandSanitizer.sanitize(request.getCommand().name(), request.getArgs());
+    return sanitizer.sanitize(request.getCommand().name(), request.getArgs());
   }
 
   @Override

+ 5 - 1
instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisRequest.java

@@ -7,6 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.jedis.v3_0;
 
 import com.google.auto.value.AutoValue;
 import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 import redis.clients.jedis.Connection;
@@ -16,6 +17,9 @@ import redis.clients.jedis.commands.ProtocolCommand;
 @AutoValue
 public abstract class JedisRequest {
 
+  private static final RedisCommandSanitizer sanitizer =
+      RedisCommandSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled());
+
   public static JedisRequest create(
       Connection connection, ProtocolCommand command, List<byte[]> args) {
     return new AutoValue_JedisRequest(connection, command, args);
@@ -39,6 +43,6 @@ public abstract class JedisRequest {
   }
 
   public String getStatement() {
-    return RedisCommandSanitizer.sanitize(getOperation(), getArgs());
+    return sanitizer.sanitize(getOperation(), getArgs());
   }
 }

+ 5 - 1
instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisRequest.java

@@ -7,6 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.jedis.v4_0;
 
 import com.google.auto.value.AutoValue;
 import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
 import java.net.Socket;
 import java.net.SocketAddress;
 import java.nio.charset.StandardCharsets;
@@ -20,6 +21,9 @@ import redis.clients.jedis.commands.ProtocolCommand;
 @AutoValue
 public abstract class JedisRequest {
 
+  private static final RedisCommandSanitizer sanitizer =
+      RedisCommandSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled());
+
   public static JedisRequest create(ProtocolCommand command, List<byte[]> args) {
     return new AutoValue_JedisRequest(command, args);
   }
@@ -54,7 +58,7 @@ public abstract class JedisRequest {
   }
 
   public String getStatement() {
-    return RedisCommandSanitizer.sanitize(getOperation(), getArgs());
+    return sanitizer.sanitize(getOperation(), getArgs());
   }
 
   private SocketAddress remoteSocketAddress;

+ 5 - 1
instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceDbAttributesGetter.java

@@ -9,6 +9,7 @@ import io.lettuce.core.protocol.RedisCommand;
 import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer;
 import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesGetter;
 import io.opentelemetry.instrumentation.lettuce.common.LettuceArgSplitter;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
 import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
 import java.util.Collections;
 import java.util.List;
@@ -16,6 +17,9 @@ import javax.annotation.Nullable;
 
 final class LettuceDbAttributesGetter implements DbClientAttributesGetter<RedisCommand<?, ?, ?>> {
 
+  private static final RedisCommandSanitizer sanitizer =
+      RedisCommandSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled());
+
   @Override
   public String system(RedisCommand<?, ?, ?> request) {
     return SemanticAttributes.DbSystemValues.REDIS;
@@ -46,7 +50,7 @@ final class LettuceDbAttributesGetter implements DbClientAttributesGetter<RedisC
         request.getArgs() == null
             ? Collections.emptyList()
             : LettuceArgSplitter.splitArgs(request.getArgs().toCommandString());
-    return RedisCommandSanitizer.sanitize(command, args);
+    return sanitizer.sanitize(command, args);
   }
 
   @Override

+ 13 - 3
instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetry.java

@@ -9,6 +9,7 @@ import io.lettuce.core.tracing.Tracing;
 import io.opentelemetry.api.OpenTelemetry;
 import io.opentelemetry.api.trace.Tracer;
 import io.opentelemetry.api.trace.TracerBuilder;
+import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer;
 import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
 
 /** Entrypoint for instrumenting Lettuce or clients. */
@@ -18,18 +19,27 @@ public final class LettuceTelemetry {
 
   /** Returns a new {@link LettuceTelemetry} configured with the given {@link OpenTelemetry}. */
   public static LettuceTelemetry create(OpenTelemetry openTelemetry) {
-    return new LettuceTelemetry(openTelemetry);
+    return builder(openTelemetry).build();
+  }
+
+  /**
+   * Returns a new {@link LettuceTelemetryBuilder} configured with the given {@link OpenTelemetry}.
+   */
+  public static LettuceTelemetryBuilder builder(OpenTelemetry openTelemetry) {
+    return new LettuceTelemetryBuilder(openTelemetry);
   }
 
   private final Tracer tracer;
+  private final RedisCommandSanitizer sanitizer;
 
-  private LettuceTelemetry(OpenTelemetry openTelemetry) {
+  LettuceTelemetry(OpenTelemetry openTelemetry, boolean statementSanitizationEnabled) {
     TracerBuilder tracerBuilder = openTelemetry.tracerBuilder(INSTRUMENTATION_NAME);
     String version = EmbeddedInstrumentationProperties.findVersion(INSTRUMENTATION_NAME);
     if (version != null) {
       tracerBuilder.setInstrumentationVersion(version);
     }
     tracer = tracerBuilder.build();
+    sanitizer = RedisCommandSanitizer.create(statementSanitizationEnabled);
   }
 
   /**
@@ -37,6 +47,6 @@ public final class LettuceTelemetry {
    * io.lettuce.core.resource.ClientResources.Builder#tracing(Tracing)}.
    */
   public Tracing newTracing() {
-    return new OpenTelemetryTracing(tracer);
+    return new OpenTelemetryTracing(tracer, sanitizer);
   }
 }

+ 39 - 0
instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetryBuilder.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.lettuce.v5_1;
+
+import io.opentelemetry.api.OpenTelemetry;
+
+/** A builder of {@link LettuceTelemetry}. */
+public final class LettuceTelemetryBuilder {
+
+  private final OpenTelemetry openTelemetry;
+
+  private boolean statementSanitizationEnabled = true;
+
+  LettuceTelemetryBuilder(OpenTelemetry openTelemetry) {
+    this.openTelemetry = openTelemetry;
+  }
+
+  /**
+   * Sets whether the {@code db.statement} attribute on the spans emitted by the constructed {@link
+   * LettuceTelemetry} should be sanitized. If set to {@code true}, all parameters that can
+   * potentially contain sensitive information will be masked. Enabled by default.
+   */
+  public LettuceTelemetryBuilder setStatementSanitizationEnabled(
+      boolean statementSanitizationEnabled) {
+    this.statementSanitizationEnabled = statementSanitizationEnabled;
+    return this;
+  }
+
+  /**
+   * Returns a new {@link LettuceTelemetry} with the settings of this {@link
+   * LettuceTelemetryBuilder}.
+   */
+  public LettuceTelemetry build() {
+    return new LettuceTelemetry(openTelemetry, statementSanitizationEnabled);
+  }
+}

+ 13 - 8
instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/OpenTelemetryTracing.java

@@ -40,8 +40,8 @@ final class OpenTelemetryTracing implements Tracing {
           NetClientAttributesExtractor.create(new LettuceNetAttributesGetter());
   private final TracerProvider tracerProvider;
 
-  OpenTelemetryTracing(io.opentelemetry.api.trace.Tracer tracer) {
-    this.tracerProvider = new OpenTelemetryTracerProvider(tracer);
+  OpenTelemetryTracing(io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) {
+    this.tracerProvider = new OpenTelemetryTracerProvider(tracer, sanitizer);
   }
 
   @Override
@@ -81,8 +81,9 @@ final class OpenTelemetryTracing implements Tracing {
 
     private final Tracer openTelemetryTracer;
 
-    OpenTelemetryTracerProvider(io.opentelemetry.api.trace.Tracer tracer) {
-      openTelemetryTracer = new OpenTelemetryTracer(tracer);
+    OpenTelemetryTracerProvider(
+        io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) {
+      openTelemetryTracer = new OpenTelemetryTracer(tracer, sanitizer);
     }
 
     @Override
@@ -126,9 +127,11 @@ final class OpenTelemetryTracing implements Tracing {
   private static class OpenTelemetryTracer extends Tracer {
 
     private final io.opentelemetry.api.trace.Tracer tracer;
+    private final RedisCommandSanitizer sanitizer;
 
-    OpenTelemetryTracer(io.opentelemetry.api.trace.Tracer tracer) {
+    OpenTelemetryTracer(io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) {
       this.tracer = tracer;
+      this.sanitizer = sanitizer;
     }
 
     @Override
@@ -155,7 +158,7 @@ final class OpenTelemetryTracing implements Tracing {
               .setSpanKind(SpanKind.CLIENT)
               .setParent(context)
               .setAttribute(SemanticAttributes.DB_SYSTEM, DbSystemValues.REDIS);
-      return new OpenTelemetrySpan(context, spanBuilder);
+      return new OpenTelemetrySpan(context, spanBuilder, sanitizer);
     }
   }
 
@@ -167,6 +170,7 @@ final class OpenTelemetryTracing implements Tracing {
 
     private final Context context;
     private final SpanBuilder spanBuilder;
+    private final RedisCommandSanitizer sanitizer;
 
     @Nullable private String name;
     @Nullable private List<Object> events;
@@ -174,9 +178,10 @@ final class OpenTelemetryTracing implements Tracing {
     @Nullable private Span span;
     @Nullable private String args;
 
-    OpenTelemetrySpan(Context context, SpanBuilder spanBuilder) {
+    OpenTelemetrySpan(Context context, SpanBuilder spanBuilder, RedisCommandSanitizer sanitizer) {
       this.context = context;
       this.spanBuilder = spanBuilder;
+      this.sanitizer = sanitizer;
     }
 
     @Override
@@ -319,7 +324,7 @@ final class OpenTelemetryTracing implements Tracing {
 
     private void finish(Span span) {
       if (name != null) {
-        String statement = RedisCommandSanitizer.sanitize(name, splitArgs(args));
+        String statement = sanitizer.sanitize(name, splitArgs(args));
         span.setAttribute(SemanticAttributes.DB_STATEMENT, statement);
       }
       span.end();

+ 5 - 1
instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonRequest.java

@@ -11,6 +11,7 @@ import static java.util.Collections.singletonList;
 import com.google.auto.value.AutoValue;
 import io.netty.buffer.ByteBuf;
 import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
@@ -27,6 +28,9 @@ import org.redisson.client.protocol.CommandsData;
 @AutoValue
 public abstract class RedissonRequest {
 
+  private static final RedisCommandSanitizer sanitizer =
+      RedisCommandSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled());
+
   public static RedissonRequest create(InetSocketAddress address, Object command) {
     return new AutoValue_RedissonRequest(address, command);
   }
@@ -98,7 +102,7 @@ public abstract class RedissonRequest {
         args.add(param);
       }
     }
-    return RedisCommandSanitizer.sanitize(command.getCommand().getName(), args);
+    return sanitizer.sanitize(command.getCommand().getName(), args);
   }
 
   @Nullable

+ 7 - 0
javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/CommonConfig.java

@@ -28,6 +28,7 @@ public final class CommonConfig {
   private final List<String> clientResponseHeaders;
   private final List<String> serverRequestHeaders;
   private final List<String> serverResponseHeaders;
+  private final boolean statementSanitizationEnabled;
 
   CommonConfig(InstrumentationConfig config) {
     peerServiceMapping =
@@ -40,6 +41,8 @@ public final class CommonConfig {
         config.getList("otel.instrumentation.http.capture-headers.server.request", emptyList());
     serverResponseHeaders =
         config.getList("otel.instrumentation.http.capture-headers.server.response", emptyList());
+    statementSanitizationEnabled =
+        config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true);
   }
 
   public Map<String, String> getPeerServiceMapping() {
@@ -61,4 +64,8 @@ public final class CommonConfig {
   public List<String> getServerResponseHeaders() {
     return serverResponseHeaders;
   }
+
+  public boolean isStatementSanitizationEnabled() {
+    return statementSanitizationEnabled;
+  }
 }