Use a single configuration property for db.statement sanitization (#2125)
* Use a single configuration property for db.statement sanitization * spotless
This commit is contained in:
parent
b13ef2d809
commit
c61f3f2949
|
@ -12,6 +12,7 @@ import io.opentelemetry.api.trace.Span;
|
|||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
||||
import java.net.InetSocketAddress;
|
||||
|
@ -30,7 +31,7 @@ public class CassandraDatabaseClientTracer extends DatabaseClientTracer<Session,
|
|||
|
||||
@Override
|
||||
protected String normalizeQuery(String query) {
|
||||
return CassandraQueryNormalizer.normalize(query);
|
||||
return SqlStatementSanitizer.sanitize(query).getFullStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.cassandra.v3_0;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.api.db.QueryNormalizationConfig.isQueryNormalizationEnabled;
|
||||
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlSanitizer;
|
||||
|
||||
public final class CassandraQueryNormalizer {
|
||||
private static final boolean NORMALIZATION_ENABLED =
|
||||
isQueryNormalizationEnabled("cassandra", "cassandra-3.0");
|
||||
|
||||
public static String normalize(String query) {
|
||||
if (!NORMALIZATION_ENABLED) {
|
||||
return query;
|
||||
}
|
||||
return SqlSanitizer.sanitize(query).getFullStatement();
|
||||
}
|
||||
|
||||
private CassandraQueryNormalizer() {}
|
||||
}
|
|
@ -13,6 +13,7 @@ import io.opentelemetry.api.trace.Span;
|
|||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
@ -31,7 +32,7 @@ public class CassandraDatabaseClientTracer extends DatabaseClientTracer<CqlSessi
|
|||
|
||||
@Override
|
||||
protected String normalizeQuery(String query) {
|
||||
return CassandraQueryNormalizer.normalize(query);
|
||||
return SqlStatementSanitizer.sanitize(query).getFullStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.cassandra.v4_0;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.api.db.QueryNormalizationConfig.isQueryNormalizationEnabled;
|
||||
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlSanitizer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class CassandraQueryNormalizer {
|
||||
private static final Logger log = LoggerFactory.getLogger(CassandraQueryNormalizer.class);
|
||||
private static final boolean NORMALIZATION_ENABLED =
|
||||
isQueryNormalizationEnabled("cassandra", "cassandra-4.0");
|
||||
|
||||
public static String normalize(String query) {
|
||||
if (!NORMALIZATION_ENABLED) {
|
||||
return query;
|
||||
}
|
||||
return SqlSanitizer.sanitize(query).getFullStatement();
|
||||
}
|
||||
|
||||
private CassandraQueryNormalizer() {}
|
||||
}
|
|
@ -31,7 +31,7 @@ public class CouchbaseOnSubscribe<T> extends TracedOnSubscribe<T> {
|
|||
public static <T> CouchbaseOnSubscribe<T> create(
|
||||
Observable<T> originalObservable, String bucket, Object query) {
|
||||
return new CouchbaseOnSubscribe<>(
|
||||
originalObservable, bucket, CouchbaseQueryNormalizer.normalize(query));
|
||||
originalObservable, bucket, CouchbaseQuerySanitizer.sanitize(query));
|
||||
}
|
||||
|
||||
private CouchbaseOnSubscribe(Observable<T> originalObservable, String bucket, String query) {
|
||||
|
|
|
@ -5,17 +5,12 @@
|
|||
|
||||
package io.opentelemetry.javaagent.instrumentation.couchbase.v2_0;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.api.db.QueryNormalizationConfig.isQueryNormalizationEnabled;
|
||||
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlSanitizer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
public final class CouchbaseQueryNormalizer {
|
||||
private static final boolean NORMALIZATION_ENABLED =
|
||||
isQueryNormalizationEnabled("couchbase", "couchbase-2.0");
|
||||
|
||||
public final class CouchbaseQuerySanitizer {
|
||||
private static final Class<?> QUERY_CLASS;
|
||||
private static final Class<?> STATEMENT_CLASS;
|
||||
private static final Class<?> N1QL_QUERY_CLASS;
|
||||
|
@ -73,15 +68,15 @@ public final class CouchbaseQueryNormalizer {
|
|||
ANALYTICS_GET_STATEMENT = analyticsGetStatement;
|
||||
}
|
||||
|
||||
public static String normalize(Object query) {
|
||||
public static String sanitize(Object query) {
|
||||
if (query instanceof String) {
|
||||
return normalizeString((String) query);
|
||||
return sanitizeString((String) query);
|
||||
}
|
||||
// Query is present in Couchbase [2.0.0, 2.2.0)
|
||||
// Statement is present starting from Couchbase 2.1.0
|
||||
if (QUERY_CLASS != null && QUERY_CLASS.isAssignableFrom(query.getClass())
|
||||
|| STATEMENT_CLASS != null && STATEMENT_CLASS.isAssignableFrom(query.getClass())) {
|
||||
return normalizeString(query.toString());
|
||||
return sanitizeString(query.toString());
|
||||
}
|
||||
// SpatialViewQuery is present starting from Couchbase 2.1.0
|
||||
String queryClassName = query.getClass().getName();
|
||||
|
@ -93,14 +88,14 @@ public final class CouchbaseQueryNormalizer {
|
|||
if (N1QL_QUERY_CLASS != null && N1QL_QUERY_CLASS.isAssignableFrom(query.getClass())) {
|
||||
String statement = getStatementString(N1QL_GET_STATEMENT, query);
|
||||
if (statement != null) {
|
||||
return normalizeString(statement);
|
||||
return sanitizeString(statement);
|
||||
}
|
||||
}
|
||||
// AnalyticsQuery is present starting from Couchbase 2.4.3
|
||||
if (ANALYTICS_QUERY_CLASS != null && ANALYTICS_QUERY_CLASS.isAssignableFrom(query.getClass())) {
|
||||
String statement = getStatementString(ANALYTICS_GET_STATEMENT, query);
|
||||
if (statement != null) {
|
||||
return normalizeString(statement);
|
||||
return sanitizeString(statement);
|
||||
}
|
||||
}
|
||||
return query.getClass().getSimpleName();
|
||||
|
@ -117,12 +112,9 @@ public final class CouchbaseQueryNormalizer {
|
|||
}
|
||||
}
|
||||
|
||||
private static String normalizeString(String query) {
|
||||
if (!NORMALIZATION_ENABLED || query == null) {
|
||||
return query;
|
||||
}
|
||||
return SqlSanitizer.sanitize(query).getFullStatement();
|
||||
private static String sanitizeString(String query) {
|
||||
return SqlStatementSanitizer.sanitize(query).getFullStatement();
|
||||
}
|
||||
|
||||
private CouchbaseQueryNormalizer() {}
|
||||
private CouchbaseQuerySanitizer() {}
|
||||
}
|
|
@ -14,11 +14,11 @@ import com.couchbase.client.java.view.ViewQuery
|
|||
import spock.lang.Specification
|
||||
import spock.lang.Unroll
|
||||
|
||||
class CouchbaseQueryNormalizerTest extends Specification {
|
||||
class CouchbaseQuerySanitizerTest extends Specification {
|
||||
@Unroll
|
||||
def "should normalize #desc query"() {
|
||||
when:
|
||||
def normalized = CouchbaseQueryNormalizer.normalize(query)
|
||||
def normalized = CouchbaseQuerySanitizer.sanitize(query)
|
||||
|
||||
then:
|
||||
// the analytics query ends up with trailing ';' in earlier couchbase version, but no trailing ';' in later couchbase version
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.geode;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.api.db.QueryNormalizationConfig.isQueryNormalizationEnabled;
|
||||
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlSanitizer;
|
||||
|
||||
public final class GeodeQueryNormalizer {
|
||||
private static final boolean NORMALIZATION_ENABLED =
|
||||
isQueryNormalizationEnabled("geode", "geode-1.4");
|
||||
|
||||
public static String normalize(String query) {
|
||||
if (!NORMALIZATION_ENABLED || query == null) {
|
||||
return query;
|
||||
}
|
||||
return SqlSanitizer.sanitize(query).getFullStatement();
|
||||
}
|
||||
|
||||
private GeodeQueryNormalizer() {}
|
||||
}
|
|
@ -9,6 +9,7 @@ import static io.opentelemetry.api.trace.Span.Kind.CLIENT;
|
|||
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||
import java.net.InetSocketAddress;
|
||||
import org.apache.geode.cache.Region;
|
||||
|
@ -40,7 +41,7 @@ public class GeodeTracer extends DatabaseClientTracer<Region<?, ?>, String> {
|
|||
|
||||
@Override
|
||||
protected String normalizeQuery(String query) {
|
||||
return GeodeQueryNormalizer.normalize(query);
|
||||
return SqlStatementSanitizer.sanitize(query).getFullStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -14,7 +14,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
|||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementInfo;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.Map;
|
||||
|
@ -49,10 +49,7 @@ public class ConnectionInstrumentation implements TypeInstrumentation {
|
|||
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||
public static void addDbInfo(
|
||||
@Advice.Argument(0) String sql, @Advice.Return PreparedStatement statement) {
|
||||
SqlStatementInfo sanitizedSql = JdbcUtils.sanitizeAndExtractInfo(sql);
|
||||
if (sanitizedSql != null) {
|
||||
JdbcMaps.preparedStatements.put(statement, sanitizedSql);
|
||||
}
|
||||
JdbcMaps.preparedStatements.put(statement, SqlStatementSanitizer.sanitize(sql));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
package io.opentelemetry.javaagent.instrumentation.jdbc;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcUtils.connectionFromStatement;
|
||||
import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcUtils.sanitizeAndExtractInfo;
|
||||
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementInfo;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
|
@ -71,7 +71,7 @@ public class JdbcTracer extends DatabaseClientTracer<DbInfo, SqlStatementInfo> {
|
|||
}
|
||||
|
||||
public Context startSpan(Context parentContext, Statement statement, String query) {
|
||||
return startSpan(parentContext, statement, sanitizeAndExtractInfo(query));
|
||||
return startSpan(parentContext, statement, SqlStatementSanitizer.sanitize(query));
|
||||
}
|
||||
|
||||
private Context startSpan(
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
|
||||
package io.opentelemetry.javaagent.instrumentation.jdbc;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.api.db.QueryNormalizationConfig.isQueryNormalizationEnabled;
|
||||
|
||||
import io.opentelemetry.javaagent.instrumentation.api.BoundedCache;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlSanitizer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementInfo;
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.Connection;
|
||||
import java.sql.Statement;
|
||||
|
@ -20,11 +15,7 @@ public abstract class JdbcUtils {
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(JdbcUtils.class);
|
||||
|
||||
private static final boolean NORMALIZATION_ENABLED = isQueryNormalizationEnabled("jdbc");
|
||||
|
||||
private static Field c3poField = null;
|
||||
private static final BoundedCache<String, SqlStatementInfo> sqlToStatementInfoCache =
|
||||
BoundedCache.build(1000);
|
||||
|
||||
/** Returns the unwrapped connection or null if exception was thrown. */
|
||||
public static Connection connectionFromStatement(Statement statement) {
|
||||
|
@ -69,16 +60,4 @@ public abstract class JdbcUtils {
|
|||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
public static SqlStatementInfo sanitizeAndExtractInfo(String sql) {
|
||||
if (!NORMALIZATION_ENABLED) {
|
||||
return new SqlStatementInfo(sql, null, null);
|
||||
}
|
||||
return sqlToStatementInfoCache.get(
|
||||
sql,
|
||||
k -> {
|
||||
log.trace("SQL statement cache miss");
|
||||
return SqlSanitizer.sanitize(sql);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
package io.opentelemetry.javaagent.instrumentation.jedis.v1_4;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandNormalizer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandSanitizer;
|
||||
import io.opentelemetry.javaagent.instrumentation.jedis.v1_4.JedisClientTracer.CommandWithArgs;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
||||
import java.net.InetSocketAddress;
|
||||
|
@ -22,9 +22,6 @@ public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandW
|
|||
return TRACER;
|
||||
}
|
||||
|
||||
private final RedisCommandNormalizer commandNormalizer =
|
||||
new RedisCommandNormalizer("jedis", "jedis-1.4");
|
||||
|
||||
@Override
|
||||
protected String spanName(Connection connection, CommandWithArgs query, String normalizedQuery) {
|
||||
return query.getStringCommand();
|
||||
|
@ -32,7 +29,7 @@ public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandW
|
|||
|
||||
@Override
|
||||
protected String normalizeQuery(CommandWithArgs command) {
|
||||
return commandNormalizer.normalize(command.getStringCommand(), command.getArgs());
|
||||
return RedisCommandSanitizer.sanitize(command.getStringCommand(), command.getArgs());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
package io.opentelemetry.javaagent.instrumentation.jedis.v3_0;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandNormalizer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandSanitizer;
|
||||
import io.opentelemetry.javaagent.instrumentation.jedis.v3_0.JedisClientTracer.CommandWithArgs;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
||||
import java.net.InetSocketAddress;
|
||||
|
@ -24,9 +24,6 @@ public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandW
|
|||
return TRACER;
|
||||
}
|
||||
|
||||
private final RedisCommandNormalizer commandNormalizer =
|
||||
new RedisCommandNormalizer("jedis", "jedis-3.0");
|
||||
|
||||
@Override
|
||||
protected String spanName(Connection connection, CommandWithArgs query, String normalizedQuery) {
|
||||
return query.getStringCommand();
|
||||
|
@ -34,7 +31,7 @@ public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandW
|
|||
|
||||
@Override
|
||||
protected String normalizeQuery(CommandWithArgs command) {
|
||||
return commandNormalizer.normalize(command.getStringCommand(), command.getArgs());
|
||||
return RedisCommandSanitizer.sanitize(command.getStringCommand(), command.getArgs());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,7 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.lettuce.v5_0;
|
|||
|
||||
import io.lettuce.core.RedisURI;
|
||||
import io.lettuce.core.protocol.RedisCommand;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandNormalizer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandSanitizer;
|
||||
import io.opentelemetry.javaagent.instrumentation.lettuce.LettuceArgSplitter;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -20,9 +20,6 @@ public class LettuceDatabaseClientTracer
|
|||
return TRACER;
|
||||
}
|
||||
|
||||
private final RedisCommandNormalizer commandNormalizer =
|
||||
new RedisCommandNormalizer("lettuce", "lettuce-5.0");
|
||||
|
||||
@Override
|
||||
protected String spanName(
|
||||
RedisURI connection, RedisCommand<?, ?, ?> query, String normalizedQuery) {
|
||||
|
@ -36,6 +33,6 @@ public class LettuceDatabaseClientTracer
|
|||
redisCommand.getArgs() == null
|
||||
? Collections.emptyList()
|
||||
: LettuceArgSplitter.splitArgs(redisCommand.getArgs().toCommandString());
|
||||
return commandNormalizer.normalize(command, args);
|
||||
return RedisCommandSanitizer.sanitize(command, args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import io.opentelemetry.api.trace.StatusCode;
|
|||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
|
||||
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils.SpanAttributeSetter;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandNormalizer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandSanitizer;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
||||
import java.net.InetSocketAddress;
|
||||
|
@ -40,9 +40,6 @@ public enum OpenTelemetryTracing implements Tracing {
|
|||
return TRACER;
|
||||
}
|
||||
|
||||
private static final RedisCommandNormalizer commandNormalizer =
|
||||
new RedisCommandNormalizer("lettuce", "lettuce-5.1");
|
||||
|
||||
@Override
|
||||
public TracerProvider getTracerProvider() {
|
||||
return OpenTelemetryTracerProvider.INSTANCE;
|
||||
|
@ -256,7 +253,7 @@ public enum OpenTelemetryTracing implements Tracing {
|
|||
public synchronized void finish() {
|
||||
if (span != null) {
|
||||
if (name != null) {
|
||||
String statement = commandNormalizer.normalize(name, splitArgs(args));
|
||||
String statement = RedisCommandSanitizer.sanitize(name, splitArgs(args));
|
||||
span.setAttribute(SemanticAttributes.DB_STATEMENT, statement);
|
||||
}
|
||||
span.end();
|
||||
|
|
|
@ -8,7 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.redisson;
|
|||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandNormalizer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandSanitizer;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
|
@ -26,9 +26,6 @@ public class RedissonClientTracer extends DatabaseClientTracer<RedisConnection,
|
|||
return TRACER;
|
||||
}
|
||||
|
||||
private final RedisCommandNormalizer commandNormalizer =
|
||||
new RedisCommandNormalizer("redisson", "redisson-3.0");
|
||||
|
||||
@Override
|
||||
protected String spanName(RedisConnection connection, Object query, String normalizedQuery) {
|
||||
if (query instanceof CommandsData) {
|
||||
|
@ -93,7 +90,7 @@ public class RedissonClientTracer extends DatabaseClientTracer<RedisConnection,
|
|||
args.add(param);
|
||||
}
|
||||
}
|
||||
return commandNormalizer.normalize(command.getCommand().getName(), args);
|
||||
return RedisCommandSanitizer.sanitize(command.getCommand().getName(), args);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.api.db;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
|
||||
/**
|
||||
* This class encapsulates query normalization property naming convention. Query normalization is
|
||||
* always enabled by default, you have to manually disable it.
|
||||
*/
|
||||
public final class QueryNormalizationConfig {
|
||||
|
||||
public static boolean isQueryNormalizationEnabled(String... instrumentationNames) {
|
||||
return Config.get()
|
||||
.isInstrumentationPropertyEnabled(
|
||||
asList(instrumentationNames), "query.normalizer.enabled", true);
|
||||
}
|
||||
|
||||
private QueryNormalizationConfig() {}
|
||||
}
|
|
@ -5,14 +5,13 @@
|
|||
|
||||
package io.opentelemetry.javaagent.instrumentation.api.db;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.api.db.QueryNormalizationConfig.isQueryNormalizationEnabled;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.unmodifiableMap;
|
||||
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandNormalizer.CommandNormalizer.CommandAndNumArgs;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandNormalizer.CommandNormalizer.Eval;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandNormalizer.CommandNormalizer.KeepAllArgs;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandNormalizer.CommandNormalizer.MultiKeyValue;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.CommandAndNumArgs;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.Eval;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.KeepAllArgs;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.MultiKeyValue;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -38,42 +37,42 @@ import java.util.Map;
|
|||
* </tr>
|
||||
* </table>
|
||||
*/
|
||||
public final class RedisCommandNormalizer {
|
||||
public final class RedisCommandSanitizer {
|
||||
|
||||
private static final Map<String, CommandNormalizer> NORMALIZERS;
|
||||
private static final CommandNormalizer DEFAULT = new CommandAndNumArgs(0);
|
||||
private static final Map<String, CommandSanitizer> SANITIZERS;
|
||||
private static final CommandSanitizer DEFAULT = new CommandAndNumArgs(0);
|
||||
|
||||
static {
|
||||
Map<String, CommandNormalizer> normalizers = new HashMap<>();
|
||||
Map<String, CommandSanitizer> sanitizers = new HashMap<>();
|
||||
|
||||
CommandNormalizer keepOneArg = new CommandAndNumArgs(1);
|
||||
CommandNormalizer keepTwoArgs = new CommandAndNumArgs(2);
|
||||
CommandNormalizer setMultiHashField = new MultiKeyValue(1);
|
||||
CommandNormalizer setMultiField = new MultiKeyValue(0);
|
||||
CommandSanitizer keepOneArg = new CommandAndNumArgs(1);
|
||||
CommandSanitizer keepTwoArgs = new CommandAndNumArgs(2);
|
||||
CommandSanitizer setMultiHashField = new MultiKeyValue(1);
|
||||
CommandSanitizer setMultiField = new MultiKeyValue(0);
|
||||
|
||||
// Cluster
|
||||
for (String command : asList("CLUSTER", "READONLY", "READWRITE")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Connection
|
||||
normalizers.put("AUTH", DEFAULT);
|
||||
sanitizers.put("AUTH", DEFAULT);
|
||||
// HELLO can contain AUTH data
|
||||
normalizers.put("HELLO", keepTwoArgs);
|
||||
sanitizers.put("HELLO", keepTwoArgs);
|
||||
for (String command : asList("CLIENT", "ECHO", "PING", "QUIT", "SELECT")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Geo
|
||||
for (String command :
|
||||
asList("GEOADD", "GEODIST", "GEOHASH", "GEOPOS", "GEORADIUS", "GEORADIUSBYMEMBER")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Hashes
|
||||
normalizers.put("HMSET", setMultiHashField);
|
||||
normalizers.put("HSET", setMultiHashField);
|
||||
normalizers.put("HSETNX", keepTwoArgs);
|
||||
sanitizers.put("HMSET", setMultiHashField);
|
||||
sanitizers.put("HSET", setMultiHashField);
|
||||
sanitizers.put("HSETNX", keepTwoArgs);
|
||||
for (String command :
|
||||
asList(
|
||||
"HDEL",
|
||||
|
@ -88,19 +87,19 @@ public final class RedisCommandNormalizer {
|
|||
"HSCAN",
|
||||
"HSTRLEN",
|
||||
"HVALS")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// HyperLogLog
|
||||
normalizers.put("PFADD", keepOneArg);
|
||||
sanitizers.put("PFADD", keepOneArg);
|
||||
for (String command : asList("PFCOUNT", "PFMERGE")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Keys
|
||||
// MIGRATE can contain AUTH data
|
||||
normalizers.put("MIGRATE", new CommandAndNumArgs(6));
|
||||
normalizers.put("RESTORE", keepTwoArgs);
|
||||
sanitizers.put("MIGRATE", new CommandAndNumArgs(6));
|
||||
sanitizers.put("RESTORE", keepTwoArgs);
|
||||
for (String command :
|
||||
asList(
|
||||
"DEL",
|
||||
|
@ -125,18 +124,18 @@ public final class RedisCommandNormalizer {
|
|||
"TYPE",
|
||||
"UNLINK",
|
||||
"WAIT")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Lists
|
||||
normalizers.put("LINSERT", keepTwoArgs);
|
||||
normalizers.put("LPOS", keepOneArg);
|
||||
normalizers.put("LPUSH", keepOneArg);
|
||||
normalizers.put("LPUSHX", keepOneArg);
|
||||
normalizers.put("LREM", keepOneArg);
|
||||
normalizers.put("LSET", keepOneArg);
|
||||
normalizers.put("RPUSH", keepOneArg);
|
||||
normalizers.put("RPUSHX", keepOneArg);
|
||||
sanitizers.put("LINSERT", keepTwoArgs);
|
||||
sanitizers.put("LPOS", keepOneArg);
|
||||
sanitizers.put("LPUSH", keepOneArg);
|
||||
sanitizers.put("LPUSHX", keepOneArg);
|
||||
sanitizers.put("LREM", keepOneArg);
|
||||
sanitizers.put("LSET", keepOneArg);
|
||||
sanitizers.put("RPUSH", keepOneArg);
|
||||
sanitizers.put("RPUSHX", keepOneArg);
|
||||
for (String command :
|
||||
asList(
|
||||
"BLMOVE",
|
||||
|
@ -151,24 +150,24 @@ public final class RedisCommandNormalizer {
|
|||
"LTRIM",
|
||||
"RPOP",
|
||||
"RPOPLPUSH")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Pub/Sub
|
||||
normalizers.put("PUBLISH", keepOneArg);
|
||||
sanitizers.put("PUBLISH", keepOneArg);
|
||||
for (String command :
|
||||
asList("PSUBSCRIBE", "PUBSUB", "PUNSUBSCRIBE", "SUBSCRIBE", "UNSUBSCRIBE")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Scripting
|
||||
normalizers.put("EVAL", Eval.INSTANCE);
|
||||
normalizers.put("EVALSHA", Eval.INSTANCE);
|
||||
normalizers.put("SCRIPT", KeepAllArgs.INSTANCE);
|
||||
sanitizers.put("EVAL", Eval.INSTANCE);
|
||||
sanitizers.put("EVALSHA", Eval.INSTANCE);
|
||||
sanitizers.put("SCRIPT", KeepAllArgs.INSTANCE);
|
||||
|
||||
// Server
|
||||
// CONFIG SET can set any property, including the master password
|
||||
normalizers.put("CONFIG", keepTwoArgs);
|
||||
sanitizers.put("CONFIG", keepTwoArgs);
|
||||
for (String command :
|
||||
asList(
|
||||
"ACL",
|
||||
|
@ -196,15 +195,15 @@ public final class RedisCommandNormalizer {
|
|||
"SWAPDB",
|
||||
"SYNC",
|
||||
"TIME")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Sets
|
||||
normalizers.put("SADD", keepOneArg);
|
||||
normalizers.put("SISMEMBER", keepOneArg);
|
||||
normalizers.put("SMISMEMBER", keepOneArg);
|
||||
normalizers.put("SMOVE", keepTwoArgs);
|
||||
normalizers.put("SREM", keepOneArg);
|
||||
sanitizers.put("SADD", keepOneArg);
|
||||
sanitizers.put("SISMEMBER", keepOneArg);
|
||||
sanitizers.put("SMISMEMBER", keepOneArg);
|
||||
sanitizers.put("SMOVE", keepTwoArgs);
|
||||
sanitizers.put("SREM", keepOneArg);
|
||||
for (String command :
|
||||
asList(
|
||||
"SCARD",
|
||||
|
@ -218,25 +217,25 @@ public final class RedisCommandNormalizer {
|
|||
"SSCAN",
|
||||
"SUNION",
|
||||
"SUNIONSTORE")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Sorted Sets
|
||||
normalizers.put("ZADD", keepOneArg);
|
||||
normalizers.put("ZCOUNT", keepOneArg);
|
||||
normalizers.put("ZINCRBY", keepOneArg);
|
||||
normalizers.put("ZLEXCOUNT", keepOneArg);
|
||||
normalizers.put("ZMSCORE", keepOneArg);
|
||||
normalizers.put("ZRANGEBYLEX", keepOneArg);
|
||||
normalizers.put("ZRANGEBYSCORE", keepOneArg);
|
||||
normalizers.put("ZRANK", keepOneArg);
|
||||
normalizers.put("ZREM", keepOneArg);
|
||||
normalizers.put("ZREMRANGEBYLEX", keepOneArg);
|
||||
normalizers.put("ZREMRANGEBYSCORE", keepOneArg);
|
||||
normalizers.put("ZREVRANGEBYLEX", keepOneArg);
|
||||
normalizers.put("ZREVRANGEBYSCORE", keepOneArg);
|
||||
normalizers.put("ZREVRANK", keepOneArg);
|
||||
normalizers.put("ZSCORE", keepOneArg);
|
||||
sanitizers.put("ZADD", keepOneArg);
|
||||
sanitizers.put("ZCOUNT", keepOneArg);
|
||||
sanitizers.put("ZINCRBY", keepOneArg);
|
||||
sanitizers.put("ZLEXCOUNT", keepOneArg);
|
||||
sanitizers.put("ZMSCORE", keepOneArg);
|
||||
sanitizers.put("ZRANGEBYLEX", keepOneArg);
|
||||
sanitizers.put("ZRANGEBYSCORE", keepOneArg);
|
||||
sanitizers.put("ZRANK", keepOneArg);
|
||||
sanitizers.put("ZREM", keepOneArg);
|
||||
sanitizers.put("ZREMRANGEBYLEX", keepOneArg);
|
||||
sanitizers.put("ZREMRANGEBYSCORE", keepOneArg);
|
||||
sanitizers.put("ZREVRANGEBYLEX", keepOneArg);
|
||||
sanitizers.put("ZREVRANGEBYSCORE", keepOneArg);
|
||||
sanitizers.put("ZREVRANK", keepOneArg);
|
||||
sanitizers.put("ZSCORE", keepOneArg);
|
||||
for (String command :
|
||||
asList(
|
||||
"BZPOPMAX",
|
||||
|
@ -252,11 +251,11 @@ public final class RedisCommandNormalizer {
|
|||
"ZSCAN",
|
||||
"ZUNION",
|
||||
"ZUNIONSTORE")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Streams
|
||||
normalizers.put("XADD", new MultiKeyValue(2));
|
||||
sanitizers.put("XADD", new MultiKeyValue(2));
|
||||
for (String command :
|
||||
asList(
|
||||
"XACK",
|
||||
|
@ -271,19 +270,19 @@ public final class RedisCommandNormalizer {
|
|||
"XREADGROUP",
|
||||
"XREVRANGE",
|
||||
"XTRIM")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Strings
|
||||
normalizers.put("APPEND", keepOneArg);
|
||||
normalizers.put("GETSET", keepOneArg);
|
||||
normalizers.put("MSET", setMultiField);
|
||||
normalizers.put("MSETNX", setMultiField);
|
||||
normalizers.put("PSETEX", keepTwoArgs);
|
||||
normalizers.put("SET", keepOneArg);
|
||||
normalizers.put("SETEX", keepTwoArgs);
|
||||
normalizers.put("SETNX", keepOneArg);
|
||||
normalizers.put("SETRANGE", keepOneArg);
|
||||
sanitizers.put("APPEND", keepOneArg);
|
||||
sanitizers.put("GETSET", keepOneArg);
|
||||
sanitizers.put("MSET", setMultiField);
|
||||
sanitizers.put("MSETNX", setMultiField);
|
||||
sanitizers.put("PSETEX", keepTwoArgs);
|
||||
sanitizers.put("SET", keepOneArg);
|
||||
sanitizers.put("SETEX", keepTwoArgs);
|
||||
sanitizers.put("SETNX", keepOneArg);
|
||||
sanitizers.put("SETRANGE", keepOneArg);
|
||||
for (String command :
|
||||
asList(
|
||||
"BITCOUNT",
|
||||
|
@ -302,37 +301,26 @@ public final class RedisCommandNormalizer {
|
|||
"SETBIT",
|
||||
"STRALGO",
|
||||
"STRLEN")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
// Transactions
|
||||
for (String command : asList("DISCARD", "EXEC", "MULTI", "UNWATCH", "WATCH")) {
|
||||
normalizers.put(command, KeepAllArgs.INSTANCE);
|
||||
sanitizers.put(command, KeepAllArgs.INSTANCE);
|
||||
}
|
||||
|
||||
NORMALIZERS = unmodifiableMap(normalizers);
|
||||
SANITIZERS = unmodifiableMap(sanitizers);
|
||||
}
|
||||
|
||||
private final boolean normalizationEnabled;
|
||||
|
||||
public RedisCommandNormalizer(String... instrumentationNames) {
|
||||
this(isQueryNormalizationEnabled(instrumentationNames));
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
RedisCommandNormalizer(boolean normalizationEnabled) {
|
||||
this.normalizationEnabled = normalizationEnabled;
|
||||
}
|
||||
|
||||
public String normalize(String command, List<?> args) {
|
||||
if (!normalizationEnabled) {
|
||||
return KeepAllArgs.INSTANCE.normalize(command, args);
|
||||
public static String sanitize(String command, List<?> args) {
|
||||
if (!StatementSanitizationConfig.isStatementSanitizationEnabled()) {
|
||||
return KeepAllArgs.INSTANCE.sanitize(command, args);
|
||||
}
|
||||
return NORMALIZERS.getOrDefault(command.toUpperCase(), DEFAULT).normalize(command, args);
|
||||
return SANITIZERS.getOrDefault(command.toUpperCase(), DEFAULT).sanitize(command, args);
|
||||
}
|
||||
|
||||
public interface CommandNormalizer {
|
||||
String normalize(String command, List<?> args);
|
||||
public interface CommandSanitizer {
|
||||
String sanitize(String command, List<?> args);
|
||||
|
||||
static String argToString(Object arg) {
|
||||
if (arg instanceof byte[]) {
|
||||
|
@ -342,22 +330,22 @@ public final class RedisCommandNormalizer {
|
|||
}
|
||||
}
|
||||
|
||||
enum KeepAllArgs implements CommandNormalizer {
|
||||
enum KeepAllArgs implements CommandSanitizer {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String normalize(String command, List<?> args) {
|
||||
StringBuilder normalised = new StringBuilder(command);
|
||||
public String sanitize(String command, List<?> args) {
|
||||
StringBuilder sanitized = new StringBuilder(command);
|
||||
for (Object arg : args) {
|
||||
normalised.append(" ").append(argToString(arg));
|
||||
sanitized.append(" ").append(argToString(arg));
|
||||
}
|
||||
return normalised.toString();
|
||||
return sanitized.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// keeps only a chosen number of arguments
|
||||
// example for num=2: CMD arg1 arg2 ? ?
|
||||
class CommandAndNumArgs implements CommandNormalizer {
|
||||
class CommandAndNumArgs implements CommandSanitizer {
|
||||
private final int numOfArgsToKeep;
|
||||
|
||||
public CommandAndNumArgs(int numOfArgsToKeep) {
|
||||
|
@ -365,21 +353,21 @@ public final class RedisCommandNormalizer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String normalize(String command, List<?> args) {
|
||||
StringBuilder normalised = new StringBuilder(command);
|
||||
public String sanitize(String command, List<?> args) {
|
||||
StringBuilder sanitized = new StringBuilder(command);
|
||||
for (int i = 0; i < numOfArgsToKeep && i < args.size(); ++i) {
|
||||
normalised.append(" ").append(argToString(args.get(i)));
|
||||
sanitized.append(" ").append(argToString(args.get(i)));
|
||||
}
|
||||
for (int i = numOfArgsToKeep; i < args.size(); ++i) {
|
||||
normalised.append(" ?");
|
||||
sanitized.append(" ?");
|
||||
}
|
||||
return normalised.toString();
|
||||
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 CommandNormalizer {
|
||||
class MultiKeyValue implements CommandSanitizer {
|
||||
private final int numOfArgsBeforeKeyValue;
|
||||
|
||||
public MultiKeyValue(int numOfArgsBeforeKeyValue) {
|
||||
|
@ -387,27 +375,27 @@ public final class RedisCommandNormalizer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String normalize(String command, List<?> args) {
|
||||
StringBuilder normalised = new StringBuilder(command);
|
||||
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) {
|
||||
normalised.append(" ").append(argToString(args.get(i)));
|
||||
sanitized.append(" ").append(argToString(args.get(i)));
|
||||
}
|
||||
|
||||
// loop over keys only
|
||||
for (int i = numOfArgsBeforeKeyValue; i < args.size(); i += 2) {
|
||||
normalised.append(" ").append(argToString(args.get(i))).append(" ?");
|
||||
sanitized.append(" ").append(argToString(args.get(i))).append(" ?");
|
||||
}
|
||||
return normalised.toString();
|
||||
return sanitized.toString();
|
||||
}
|
||||
}
|
||||
|
||||
enum Eval implements CommandNormalizer {
|
||||
enum Eval implements CommandSanitizer {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String normalize(String command, List<?> args) {
|
||||
StringBuilder normalised = new StringBuilder(command);
|
||||
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;
|
||||
|
@ -421,13 +409,13 @@ public final class RedisCommandNormalizer {
|
|||
int i = 0;
|
||||
// log the script, number of keys and all keys
|
||||
for (; i < (numberOfKeys + 2) && i < args.size(); ++i) {
|
||||
normalised.append(" ").append(argToString(args.get(i)));
|
||||
sanitized.append(" ").append(argToString(args.get(i)));
|
||||
}
|
||||
// mask the rest
|
||||
for (; i < args.size(); ++i) {
|
||||
normalised.append(" ?");
|
||||
sanitized.append(" ?");
|
||||
}
|
||||
return normalised.toString();
|
||||
return sanitized.toString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.api.db;
|
||||
|
||||
public final class SqlSanitizer {
|
||||
|
||||
public static SqlStatementInfo sanitize(String statement) {
|
||||
if (statement == null) {
|
||||
return new SqlStatementInfo(null, null, null);
|
||||
}
|
||||
return AutoSqlSanitizer.sanitize(statement);
|
||||
}
|
||||
|
||||
private SqlSanitizer() {}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.api.db;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.api.db.StatementSanitizationConfig.isStatementSanitizationEnabled;
|
||||
|
||||
import io.opentelemetry.javaagent.instrumentation.api.BoundedCache;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class is responsible for masking potentially sensitive parameters in SQL (and SQL-like)
|
||||
* statements and queries.
|
||||
*/
|
||||
public final class SqlStatementSanitizer {
|
||||
private static final Logger log = LoggerFactory.getLogger(SqlStatementSanitizer.class);
|
||||
|
||||
private static final BoundedCache<String, SqlStatementInfo> sqlToStatementInfoCache =
|
||||
BoundedCache.build(1000);
|
||||
|
||||
public static SqlStatementInfo sanitize(String statement) {
|
||||
if (!isStatementSanitizationEnabled() || statement == null) {
|
||||
return new SqlStatementInfo(statement, null, null);
|
||||
}
|
||||
return sqlToStatementInfoCache.get(
|
||||
statement,
|
||||
k -> {
|
||||
log.trace("SQL statement cache miss");
|
||||
return AutoSqlSanitizer.sanitize(statement);
|
||||
});
|
||||
}
|
||||
|
||||
private SqlStatementSanitizer() {}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.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().getBooleanProperty("otel.instrumentation.db-statement-sanitizer.enabled", true);
|
||||
|
||||
static boolean isStatementSanitizationEnabled() {
|
||||
return STATEMENT_SANITIZATION_ENABLED;
|
||||
}
|
||||
|
||||
private StatementSanitizationConfig() {}
|
||||
}
|
|
@ -8,27 +8,14 @@ package io.opentelemetry.javaagent.instrumentation.api.db
|
|||
import spock.lang.Specification
|
||||
import spock.lang.Unroll
|
||||
|
||||
class RedisCommandNormalizerTest extends Specification {
|
||||
def normalizer = new RedisCommandNormalizer("redis")
|
||||
|
||||
def "should not normalize anything when turned off"() {
|
||||
given:
|
||||
def normalizer = new RedisCommandNormalizer(false)
|
||||
|
||||
when:
|
||||
def result = normalizer.normalize("AUTH", ["user", "password"])
|
||||
|
||||
then:
|
||||
result == "AUTH user password"
|
||||
}
|
||||
|
||||
class RedisCommandSanitizerTest extends Specification {
|
||||
@Unroll
|
||||
def "should normalize #expected"() {
|
||||
def "should sanitize #expected"() {
|
||||
when:
|
||||
def normalised = normalizer.normalize(command, args)
|
||||
def sanitized = RedisCommandSanitizer.sanitize(command, args)
|
||||
|
||||
then:
|
||||
normalised == expected
|
||||
sanitized == expected
|
||||
|
||||
where:
|
||||
command | args | expected
|
||||
|
@ -103,10 +90,10 @@ class RedisCommandNormalizerTest extends Specification {
|
|||
def args = ["arg1", "arg 2"]
|
||||
|
||||
when:
|
||||
def normalised = normalizer.normalize(command, args)
|
||||
def sanitized = RedisCommandSanitizer.sanitize(command, args)
|
||||
|
||||
then:
|
||||
normalised == command + " " + args.join(" ")
|
||||
sanitized == command + " " + args.join(" ")
|
||||
|
||||
where:
|
||||
command << [
|
||||
|
@ -153,9 +140,9 @@ class RedisCommandNormalizerTest extends Specification {
|
|||
|
||||
def "should mask all arguments of an unknown command"() {
|
||||
when:
|
||||
def normalised = normalizer.normalize("NEWAUTH", ["password", "secret"])
|
||||
def sanitized = RedisCommandSanitizer.sanitize("NEWAUTH", ["password", "secret"])
|
||||
|
||||
then:
|
||||
normalised == "NEWAUTH ? ?"
|
||||
sanitized == "NEWAUTH ? ?"
|
||||
}
|
||||
}
|
|
@ -10,17 +10,17 @@ import spock.lang.Timeout
|
|||
import spock.lang.Unroll
|
||||
|
||||
@Timeout(20)
|
||||
class SqlSanitizerTest extends Specification {
|
||||
class SqlStatementSanitizerTest extends Specification {
|
||||
|
||||
def "normalize #originalSql"() {
|
||||
setup:
|
||||
def actualSanitized = SqlSanitizer.sanitize(originalSql)
|
||||
def actualSanitized = SqlStatementSanitizer.sanitize(originalSql)
|
||||
|
||||
expect:
|
||||
actualSanitized.getFullStatement() == normalizedSql
|
||||
actualSanitized.getFullStatement() == sanitizedSql
|
||||
|
||||
where:
|
||||
originalSql | normalizedSql
|
||||
originalSql | sanitizedSql
|
||||
// Numbers
|
||||
"SELECT * FROM TABLE WHERE FIELD=1234" | "SELECT * FROM TABLE WHERE FIELD=?"
|
||||
"SELECT * FROM TABLE WHERE FIELD = 1234" | "SELECT * FROM TABLE WHERE FIELD = ?"
|
||||
|
@ -87,7 +87,7 @@ class SqlSanitizerTest extends Specification {
|
|||
@Unroll
|
||||
def "should simplify #sql"() {
|
||||
expect:
|
||||
SqlSanitizer.sanitize(sql) == expected
|
||||
SqlStatementSanitizer.sanitize(sql) == expected
|
||||
|
||||
where:
|
||||
sql | expected
|
||||
|
@ -144,14 +144,14 @@ class SqlSanitizerTest extends Specification {
|
|||
|
||||
expect:
|
||||
def sanitizedQuery = query.replace('=123', '=?').substring(0, AutoSqlSanitizer.LIMIT)
|
||||
SqlSanitizer.sanitize(query) == new SqlStatementInfo(sanitizedQuery, "SELECT", "table")
|
||||
SqlStatementSanitizer.sanitize(query) == new SqlStatementInfo(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 SqlSanitizer.sanitize(s) != null
|
||||
assert SqlStatementSanitizer.sanitize(s) != null
|
||||
s += "'"
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ class SqlSanitizerTest extends Specification {
|
|||
for (int i = 0; i < 10000; i++) {
|
||||
s += String.valueOf(i)
|
||||
}
|
||||
assert "?" == SqlSanitizer.sanitize(s).getFullStatement()
|
||||
assert "?" == SqlStatementSanitizer.sanitize(s).getFullStatement()
|
||||
}
|
||||
|
||||
def "very long numbers at end of table name don't cause problem"() {
|
||||
|
@ -171,7 +171,7 @@ class SqlSanitizerTest extends Specification {
|
|||
for (int i = 0; i < 10000; i++) {
|
||||
s += String.valueOf(i)
|
||||
}
|
||||
assert s.substring(0, AutoSqlSanitizer.LIMIT) == SqlSanitizer.sanitize(s).getFullStatement()
|
||||
assert s.substring(0, AutoSqlSanitizer.LIMIT) == SqlStatementSanitizer.sanitize(s).getFullStatement()
|
||||
}
|
||||
|
||||
def "test 32k truncation"() {
|
||||
|
@ -180,7 +180,7 @@ class SqlSanitizerTest extends Specification {
|
|||
for (int i = 0; i < 10000; i++) {
|
||||
s.append("SELECT * FROM TABLE WHERE FIELD = 1234 AND ")
|
||||
}
|
||||
String sanitized = SqlSanitizer.sanitize(s.toString()).getFullStatement()
|
||||
String sanitized = SqlStatementSanitizer.sanitize(s.toString()).getFullStatement()
|
||||
System.out.println(sanitized.length())
|
||||
assert sanitized.length() <= AutoSqlSanitizer.LIMIT
|
||||
assert !sanitized.contains("1234")
|
||||
|
@ -194,7 +194,7 @@ class SqlSanitizerTest extends Specification {
|
|||
for (int c = 0; c < 1000; c++) {
|
||||
sb.append((char) r.nextInt((int) Character.MAX_VALUE))
|
||||
}
|
||||
SqlSanitizer.sanitize(sb.toString())
|
||||
SqlStatementSanitizer.sanitize(sb.toString())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue