diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/tracer/utils/NetPeerUtils.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/tracer/utils/NetPeerUtils.java index c640cb97d8..b1b6d1f96b 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/tracer/utils/NetPeerUtils.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/tracer/utils/NetPeerUtils.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.api.tracer.utils; +import io.opentelemetry.common.AttributeKey; import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.trace.Span; import io.opentelemetry.trace.attributes.SemanticAttributes; @@ -30,13 +31,15 @@ public final class NetPeerUtils { setNetPeer(span, remoteAddress, remoteConnection.getPort()); } else { // Failed DNS lookup, the host string is the name. - setNetPeer(span, remoteConnection.getHostString(), null, remoteConnection.getPort()); + setNetPeer( + span::setAttribute, remoteConnection.getHostString(), null, remoteConnection.getPort()); } } } public static void setNetPeer(Span span, InetAddress remoteAddress, int port) { - setNetPeer(span, remoteAddress.getHostName(), remoteAddress.getHostAddress(), port); + setNetPeer( + span::setAttribute, remoteAddress.getHostName(), remoteAddress.getHostAddress(), port); } public static void setNetPeer(Span span, String nameOrIp, int port) { @@ -45,16 +48,20 @@ public final class NetPeerUtils { setNetPeer(span, address); } catch (IllegalArgumentException iae) { // can't create address, try setting directly - setNetPeer(span, nameOrIp, null, port); + setNetPeer(span::setAttribute, nameOrIp, null, port); } } public static void setNetPeer(Span span, String peerName, String peerIp) { - setNetPeer(span, peerName, peerIp, -1); + setNetPeer(span::setAttribute, peerName, peerIp, -1); } public static void setNetPeer(Span span, String peerName, String peerIp, int port) { + setNetPeer(span::setAttribute, peerName, peerIp, port); + } + public static void setNetPeer( + SpanAttributeSetter span, String peerName, String peerIp, int port) { if (peerName != null && !peerName.equals(peerIp)) { span.setAttribute(SemanticAttributes.NET_PEER_NAME, peerName); } @@ -81,4 +88,12 @@ public final class NetPeerUtils { return ENDPOINT_PEER_SERVICE_MAPPING.get(endpoint); } + + /** + * This helper interface allows setting attributes on both {@link Span} and {@link Span.Builder}. + */ + @FunctionalInterface + public interface SpanAttributeSetter { + void setAttribute(AttributeKey key, @Nullable T value); + } } diff --git a/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceArgSplitter.java b/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceArgSplitter.java new file mode 100644 index 0000000000..4c67d3f4bc --- /dev/null +++ b/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceArgSplitter.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.lettuce.v5_1; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.checkerframework.checker.nullness.qual.Nullable; + +public final class LettuceArgSplitter { + private static final Pattern KEY_PATTERN = + Pattern.compile("((key|value)<(?[^>]+)>|(?[0-9A-Za-z=]+))(\\s+|$)"); + + // this method removes the key|value<...> wrappers around redis keys or values and splits the args + // string + public static List splitArgs(@Nullable String args) { + if (args == null || args.isEmpty()) { + return Collections.emptyList(); + } + + List argsList = new ArrayList<>(); + Matcher m = KEY_PATTERN.matcher(args); + while (m.find()) { + String wrapped = m.group("wrapped"); + if (wrapped != null) { + argsList.add(wrapped); + } else { + argsList.add(m.group("plain")); + } + } + return argsList; + } + + private LettuceArgSplitter() {} +} diff --git a/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceClientResourcesInstrumentation.java b/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceClientResourcesInstrumentation.java index 4f1dae9372..8affd2b259 100644 --- a/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceClientResourcesInstrumentation.java +++ b/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceClientResourcesInstrumentation.java @@ -41,6 +41,13 @@ public class LettuceClientResourcesInstrumentation extends Instrumenter.Default @Override public String[] helperClassNames() { return new String[] { + packageName + ".RedisCommandNormalizer", + packageName + ".RedisCommandNormalizer$CommandNormalizer", + packageName + ".RedisCommandNormalizer$CommandNormalizer$KeepAllArgs", + packageName + ".RedisCommandNormalizer$CommandNormalizer$CommandAndNumArgs", + packageName + ".RedisCommandNormalizer$CommandNormalizer$MultiKeyValue", + packageName + ".RedisCommandNormalizer$CommandNormalizer$Eval", + packageName + ".LettuceArgSplitter", packageName + ".OpenTelemetryTracing", packageName + ".OpenTelemetryTracing$OpenTelemetryTracerProvider", packageName + ".OpenTelemetryTracing$OpenTelemetryTraceContextProvider", diff --git a/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/OpenTelemetryTracing.java b/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/OpenTelemetryTracing.java index e8576382c6..27ffd78272 100644 --- a/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/OpenTelemetryTracing.java +++ b/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/OpenTelemetryTracing.java @@ -5,6 +5,8 @@ package io.opentelemetry.javaagent.instrumentation.lettuce.v5_1; +import static io.opentelemetry.javaagent.instrumentation.lettuce.v5_1.LettuceArgSplitter.splitArgs; + import io.grpc.Context; import io.lettuce.core.tracing.TraceContext; import io.lettuce.core.tracing.TraceContextProvider; @@ -12,6 +14,8 @@ import io.lettuce.core.tracing.Tracer; import io.lettuce.core.tracing.TracerProvider; import io.lettuce.core.tracing.Tracing; import io.opentelemetry.OpenTelemetry; +import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils; +import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils.SpanAttributeSetter; import io.opentelemetry.javaagent.instrumentation.api.jdbc.DbSystem; import io.opentelemetry.trace.Span; import io.opentelemetry.trace.Span.Kind; @@ -23,7 +27,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import reactor.util.annotation.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; public enum OpenTelemetryTracing implements Tracing { INSTANCE; @@ -57,8 +61,8 @@ public enum OpenTelemetryTracing implements Tracing { if (socketAddress instanceof InetSocketAddress) { InetSocketAddress address = (InetSocketAddress) socketAddress; - return new OpenTelemetryEndpoint( - address.getAddress().getHostAddress(), address.getPort(), address.getHostString()); + String ip = address.getAddress() == null ? null : address.getAddress().getHostAddress(); + return new OpenTelemetryEndpoint(ip, address.getPort(), address.getHostString()); } return null; } @@ -96,18 +100,14 @@ public enum OpenTelemetryTracing implements Tracing { } private static class OpenTelemetryEndpoint implements Endpoint { - final String ip; + @Nullable final String ip; final int port; @Nullable final String name; - OpenTelemetryEndpoint(String ip, int port, @Nullable String name) { + OpenTelemetryEndpoint(@Nullable String ip, int port, @Nullable String name) { this.ip = ip; this.port = port; - if (!ip.equals(name)) { - this.name = name; - } else { - this.name = null; - } + this.name = name; } } @@ -175,9 +175,9 @@ public enum OpenTelemetryTracing implements Tracing { public synchronized Tracer.Span remoteEndpoint(Endpoint endpoint) { if (endpoint instanceof OpenTelemetryEndpoint) { if (span != null) { - fillEndpoint(span, (OpenTelemetryEndpoint) endpoint); + fillEndpoint(span::setAttribute, (OpenTelemetryEndpoint) endpoint); } else { - fillEndpoint(spanBuilder, (OpenTelemetryEndpoint) endpoint); + fillEndpoint(spanBuilder::setAttribute, (OpenTelemetryEndpoint) endpoint); } } return this; @@ -249,50 +249,20 @@ public enum OpenTelemetryTracing implements Tracing { public synchronized void finish() { if (span != null) { if (name != null) { - String statement = - (args != null && !args.isEmpty()) && !name.equals("AUTH") ? name + " " + args : name; + String statement = RedisCommandNormalizer.normalize(name, splitArgs(args)); span.setAttribute(SemanticAttributes.DB_STATEMENT, statement); } span.end(); } } - private static void fillEndpoint(Span.Builder span, OpenTelemetryEndpoint endpoint) { + private static void fillEndpoint(SpanAttributeSetter span, OpenTelemetryEndpoint endpoint) { span.setAttribute(SemanticAttributes.NET_TRANSPORT, "IP.TCP"); - span.setAttribute(SemanticAttributes.NET_PEER_IP, endpoint.ip); + NetPeerUtils.setNetPeer(span, endpoint.name, endpoint.ip, endpoint.port); - StringBuilder redisUrl = new StringBuilder("redis://"); - - if (endpoint.name != null) { - span.setAttribute(SemanticAttributes.NET_PEER_NAME, endpoint.name); - redisUrl.append(endpoint.name); - } else { - redisUrl.append(endpoint.ip); - } - - if (endpoint.port != 0) { - span.setAttribute(SemanticAttributes.NET_PEER_PORT, (long) endpoint.port); - redisUrl.append(":").append(endpoint.port); - } - - span.setAttribute(SemanticAttributes.DB_CONNECTION_STRING, redisUrl.toString()); - } - - private static void fillEndpoint(Span span, OpenTelemetryEndpoint endpoint) { - span.setAttribute(SemanticAttributes.NET_TRANSPORT, "IP.TCP"); - span.setAttribute(SemanticAttributes.NET_PEER_IP, endpoint.ip); - - StringBuilder redisUrl = new StringBuilder("redis://"); - - if (endpoint.name != null) { - span.setAttribute(SemanticAttributes.NET_PEER_NAME, endpoint.name); - redisUrl.append(endpoint.name); - } else { - redisUrl.append(endpoint.ip); - } - - if (endpoint.port != 0) { - span.setAttribute(SemanticAttributes.NET_PEER_PORT, (long) endpoint.port); + StringBuilder redisUrl = + new StringBuilder("redis://").append(endpoint.name != null ? endpoint.name : endpoint.ip); + if (endpoint.port > 0) { redisUrl.append(":").append(endpoint.port); } diff --git a/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/RedisCommandNormalizer.java b/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/RedisCommandNormalizer.java new file mode 100644 index 0000000000..ed89421d3c --- /dev/null +++ b/instrumentation/lettuce/lettuce-5.1/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/RedisCommandNormalizer.java @@ -0,0 +1,412 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.lettuce.v5_1; + +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableMap; + +import io.opentelemetry.javaagent.instrumentation.lettuce.v5_1.RedisCommandNormalizer.CommandNormalizer.CommandAndNumArgs; +import io.opentelemetry.javaagent.instrumentation.lettuce.v5_1.RedisCommandNormalizer.CommandNormalizer.Eval; +import io.opentelemetry.javaagent.instrumentation.lettuce.v5_1.RedisCommandNormalizer.CommandNormalizer.KeepAllArgs; +import io.opentelemetry.javaagent.instrumentation.lettuce.v5_1.RedisCommandNormalizer.CommandNormalizer.MultiKeyValue; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class is responsible for masking potentially sensitive data in Redis commands. + * + *

Examples: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Raw commandNormalized command
{@code AUTH password}{@code AUTH ?}
{@code HMSET hash creditcard 1234567887654321 address asdf}{@code HMSET hash creditcard ? address ?}
+ */ +public final class RedisCommandNormalizer { + + private static final Map NORMALIZERS; + private static final CommandNormalizer DEFAULT = new CommandAndNumArgs(0); + + static { + Map normalizers = new HashMap<>(); + + CommandNormalizer keepOneArg = new CommandAndNumArgs(1); + CommandNormalizer keepTwoArgs = new CommandAndNumArgs(2); + CommandNormalizer setMultiHashField = new MultiKeyValue(1); + CommandNormalizer setMultiField = new MultiKeyValue(0); + + // Cluster + for (String command : asList("CLUSTER", "READONLY", "READWRITE")) { + normalizers.put(command, KeepAllArgs.INSTANCE); + } + + // Connection + normalizers.put("AUTH", DEFAULT); + // HELLO can contain AUTH data + normalizers.put("HELLO", keepTwoArgs); + for (String command : asList("CLIENT", "ECHO", "PING", "QUIT", "SELECT")) { + normalizers.put(command, KeepAllArgs.INSTANCE); + } + + // Geo + for (String command : + asList("GEOADD", "GEODIST", "GEOHASH", "GEOPOS", "GEORADIUS", "GEORADIUSBYMEMBER")) { + normalizers.put(command, KeepAllArgs.INSTANCE); + } + + // Hashes + normalizers.put("HMSET", setMultiHashField); + normalizers.put("HSET", setMultiHashField); + normalizers.put("HSETNX", keepTwoArgs); + for (String command : + asList( + "HDEL", + "HEXISTS", + "HGET", + "HGETALL", + "HINCRBY", + "HINCRBYFLOAT", + "HKEYS", + "HLEN", + "HMGET", + "HSCAN", + "HSTRLEN", + "HVALS")) { + normalizers.put(command, KeepAllArgs.INSTANCE); + } + + // HyperLogLog + normalizers.put("PFADD", keepOneArg); + for (String command : asList("PFCOUNT", "PFMERGE")) { + normalizers.put(command, KeepAllArgs.INSTANCE); + } + + // Keys + // MIGRATE can contain AUTH data + normalizers.put("MIGRATE", new CommandAndNumArgs(6)); + normalizers.put("RESTORE", keepTwoArgs); + for (String command : + asList( + "DEL", + "DUMP", + "EXISTS", + "EXPIRE", + "EXPIREAT", + "KEYS", + "MOVE", + "OBJECT", + "PERSIST", + "PEXPIRE", + "PEXPIREAT", + "PTTL", + "RANDOMKEY", + "RENAME", + "RENAMENX", + "SCAN", + "SORT", + "TOUCH", + "TTL", + "TYPE", + "UNLINK", + "WAIT")) { + normalizers.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); + for (String command : + asList( + "BLMOVE", + "BLPOP", + "BRPOP", + "BRPOPLPUSH", + "LINDEX", + "LLEN", + "LMOVE", + "LPOP", + "LRANGE", + "LTRIM", + "RPOP", + "RPOPLPUSH")) { + normalizers.put(command, KeepAllArgs.INSTANCE); + } + + // Pub/Sub + normalizers.put("PUBLISH", keepOneArg); + for (String command : + asList("PSUBSCRIBE", "PUBSUB", "PUNSUBSCRIBE", "SUBSCRIBE", "UNSUBSCRIBE")) { + normalizers.put(command, KeepAllArgs.INSTANCE); + } + + // Scripting + normalizers.put("EVAL", Eval.INSTANCE); + normalizers.put("EVALSHA", Eval.INSTANCE); + normalizers.put("SCRIPT", KeepAllArgs.INSTANCE); + + // Server + // CONFIG SET can set any property, including the master password + normalizers.put("CONFIG", keepTwoArgs); + for (String command : + asList( + "ACL", + "BGREWRITEAOF", + "BGSAVE", + "COMMAND", + "DBSIZE", + "DEBUG", + "FLUSHALL", + "FLUSHDB", + "INFO", + "LASTSAVE", + "LATENCY", + "LOLWUT", + "MEMORY", + "MODULE", + "MONITOR", + "PSYNC", + "REPLICAOF", + "ROLE", + "SAVE", + "SHUTDOWN", + "SLAVEOF", + "SLOWLOG", + "SWAPDB", + "SYNC", + "TIME")) { + normalizers.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); + for (String command : + asList( + "SCARD", + "SDIFF", + "SDIFFSTORE", + "SINTER", + "SINTERSTORE", + "SMEMBERS", + "SPOP", + "SRANDMEMBER", + "SSCAN", + "SUNION", + "SUNIONSTORE")) { + normalizers.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); + for (String command : + asList( + "BZPOPMAX", + "BZPOPMIN", + "ZCARD", + "ZINTER", + "ZINTERSTORE", + "ZPOPMAX", + "ZPOPMIN", + "ZRANGE", + "ZREMRANGEBYRANK", + "ZREVRANGE", + "ZSCAN", + "ZUNION", + "ZUNIONSTORE")) { + normalizers.put(command, KeepAllArgs.INSTANCE); + } + + // Streams + normalizers.put("XADD", new MultiKeyValue(2)); + for (String command : + asList( + "XACK", + "XCLAIM", + "XDEL", + "XGROUP", + "XINFO", + "XLEN", + "XPENDING", + "XRANGE", + "XREAD", + "XREADGROUP", + "XREVRANGE", + "XTRIM")) { + normalizers.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); + for (String command : + asList( + "BITCOUNT", + "BITFIELD", + "BITOP", + "BITPOS", + "DECR", + "DECRBY", + "GET", + "GETBIT", + "GETRANGE", + "INCR", + "INCRBY", + "INCRBYFLOAT", + "MGET", + "SETBIT", + "STRALGO", + "STRLEN")) { + normalizers.put(command, KeepAllArgs.INSTANCE); + } + + // Transactions + for (String command : asList("DISCARD", "EXEC", "MULTI", "UNWATCH", "WATCH")) { + normalizers.put(command, KeepAllArgs.INSTANCE); + } + + NORMALIZERS = unmodifiableMap(normalizers); + } + + public static String normalize(String command, List args) { + return NORMALIZERS.getOrDefault(command.toUpperCase(), DEFAULT).normalize(command, args); + } + + public interface CommandNormalizer { + String normalize(String command, List args); + + enum KeepAllArgs implements CommandNormalizer { + INSTANCE; + + @Override + public String normalize(String command, List args) { + StringBuilder normalised = new StringBuilder(command); + for (String arg : args) { + normalised.append(" ").append(arg); + } + return normalised.toString(); + } + } + + // keeps only a chosen number of arguments + // example for num=2: CMD arg1 arg2 ? ? + class CommandAndNumArgs implements CommandNormalizer { + private final int numOfArgsToKeep; + + public CommandAndNumArgs(int numOfArgsToKeep) { + this.numOfArgsToKeep = numOfArgsToKeep; + } + + @Override + public String normalize(String command, List args) { + StringBuilder normalised = new StringBuilder(command); + for (int i = 0; i < numOfArgsToKeep && i < args.size(); ++i) { + normalised.append(" ").append(args.get(i)); + } + for (int i = numOfArgsToKeep; i < args.size(); ++i) { + normalised.append(" ?"); + } + return normalised.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 { + private final int numOfArgsBeforeKeyValue; + + public MultiKeyValue(int numOfArgsBeforeKeyValue) { + this.numOfArgsBeforeKeyValue = numOfArgsBeforeKeyValue; + } + + @Override + public String normalize(String command, List args) { + StringBuilder normalised = 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(args.get(i)); + } + + // loop over keys only + for (int i = numOfArgsBeforeKeyValue; i < args.size(); i += 2) { + normalised.append(" ").append(args.get(i)).append(" ?"); + } + return normalised.toString(); + } + } + + enum Eval implements CommandNormalizer { + INSTANCE; + + @Override + public String normalize(String command, List args) { + StringBuilder normalised = 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(args.get(1)); + } catch (NumberFormatException ignored) { + } + } + + int i = 0; + // log the script, number of keys and all keys + for (; i < (numberOfKeys + 2) && i < args.size(); ++i) { + normalised.append(" ").append(args.get(i)); + } + // mask the rest + for (; i < args.size(); ++i) { + normalised.append(" ?"); + } + return normalised.toString(); + } + } + } + + private RedisCommandNormalizer() {} +} diff --git a/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceArgSplitterTest.groovy b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceArgSplitterTest.groovy new file mode 100644 index 0000000000..411b3270a4 --- /dev/null +++ b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceArgSplitterTest.groovy @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.lettuce.v5_1 + +import spock.lang.Specification +import spock.lang.Unroll + +class LettuceArgSplitterTest extends Specification { + @Unroll + def "should properly split #desc"() { + expect: + LettuceArgSplitter.splitArgs(args) == result + + where: + desc | args | result + "a null value" | null | [] + "an empty value" | "" | [] + "a single key" | "key" | ["key"] + "a single value" | "value" | ["value"] + "a plain string" | "teststring" | ["teststring"] + "an integer" | "42" | ["42"] + "a base64 value" | "TeST123==" | ["TeST123=="] + "a complex list of args" | "key aSDFgh4321= 5 test value" | ["key", "aSDFgh4321=", "5", "test", "val"] + } +} diff --git a/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceAsyncClientTest.groovy b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceAsyncClientTest.groovy index 968f5ce376..56a8a76235 100644 --- a/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceAsyncClientTest.groovy +++ b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceAsyncClientTest.groovy @@ -152,12 +152,12 @@ class LettuceAsyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "SET key value" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "SET TESTSETKEY ?" } event(0) { eventName "redis.encode.start" @@ -195,12 +195,12 @@ class LettuceAsyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "GET key" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "GET TESTKEY" } event(0) { eventName "redis.encode.start" @@ -252,12 +252,12 @@ class LettuceAsyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "GET key" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "GET NON_EXISTENT_KEY" } event(0) { eventName "redis.encode.start" @@ -295,12 +295,12 @@ class LettuceAsyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_STATEMENT.key()}" "RANDOMKEY" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_STATEMENT.key}" "RANDOMKEY" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" } event(0) { eventName "redis.encode.start" @@ -356,12 +356,12 @@ class LettuceAsyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "HMSET key key value key value key value<53>" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "HMSET TESTHM firstname ? lastname ? age ?" } event(0) { eventName "redis.encode.start" @@ -377,12 +377,12 @@ class LettuceAsyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "HGETALL key" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "HGETALL TESTHM" } event(0) { eventName "redis.encode.start" diff --git a/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceReactiveClientTest.groovy b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceReactiveClientTest.groovy index 5f6f06e7ae..d2fab28a91 100644 --- a/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceReactiveClientTest.groovy +++ b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceReactiveClientTest.groovy @@ -102,12 +102,12 @@ class LettuceReactiveClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "SET key value" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "SET TESTSETKEY ?" } event(0) { eventName "redis.encode.start" @@ -136,12 +136,12 @@ class LettuceReactiveClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "GET key" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "GET TESTKEY" } event(0) { eventName "redis.encode.start" @@ -178,12 +178,12 @@ class LettuceReactiveClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "GET key" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "GET NON_EXISTENT_KEY" } event(0) { eventName "redis.encode.start" @@ -218,12 +218,12 @@ class LettuceReactiveClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_STATEMENT.key()}" "RANDOMKEY" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_STATEMENT.key}" "RANDOMKEY" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" } event(0) { eventName "redis.encode.start" @@ -248,12 +248,12 @@ class LettuceReactiveClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_STATEMENT.key()}" "COMMAND" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_STATEMENT.key}" "COMMAND" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" } event(0) { eventName "redis.encode.start" @@ -301,12 +301,12 @@ class LettuceReactiveClientTest extends AgentTestRunner { errored false childOf span(0) attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "SET key value<1>" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "SET a ?" } event(0) { eventName "redis.encode.start" @@ -321,12 +321,12 @@ class LettuceReactiveClientTest extends AgentTestRunner { errored false childOf span(0) attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "GET key" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "GET a" } event(0) { eventName "redis.encode.start" @@ -362,12 +362,12 @@ class LettuceReactiveClientTest extends AgentTestRunner { errored false childOf span(0) attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "SET key value<1>" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "SET a ?" } event(0) { eventName "redis.encode.start" @@ -382,12 +382,12 @@ class LettuceReactiveClientTest extends AgentTestRunner { errored false childOf span(0) attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "GET key" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "GET a" } event(0) { eventName "redis.encode.start" @@ -424,12 +424,12 @@ class LettuceReactiveClientTest extends AgentTestRunner { errored false childOf span(0) attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "SET key value<1>" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "SET a ?" } event(0) { eventName "redis.encode.start" @@ -444,12 +444,12 @@ class LettuceReactiveClientTest extends AgentTestRunner { errored false childOf span(0) attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "GET key" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "GET a" } event(0) { eventName "redis.encode.start" diff --git a/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceSyncClientAuthTest.groovy b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceSyncClientAuthTest.groovy index 76d257f004..27f3652d56 100644 --- a/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceSyncClientAuthTest.groovy +++ b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceSyncClientAuthTest.groovy @@ -5,6 +5,8 @@ package io.opentelemetry.javaagent.instrumentation.lettuce.v5_1 +import static io.opentelemetry.trace.Span.Kind.CLIENT + import io.lettuce.core.ClientOptions import io.lettuce.core.RedisClient import io.opentelemetry.instrumentation.test.AgentTestRunner @@ -13,8 +15,6 @@ import io.opentelemetry.trace.attributes.SemanticAttributes import redis.embedded.RedisServer import spock.lang.Shared -import static io.opentelemetry.trace.Span.Kind.CLIENT - class LettuceSyncClientAuthTest extends AgentTestRunner { public static final String HOST = "127.0.0.1" public static final int DB_INDEX = 0 @@ -74,12 +74,12 @@ class LettuceSyncClientAuthTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "AUTH" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "AUTH ?" } event(0) { eventName "redis.encode.start" diff --git a/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceSyncClientTest.groovy b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceSyncClientTest.groovy index 340161d562..ed52bf5309 100644 --- a/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceSyncClientTest.groovy +++ b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceSyncClientTest.groovy @@ -6,10 +6,12 @@ package io.opentelemetry.javaagent.instrumentation.lettuce.v5_1 import static io.opentelemetry.trace.Span.Kind.CLIENT +import static java.nio.charset.StandardCharsets.UTF_8 import io.lettuce.core.ClientOptions import io.lettuce.core.RedisClient import io.lettuce.core.RedisConnectionException +import io.lettuce.core.ScriptOutputType import io.lettuce.core.api.StatefulConnection import io.lettuce.core.api.sync.RedisCommands import io.opentelemetry.instrumentation.test.AgentTestRunner @@ -133,12 +135,12 @@ class LettuceSyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "SET key value" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "SET TESTSETKEY ?" } event(0) { eventName "redis.encode.start" @@ -167,13 +169,13 @@ class LettuceSyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "net.peer.name" "localhost" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://localhost:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "SET key value" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_NAME.key}" "localhost" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://localhost:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "SET TESTSETKEY ?" } event(0) { eventName "redis.encode.start" @@ -199,12 +201,12 @@ class LettuceSyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "GET key" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "GET TESTKEY" } event(0) { eventName "redis.encode.start" @@ -230,12 +232,12 @@ class LettuceSyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "GET key" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "GET NON_EXISTENT_KEY" } event(0) { eventName "redis.encode.start" @@ -261,12 +263,12 @@ class LettuceSyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_STATEMENT.key()}" "RANDOMKEY" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_STATEMENT.key}" "RANDOMKEY" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" } event(0) { eventName "redis.encode.start" @@ -292,12 +294,12 @@ class LettuceSyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "LPUSH key value" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "LPUSH TESTLIST ?" } event(0) { eventName "redis.encode.start" @@ -323,12 +325,12 @@ class LettuceSyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "HMSET key key value key value key value<53>" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "HMSET user firstname ? lastname ? age ?" } event(0) { eventName "redis.encode.start" @@ -354,12 +356,83 @@ class LettuceSyncClientTest extends AgentTestRunner { kind CLIENT errored false attributes { - "${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP" - "${SemanticAttributes.NET_PEER_IP.key()}" "127.0.0.1" - "${SemanticAttributes.NET_PEER_PORT.key()}" port - "${SemanticAttributes.DB_CONNECTION_STRING.key()}" "redis://127.0.0.1:$port" - "${SemanticAttributes.DB_SYSTEM.key()}" "redis" - "${SemanticAttributes.DB_STATEMENT.key()}" "HGETALL key" + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "HGETALL TESTHM" + } + event(0) { + eventName "redis.encode.start" + } + event(1) { + eventName "redis.encode.end" + } + } + } + } + } + + def "eval command"() { + given: + def script = "redis.call('lpush', KEYS[1], ARGV[1], ARGV[2]); return redis.call('llen', KEYS[1])" + + when: + def result = syncCommands.eval(script, ScriptOutputType.INTEGER, ["TESTLIST"] as String[], "abc", "def") + + then: + result == 2 + + def b64Script = Base64.encoder.encodeToString(script.getBytes(UTF_8)) + assertTraces(1) { + trace(0, 1) { + span(0) { + name "EVAL" + kind CLIENT + errored false + attributes { + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "EVAL $b64Script 1 TESTLIST ? ?" + } + event(0) { + eventName "redis.encode.start" + } + event(1) { + eventName "redis.encode.end" + } + } + } + } + } + + def "mset command"() { + when: + def res = syncCommands.mset([ + "key1": "value1", + "key2": "value2" + ]) + + then: + res == "OK" + + assertTraces(1) { + trace(0, 1) { + span(0) { + name "MSET" + kind CLIENT + errored false + attributes { + "${SemanticAttributes.NET_TRANSPORT.key}" "IP.TCP" + "${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1" + "${SemanticAttributes.NET_PEER_PORT.key}" port + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "redis://127.0.0.1:$port" + "${SemanticAttributes.DB_SYSTEM.key}" "redis" + "${SemanticAttributes.DB_STATEMENT.key}" "MSET key1 ? key2 ?" } event(0) { eventName "redis.encode.start" diff --git a/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/RedisCommandNormalizerTest.groovy b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/RedisCommandNormalizerTest.groovy new file mode 100644 index 0000000000..9fbd0d2b40 --- /dev/null +++ b/instrumentation/lettuce/lettuce-5.1/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/RedisCommandNormalizerTest.groovy @@ -0,0 +1,148 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.lettuce.v5_1 + +import spock.lang.Specification +import spock.lang.Unroll + +class RedisCommandNormalizerTest extends Specification { + @Unroll + def "should normalize #expected"() { + when: + def normalised = RedisCommandNormalizer.normalize(command, args) + + then: + normalised == expected + + where: + command | args | expected + // Connection + "AUTH" | ["password"] | "AUTH ?" + "HELLO" | ["3", "AUTH", "username", "password"] | "HELLO 3 AUTH ? ?" + "HELLO" | ["3"] | "HELLO 3" + // Hashes + "HMSET" | ["hash", "key1", "value1", "key2", "value2"] | "HMSET hash key1 ? key2 ?" + "HSET" | ["hash", "key1", "value1", "key2", "value2"] | "HSET hash key1 ? key2 ?" + "HSETNX" | ["hash", "key", "value"] | "HSETNX hash key ?" + // HyperLogLog + "PFADD" | ["hll", "a", "b", "c"] | "PFADD hll ? ? ?" + // Keys + "MIGRATE" | ["127.0.0.1", "4242", "key", "0", "5000", "AUTH", "password"] | "MIGRATE 127.0.0.1 4242 key 0 5000 AUTH ?" + "RESTORE" | ["key", "42", "value"] | "RESTORE key 42 ?" + // Lists + "LINSERT" | ["list", "BEFORE", "value1", "value2"] | "LINSERT list BEFORE ? ?" + "LPOS" | ["list", "value"] | "LPOS list ?" + "LPUSH" | ["list", "value1", "value2"] | "LPUSH list ? ?" + "LPUSHX" | ["list", "value1", "value2"] | "LPUSHX list ? ?" + "LREM" | ["list", "2", "value"] | "LREM list ? ?" + "LSET" | ["list", "2", "value"] | "LSET list ? ?" + "RPUSH" | ["list", "value1", "value2"] | "RPUSH list ? ?" + "RPUSHX" | ["list", "value1", "value2"] | "RPUSHX list ? ?" + // Pub/Sub + "PUBLISH" | ["channel", "message"] | "PUBLISH channel ?" + // Scripting + "EVAL" | ["script", "2", "key1", "key2", "value"] | "EVAL script 2 key1 key2 ?" + "EVALSHA" | ["sha1", "0", "value1", "value2"] | "EVALSHA sha1 0 ? ?" + // Sets + "SADD" | ["set", "value1", "value2"] | "SADD set ? ?" + "SISMEMBER" | ["set", "value"] | "SISMEMBER set ?" + "SMISMEMBER" | ["set", "value1", "value2"] | "SMISMEMBER set ? ?" + "SMOVE" | ["set1", "set2", "value"] | "SMOVE set1 set2 ?" + "SREM" | ["set", "value1", "value2"] | "SREM set ? ?" + // Server + "CONFIG" | ["SET", "masterpassword", "password"] | "CONFIG SET masterpassword ?" + // Sorted Sets + "ZADD" | ["sset", "1", "value1", "2", "value2"] | "ZADD sset ? ? ? ?" + "ZCOUNT" | ["sset", "1", "10"] | "ZCOUNT sset ? ?" + "ZINCRBY" | ["sset", "1", "value"] | "ZINCRBY sset ? ?" + "ZLEXCOUNT" | ["sset", "1", "10"] | "ZLEXCOUNT sset ? ?" + "ZMSCORE" | ["sset", "value1", "value2"] | "ZMSCORE sset ? ?" + "ZRANGEBYLEX" | ["sset", "1", "10"] | "ZRANGEBYLEX sset ? ?" + "ZRANGEBYSCORE" | ["sset", "1", "10"] | "ZRANGEBYSCORE sset ? ?" + "ZRANK" | ["sset", "value"] | "ZRANK sset ?" + "ZREM" | ["sset", "value1", "value2"] | "ZREM sset ? ?" + "ZREMRANGEBYLEX" | ["sset", "1", "10"] | "ZREMRANGEBYLEX sset ? ?" + "ZREMRANGEBYSCORE" | ["sset", "1", "10"] | "ZREMRANGEBYSCORE sset ? ?" + "ZREVRANGEBYLEX" | ["sset", "1", "10"] | "ZREVRANGEBYLEX sset ? ?" + "ZREVRANGEBYSCORE" | ["sset", "1", "10"] | "ZREVRANGEBYSCORE sset ? ?" + "ZREVRANK" | ["sset", "value"] | "ZREVRANK sset ?" + "ZSCORE" | ["sset", "value"] | "ZSCORE sset ?" + // Streams + "XADD" | ["stream", "*", "key1", "value1", "key2", "value2"] | "XADD stream * key1 ? key2 ?" + // Strings + "APPEND" | ["key", "value"] | "APPEND key ?" + "GETSET" | ["key", "value"] | "GETSET key ?" + "MSET" | ["key1", "value1", "key2", "value2"] | "MSET key1 ? key2 ?" + "MSETNX" | ["key1", "value1", "key2", "value2"] | "MSETNX key1 ? key2 ?" + "PSETEX" | ["key", "10000", "value"] | "PSETEX key 10000 ?" + "SET" | ["key", "value"] | "SET key ?" + "SETEX" | ["key", "10", "value"] | "SETEX key 10 ?" + "SETNX" | ["key", "value"] | "SETNX key ?" + "SETRANGE" | ["key", "42", "value"] | "SETRANGE key ? ?" + } + + @Unroll + def "should keep all arguments of #command"() { + given: + def args = ["arg1", "arg 2"] + + when: + def normalised = RedisCommandNormalizer.normalize(command, args) + + then: + normalised == command + " " + args.join(" ") + + where: + command << [ + // Cluster + "CLUSTER", "READONLY", "READWRITE", + // Connection + "CLIENT", "ECHO", "PING", "QUIT", "SELECT", + // Geo + "GEOADD", "GEODIST", "GEOHASH", "GEOPOS", "GEORADIUS", "GEORADIUSBYMEMBER", + // Hashes + "HDEL", "HEXISTS", "HGET", "HGETALL", "HINCRBY", "HINCRBYFLOAT", "HKEYS", "HLEN", "HMGET", + "HSCAN", "HSTRLEN", "HVALS", + // HyperLogLog + "PFCOUNT", "PFMERGE", + // Keys + "DEL", "DUMP", "EXISTS", "EXPIRE", "EXPIREAT", "KEYS", "MOVE", "OBJECT", "PERSIST", "PEXPIRE", + "PEXPIREAT", "PTTL", "RANDOMKEY", "RENAME", "RENAMENX", "RESTORE", "SCAN", "SORT", "TOUCH", + "TTL", "TYPE", "UNLINK", "WAIT", + // Lists + "BLMOVE", "BLPOP", "BRPOP", "BRPOPLPUSH", "LINDEX", "LLEN", "LMOVE", "LPOP", "LRANGE", + "LTRIM", "RPOP", "RPOPLPUSH", + // Pub/Sub + "PSUBSCRIBE", "PUBSUB", "PUNSUBSCRIBE", "SUBSCRIBE", "UNSUBSCRIBE", + // Server + "ACL", "BGREWRITEAOF", "BGSAVE", "COMMAND", "DBSIZE", "DEBUG", "FLUSHALL", "FLUSHDB", "INFO", + "LASTSAVE", "LATENCY", "LOLWUT", "MEMORY", "MODULE", "MONITOR", "PSYNC", "REPLICAOF", "ROLE", + "SAVE", "SHUTDOWN", "SLAVEOF", "SLOWLOG", "SWAPDB", "SYNC", "TIME", + // Sets + "SCARD", "SDIFF", "SDIFFSTORE", "SINTER", "SINTERSTORE", "SMEMBERS", "SPOP", "SRANDMEMBER", + "SSCAN", "SUNION", "SUNIONSTORE", + // Sorted Sets + "BZPOPMAX", "BZPOPMIN", "ZCARD", "ZINTER", "ZINTERSTORE", "ZPOPMAX", "ZPOPMIN", "ZRANGE", + "ZREMRANGEBYRANK", "ZREVRANGE", "ZSCAN", "ZUNION", "ZUNIONSTORE", + // Streams + "XACK", "XCLAIM", "XDEL", "XGROUP", "XINFO", "XLEN", "XPENDING", "XRANGE", "XREAD", + "XREADGROUP", "XREVRANGE", "XTRIM", + // Strings + "BITCOUNT", "BITFIELD", "BITOP", "BITPOS", "DECR", "DECRBY", "GET", "GETBIT", "GETRANGE", + "INCR", "INCRBY", "INCRBYFLOAT", "MGET", "SETBIT", "STRALGO", "STRLEN", + // Transactions + "DISCARD", "EXEC", "MULTI", "UNWATCH", "WATCH" + ] + } + + def "should mask all arguments of an unknown command"() { + when: + def normalised = RedisCommandNormalizer.normalize("NEWAUTH", ["password", "secret"]) + + then: + normalised == "NEWAUTH ? ?" + } +}