diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizer.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizer.java index 1f59f804b9bc..f729c0c53357 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizer.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizer.java @@ -52,7 +52,7 @@ public final class RedisCommandSanitizer { CommandSanitizer setMultiField = new MultiKeyValue(0); // Cluster - for (String command : asList("CLUSTER", "READONLY", "READWRITE")) { + for (String command : asList("CLUSTER", "FAILOVER", "READONLY", "READWRITE")) { sanitizers.put(command, KeepAllArgs.INSTANCE); } @@ -60,13 +60,23 @@ public final class RedisCommandSanitizer { sanitizers.put("AUTH", DEFAULT); // HELLO can contain AUTH data sanitizers.put("HELLO", keepTwoArgs); - for (String command : asList("CLIENT", "ECHO", "PING", "QUIT", "SELECT")) { + for (String command : asList("CLIENT", "ECHO", "PING", "QUIT", "RESET", "SELECT")) { sanitizers.put(command, KeepAllArgs.INSTANCE); } // Geo for (String command : - asList("GEOADD", "GEODIST", "GEOHASH", "GEOPOS", "GEORADIUS", "GEORADIUSBYMEMBER")) { + asList( + "GEOADD", + "GEODIST", + "GEOHASH", + "GEOPOS", + "GEORADIUS", + "GEORADIUS_RO", + "GEORADIUSBYMEMBER", + "GEORADIUSBYMEMBER_RO", + "GEOSEARCH", + "GEOSEARCHSTORE")) { sanitizers.put(command, KeepAllArgs.INSTANCE); } @@ -85,6 +95,7 @@ public final class RedisCommandSanitizer { "HKEYS", "HLEN", "HMGET", + "HRANDFIELD", "HSCAN", "HSTRLEN", "HVALS")) { @@ -103,23 +114,27 @@ public final class RedisCommandSanitizer { sanitizers.put("RESTORE", keepTwoArgs); for (String command : asList( + "COPY", "DEL", "DUMP", "EXISTS", "EXPIRE", "EXPIREAT", + "EXPIRETIME", "KEYS", "MOVE", "OBJECT", "PERSIST", "PEXPIRE", "PEXPIREAT", + "PEXPIRETIME", "PTTL", "RANDOMKEY", "RENAME", "RENAMENX", "SCAN", "SORT", + "SORT_RO", "TOUCH", "TTL", "TYPE", @@ -140,12 +155,14 @@ public final class RedisCommandSanitizer { for (String command : asList( "BLMOVE", + "BLMPOP", "BLPOP", "BRPOP", "BRPOPLPUSH", "LINDEX", "LLEN", "LMOVE", + "LMPOP", "LPOP", "LRANGE", "LTRIM", @@ -157,13 +174,23 @@ public final class RedisCommandSanitizer { // Pub/Sub sanitizers.put("PUBLISH", keepOneArg); for (String command : - asList("PSUBSCRIBE", "PUBSUB", "PUNSUBSCRIBE", "SUBSCRIBE", "UNSUBSCRIBE")) { + asList( + "PSUBSCRIBE", + "PUBSUB", + "PUNSUBSCRIBE", + "SPUBLISH", + "SSUBSCRIBE", + "SUBSCRIBE", + "SUNSUBSCRIBE", + "UNSUBSCRIBE")) { sanitizers.put(command, KeepAllArgs.INSTANCE); } // Scripting sanitizers.put("EVAL", Eval.INSTANCE); + sanitizers.put("EVAL_RO", Eval.INSTANCE); sanitizers.put("EVALSHA", Eval.INSTANCE); + sanitizers.put("EVALSHA_RO", Eval.INSTANCE); sanitizers.put("SCRIPT", KeepAllArgs.INSTANCE); // Server @@ -211,6 +238,7 @@ public final class RedisCommandSanitizer { "SDIFF", "SDIFFSTORE", "SINTER", + "SINTERCARD", "SINTERSTORE", "SMEMBERS", "SPOP", @@ -239,14 +267,21 @@ public final class RedisCommandSanitizer { sanitizers.put("ZSCORE", keepOneArg); for (String command : asList( + "BZMPOP", "BZPOPMAX", "BZPOPMIN", "ZCARD", + "ZDIFF", + "ZDIFFSTORE", "ZINTER", + "ZINTERCARD", "ZINTERSTORE", + "ZMPOP", "ZPOPMAX", "ZPOPMIN", + "ZRANDMEMBER", "ZRANGE", + "ZRANGESTORE", "ZREMRANGEBYRANK", "ZREVRANGE", "ZSCAN", @@ -260,6 +295,7 @@ public final class RedisCommandSanitizer { for (String command : asList( "XACK", + "XAUTOCLAIM", "XCLAIM", "XDEL", "XGROUP", @@ -288,16 +324,20 @@ public final class RedisCommandSanitizer { asList( "BITCOUNT", "BITFIELD", + "BITFIELD_RO", "BITOP", "BITPOS", "DECR", "DECRBY", "GET", "GETBIT", + "GETDEL", + "GETEX", "GETRANGE", "INCR", "INCRBY", "INCRBYFLOAT", + "LCS", "MGET", "SETBIT", "STRALGO", @@ -329,7 +369,7 @@ static String argToString(Object arg) { if (arg instanceof byte[]) { return new String((byte[]) arg, StandardCharsets.UTF_8); } else { - return arg.toString(); + return String.valueOf(arg); } } diff --git a/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts b/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts index fbd28a3498aa..7addcb7e5182 100644 --- a/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts +++ b/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts @@ -17,6 +17,8 @@ dependencies { compileOnly("com.google.auto.value:auto-value-annotations") annotationProcessor("com.google.auto.value:auto-value") + implementation(project(":instrumentation:jedis:jedis-common:javaagent")) + testInstrumentation(project(":instrumentation:jedis:jedis-3.0:javaagent")) testInstrumentation(project(":instrumentation:jedis:jedis-4.0:javaagent")) diff --git a/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisConnectionInstrumentation.java b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisConnectionInstrumentation.java index 8cd6edfe8866..07102005d632 100644 --- a/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisConnectionInstrumentation.java +++ b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisConnectionInstrumentation.java @@ -18,6 +18,7 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.jedis.JedisRequestContext; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -32,8 +33,6 @@ public ElementMatcher typeMatcher() { @Override public void transform(TypeTransformer transformer) { - // FIXME: This instrumentation only incorporates sending the command, not processing the - // result. transformer.applyAdviceToMethod( isMethod() .and(named("sendCommand")) @@ -80,7 +79,7 @@ public static void stopSpan( } scope.close(); - instrumenter().end(context, request, null, throwable); + JedisRequestContext.endIfNotAttached(instrumenter(), context, request, throwable); } } @@ -116,7 +115,7 @@ public static void stopSpan( } scope.close(); - instrumenter().end(context, request, null, throwable); + JedisRequestContext.endIfNotAttached(instrumenter(), context, request, throwable); } } } diff --git a/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisInstrumentation.java b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisInstrumentation.java new file mode 100644 index 000000000000..449174872ffc --- /dev/null +++ b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisInstrumentation.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jedis.v1_4; + +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.isStatic; +import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; +import static net.bytebuddy.matcher.ElementMatchers.not; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.jedis.JedisRequestContext; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class JedisInstrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return namedOneOf("redis.clients.jedis.Jedis", "redis.clients.jedis.BinaryJedis"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod() + .and(isPublic()) + .and(not(isStatic())) + .and( + not( + namedOneOf( + "close", + "setDataSource", + "getDB", + "isConnected", + "connect", + "resetState", + "getClient", + "disconnect"))), + this.getClass().getName() + "$JedisMethodAdvice"); + } + + @SuppressWarnings("unused") + public static class JedisMethodAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static JedisRequestContext onEnter() { + return JedisRequestContext.attach(); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit(@Advice.Enter JedisRequestContext requestContext) { + if (requestContext != null) { + requestContext.detachAndEnd(); + } + } + } +} diff --git a/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisInstrumentationModule.java b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisInstrumentationModule.java index e190b4ac0145..ebbf2fbcfe79 100644 --- a/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisInstrumentationModule.java +++ b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisInstrumentationModule.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.jedis.v1_4; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; -import static java.util.Collections.singletonList; +import static java.util.Arrays.asList; import static net.bytebuddy.matcher.ElementMatchers.not; import com.google.auto.service.AutoService; @@ -30,6 +30,6 @@ public ElementMatcher.Junction classLoaderMatcher() { @Override public List typeInstrumentations() { - return singletonList(new JedisConnectionInstrumentation()); + return asList(new JedisConnectionInstrumentation(), new JedisInstrumentation()); } } diff --git a/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts b/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts index 12e994ba640a..345a1999dee7 100644 --- a/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts +++ b/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts @@ -18,6 +18,8 @@ dependencies { compileOnly("com.google.auto.value:auto-value-annotations") annotationProcessor("com.google.auto.value:auto-value") + implementation(project(":instrumentation:jedis:jedis-common:javaagent")) + // ensures jedis-1.4 instrumentation does not load with jedis 3.0+ by failing // the tests in the event it does. The tests will end up with double spans testInstrumentation(project(":instrumentation:jedis:jedis-1.4:javaagent")) diff --git a/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisConnectionInstrumentation.java b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisConnectionInstrumentation.java index 4e5432b6bc42..0dcf17ed740e 100644 --- a/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisConnectionInstrumentation.java +++ b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisConnectionInstrumentation.java @@ -18,6 +18,7 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.jedis.JedisRequestContext; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -39,7 +40,6 @@ public void transform(TypeTransformer transformer) { .and(takesArgument(0, named("redis.clients.jedis.commands.ProtocolCommand"))) .and(takesArgument(1, is(byte[][].class))), this.getClass().getName() + "$SendCommandAdvice"); - // FIXME: This instrumentation only incorporates sending the command, not processing the result. } @SuppressWarnings("unused") @@ -74,7 +74,7 @@ public static void stopSpan( } scope.close(); - instrumenter().end(context, request, null, throwable); + JedisRequestContext.endIfNotAttached(instrumenter(), context, request, throwable); } } } diff --git a/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisInstrumentation.java b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisInstrumentation.java new file mode 100644 index 000000000000..b791b55d9597 --- /dev/null +++ b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisInstrumentation.java @@ -0,0 +1,66 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jedis.v3_0; + +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.isStatic; +import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; +import static net.bytebuddy.matcher.ElementMatchers.not; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.jedis.JedisRequestContext; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class JedisInstrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return namedOneOf("redis.clients.jedis.Jedis", "redis.clients.jedis.BinaryJedis"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod() + .and(isPublic()) + .and(not(isStatic())) + .and( + not( + namedOneOf( + "close", + "setDataSource", + "getDB", + "isConnected", + "connect", + "resetState", + "getClient", + "disconnect", + "getConnection", + "isConnected", + "isBroken", + "toString"))), + this.getClass().getName() + "$JedisMethodAdvice"); + } + + @SuppressWarnings("unused") + public static class JedisMethodAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static JedisRequestContext onEnter() { + return JedisRequestContext.attach(); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit(@Advice.Enter JedisRequestContext requestContext) { + if (requestContext != null) { + requestContext.detachAndEnd(); + } + } + } +} diff --git a/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisInstrumentationModule.java b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisInstrumentationModule.java index d5c151ef8e84..22ddf9453448 100644 --- a/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisInstrumentationModule.java +++ b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisInstrumentationModule.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.jedis.v3_0; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; -import static java.util.Collections.singletonList; +import static java.util.Arrays.asList; import static net.bytebuddy.matcher.ElementMatchers.not; import com.google.auto.service.AutoService; @@ -32,6 +32,6 @@ public ElementMatcher.Junction classLoaderMatcher() { @Override public List typeInstrumentations() { - return singletonList(new JedisConnectionInstrumentation()); + return asList(new JedisConnectionInstrumentation(), new JedisInstrumentation()); } } diff --git a/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts b/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts index 70efad480b04..a70f9ed20404 100644 --- a/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts +++ b/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts @@ -18,12 +18,16 @@ dependencies { compileOnly("com.google.auto.value:auto-value-annotations") annotationProcessor("com.google.auto.value:auto-value") + implementation(project(":instrumentation:jedis:jedis-common:javaagent")) + testInstrumentation(project(":instrumentation:jedis:jedis-1.4:javaagent")) testInstrumentation(project(":instrumentation:jedis:jedis-3.0:javaagent")) } tasks { test { + // latest dep test fails because peer ip is 0:0:0:0:0:0:0:1 instead of 127.0.0.1 + jvmArgs("-Djava.net.preferIPv4Stack=true") usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) } } diff --git a/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisConnectionInstrumentation.java b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisConnectionInstrumentation.java index e1a14993e693..bc396bc0f67a 100644 --- a/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisConnectionInstrumentation.java +++ b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisConnectionInstrumentation.java @@ -18,6 +18,7 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.jedis.JedisRequestContext; import java.net.Socket; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -47,7 +48,6 @@ public void transform(TypeTransformer transformer) { .and(takesArgument(0, named("redis.clients.jedis.commands.ProtocolCommand"))) .and(takesArgument(1, is(byte[][].class))), this.getClass().getName() + "$SendCommandAdvice"); - // FIXME: This instrumentation only incorporates sending the command, not processing the result. } @SuppressWarnings("unused") @@ -84,7 +84,7 @@ public static void stopSpan( request.setSocket(socket); scope.close(); - instrumenter().end(context, request, null, throwable); + JedisRequestContext.endIfNotAttached(instrumenter(), context, request, throwable); } } @@ -121,7 +121,7 @@ public static void stopSpan( request.setSocket(socket); scope.close(); - instrumenter().end(context, request, null, throwable); + JedisRequestContext.endIfNotAttached(instrumenter(), context, request, throwable); } } } diff --git a/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisInstrumentation.java b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisInstrumentation.java new file mode 100644 index 000000000000..81c15d841a64 --- /dev/null +++ b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisInstrumentation.java @@ -0,0 +1,66 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jedis.v4_0; + +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.isStatic; +import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; +import static net.bytebuddy.matcher.ElementMatchers.not; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.jedis.JedisRequestContext; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class JedisInstrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return namedOneOf("redis.clients.jedis.Jedis", "redis.clients.jedis.UnifiedJedis"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod() + .and(isPublic()) + .and(not(isStatic())) + .and( + not( + namedOneOf( + "close", + "setDataSource", + "getDB", + "isConnected", + "connect", + "resetState", + "getClient", + "disconnect", + "getConnection", + "isConnected", + "isBroken", + "toString"))), + this.getClass().getName() + "$JedisMethodAdvice"); + } + + @SuppressWarnings("unused") + public static class JedisMethodAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static JedisRequestContext onEnter() { + return JedisRequestContext.attach(); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit(@Advice.Enter JedisRequestContext requestContext) { + if (requestContext != null) { + requestContext.detachAndEnd(); + } + } + } +} diff --git a/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisInstrumentationModule.java b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisInstrumentationModule.java index e3a8c148af02..d875cb0c1755 100644 --- a/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisInstrumentationModule.java +++ b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisInstrumentationModule.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.jedis.v4_0; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; -import static java.util.Collections.singletonList; +import static java.util.Arrays.asList; import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; @@ -28,6 +28,6 @@ public ElementMatcher.Junction classLoaderMatcher() { @Override public List typeInstrumentations() { - return singletonList(new JedisConnectionInstrumentation()); + return asList(new JedisConnectionInstrumentation(), new JedisInstrumentation()); } } diff --git a/instrumentation/jedis/jedis-common/javaagent/build.gradle.kts b/instrumentation/jedis/jedis-common/javaagent/build.gradle.kts new file mode 100644 index 000000000000..0b6bd5f67942 --- /dev/null +++ b/instrumentation/jedis/jedis-common/javaagent/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("otel.javaagent-instrumentation") +} diff --git a/instrumentation/jedis/jedis-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/JedisRequestContext.java b/instrumentation/jedis/jedis-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/JedisRequestContext.java new file mode 100644 index 000000000000..ae05a2aef048 --- /dev/null +++ b/instrumentation/jedis/jedis-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/JedisRequestContext.java @@ -0,0 +1,65 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jedis; + +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; + +public final class JedisRequestContext { + private static final ThreadLocal> contextThreadLocal = new ThreadLocal<>(); + + private Instrumenter instrumenter; + private T request; + private Context context; + private Throwable throwable; + + private JedisRequestContext() {} + + public static JedisRequestContext attach() { + JedisRequestContext requestContext = current(); + // if there already is an active request context don't start a new one + if (requestContext != null) { + return null; + } + requestContext = new JedisRequestContext<>(); + contextThreadLocal.set(requestContext); + return requestContext; + } + + public void detachAndEnd() { + contextThreadLocal.remove(); + if (request != null) { + endSpan(instrumenter, context, request, throwable); + } + } + + /** + * Schedule ending of instrumented operation when current {@link JedisRequestContext} is closed. + */ + public static void endIfNotAttached( + Instrumenter instrumenter, Context context, T request, Throwable throwable) { + JedisRequestContext requestContext = current(); + if (requestContext == null || requestContext.request != null) { + // end the span immediately if we are already tracking a request + endSpan(instrumenter, context, request, throwable); + } else { + requestContext.instrumenter = instrumenter; + requestContext.context = context; + requestContext.request = request; + requestContext.throwable = throwable; + } + } + + @SuppressWarnings("unchecked") + private static JedisRequestContext current() { + return (JedisRequestContext) contextThreadLocal.get(); + } + + private static void endSpan( + Instrumenter instrumenter, Context context, T request, Throwable throwable) { + instrumenter.end(context, request, null, throwable); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 8813dbd522ca..2c7eb4b0956a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -236,6 +236,7 @@ include(":instrumentation:jdbc:testing") include(":instrumentation:jedis:jedis-1.4:javaagent") include(":instrumentation:jedis:jedis-3.0:javaagent") include(":instrumentation:jedis:jedis-4.0:javaagent") +include(":instrumentation:jedis:jedis-common:javaagent") include(":instrumentation:jetty:jetty-8.0:javaagent") include(":instrumentation:jetty:jetty-11.0:javaagent") include(":instrumentation:jetty:jetty-common:javaagent")