Add support for jedis 3.0+
MODIFY
dd-java-agent/instrumentation/jedis-1.4/jedis-1.4.gradle
dd-java-agent/instrumentation/jedis-1.4/jedis-1.4.gradle
@@ -2,7 +2,7 @@ muzzle {
pass {
group = "redis.clients"
module = "jedis"
- versions = "[1.4.0,)"
+ versions = "[1.4.0,3.0.0)"
assertInverse = true
}
}

ADD
dd-java-agent/instrumentation/jedis-3.0/jedis-3.0.gradle
+muzzle {
+ pass {
+ group = "redis.clients"
+ module = "jedis"
+ versions = "[3.0.0,)"
+ assertInverse = true
+ }
+}
+
+apply from: "${rootDir}/gradle/java.gradle"
+
+apply plugin: 'org.unbroken-dome.test-sets'
+
+testSets {
+ latestDepTest {
+ dirName = 'test'
+ }
+}
+
+dependencies {
+ compileOnly group: 'redis.clients', name: 'jedis', version: '3.0.0'
+
+ testCompile group: 'com.github.kstyrc', name: 'embedded-redis', version: '0.6'
+ testCompile group: 'redis.clients', name: 'jedis', version: '3.0.0'
+
+ latestDepTestCompile group: 'redis.clients', name: 'jedis', version: '3.+'
+}

ADD
dd-java-agent/instrumentation/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisClientDecorator.java
+package datadog.trace.instrumentation.jedis30;
+
+import datadog.trace.agent.decorator.DatabaseClientDecorator;
+import datadog.trace.api.DDSpanTypes;
+import redis.clients.jedis.commands.ProtocolCommand;
+
+public class JedisClientDecorator extends DatabaseClientDecorator<ProtocolCommand> {
+ public static final JedisClientDecorator DECORATE = new JedisClientDecorator();
+
+ @Override
+ protected String[] instrumentationNames() {
+ return new String[] {"jedis", "redis"};
+ }
+
+ @Override
+ protected String service() {
+ return "redis";
+ }
+
+ @Override
+ protected String component() {
+ return "redis-command";
+ }
+
+ @Override
+ protected String spanType() {
+ return DDSpanTypes.REDIS;
+ }
+
+ @Override
+ protected String dbType() {
+ return "redis";
+ }
+
+ @Override
+ protected String dbUser(final ProtocolCommand session) {
+ return null;
+ }
+
+ @Override
+ protected String dbInstance(final ProtocolCommand session) {
+ return null;
+ }
+}

ADD
dd-java-agent/instrumentation/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisInstrumentation.java
+package datadog.trace.instrumentation.jedis30;
+
+import static datadog.trace.instrumentation.api.AgentTracer.activateSpan;
+import static datadog.trace.instrumentation.api.AgentTracer.startSpan;
+import static datadog.trace.instrumentation.jedis30.JedisClientDecorator.DECORATE;
+import static java.util.Collections.singletonMap;
+import static net.bytebuddy.matcher.ElementMatchers.isMethod;
+import static net.bytebuddy.matcher.ElementMatchers.isPublic;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
+
+import com.google.auto.service.AutoService;
+import datadog.trace.agent.tooling.Instrumenter;
+import datadog.trace.instrumentation.api.AgentScope;
+import datadog.trace.instrumentation.api.AgentSpan;
+import java.util.Map;
+import net.bytebuddy.asm.Advice;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import redis.clients.jedis.Protocol;
+import redis.clients.jedis.commands.ProtocolCommand;
+
+@AutoService(Instrumenter.class)
+public final class JedisInstrumentation extends Instrumenter.Default {
+
+ private static final String SERVICE_NAME = "redis";
+ private static final String COMPONENT_NAME = SERVICE_NAME + "-command";
+
+ public JedisInstrumentation() {
+ super("jedis", "redis");
+ }
+
+ @Override
+ public ElementMatcher<TypeDescription> typeMatcher() {
+ return named("redis.clients.jedis.Protocol");
+ }
+
+ @Override
+ public String[] helperClassNames() {
+ return new String[] {
+ "datadog.trace.agent.decorator.BaseDecorator",
+ "datadog.trace.agent.decorator.ClientDecorator",
+ "datadog.trace.agent.decorator.DatabaseClientDecorator",
+ packageName + ".JedisClientDecorator",
+ };
+ }
+
+ @Override
+ public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
+ return singletonMap(
+ isMethod()
+ .and(isPublic())
+ .and(named("sendCommand"))
+ .and(takesArgument(1, named("redis.clients.jedis.commands.ProtocolCommand"))),
+ JedisInstrumentation.class.getName() + "$JedisAdvice");
+ // FIXME: This instrumentation only incorporates sending the command, not processing the result.
+ }
+
+ public static class JedisAdvice {
+
+ @Advice.OnMethodEnter(suppress = Throwable.class)
+ public static AgentScope onEnter(@Advice.Argument(1) final ProtocolCommand command) {
+ final AgentSpan span = startSpan("redis.command");
+ DECORATE.afterStart(span);
+ if (command instanceof Protocol.Command) {
+ DECORATE.onStatement(span, ((Protocol.Command) command).name());
+ } else {
+ DECORATE.onStatement(span, new String(command.getRaw()));
+ }
+ return activateSpan(span, true);
+ }
+
+ @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
+ public static void stopSpan(
+ @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) {
+ DECORATE.onError(scope.span(), throwable);
+ DECORATE.beforeFinish(scope.span());
+ scope.close();
+ }
+ }
+}

ADD
dd-java-agent/instrumentation/jedis-3.0/src/test/groovy/JedisClientTest.groovy
+import datadog.trace.agent.test.AgentTestRunner
+import datadog.trace.api.Config
+import datadog.trace.api.DDSpanTypes
+import datadog.trace.instrumentation.api.Tags
+import redis.clients.jedis.Jedis
+import redis.embedded.RedisServer
+import spock.lang.Shared
+
+class JedisClientTest extends AgentTestRunner {
+
+ public static final int PORT = 6399
+
+ @Shared
+ RedisServer redisServer = RedisServer.builder()
+ // bind to localhost to avoid firewall popup
+ .setting("bind 127.0.0.1")
+ // set max memory to avoid problems in CI
+ .setting("maxmemory 128M")
+ .port(PORT).build()
+ @Shared
+ Jedis jedis = new Jedis("localhost", PORT)
+
+ def setupSpec() {
+ println "Using redis: $redisServer.args"
+ redisServer.start()
+
+ // This setting should have no effect since decorator returns null for the instance.
+ System.setProperty(Config.PREFIX + Config.DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "true")
+ }
+
+ def cleanupSpec() {
+ redisServer.stop()
+// jedis.close() // not available in the early version we're using.
+
+ System.clearProperty(Config.PREFIX + Config.DB_CLIENT_HOST_SPLIT_BY_INSTANCE)
+ }
+
+ def setup() {
+ jedis.flushAll()
+ TEST_WRITER.start()
+ }
+
+ def "set command"() {
+ when:
+ jedis.set("foo", "bar")
+
+ then:
+ assertTraces(1) {
+ trace(0, 1) {
+ span(0) {
+ serviceName "redis"
+ operationName "redis.query"
+ resourceName "SET"
+ spanType DDSpanTypes.REDIS
+ tags {
+ "$Tags.COMPONENT" "redis-command"
+ "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT
+ "$Tags.DB_TYPE" "redis"
+ defaultTags()
+ }
+ }
+ }
+ }
+ }
+
+ def "get command"() {
+ when:
+ jedis.set("foo", "bar")
+ def value = jedis.get("foo")
+
+ then:
+ value == "bar"
+
+ assertTraces(2) {
+ trace(0, 1) {
+ span(0) {
+ serviceName "redis"
+ operationName "redis.query"
+ resourceName "SET"
+ spanType DDSpanTypes.REDIS
+ tags {
+ "$Tags.COMPONENT" "redis-command"
+ "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT
+ "$Tags.DB_TYPE" "redis"
+ defaultTags()
+ }
+ }
+ }
+ trace(1, 1) {
+ span(0) {
+ serviceName "redis"
+ operationName "redis.query"
+ resourceName "GET"
+ spanType DDSpanTypes.REDIS
+ tags {
+ "$Tags.COMPONENT" "redis-command"
+ "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT
+ "$Tags.DB_TYPE" "redis"
+ defaultTags()
+ }
+ }
+ }
+ }
+ }
+
+ def "command with no arguments"() {
+ when:
+ jedis.set("foo", "bar")
+ def value = jedis.randomKey()
+
+ then:
+ value == "foo"
+
+ assertTraces(2) {
+ trace(0, 1) {
+ span(0) {
+ serviceName "redis"
+ operationName "redis.query"
+ resourceName "SET"
+ spanType DDSpanTypes.REDIS
+ tags {
+ "$Tags.COMPONENT" "redis-command"
+ "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT
+ "$Tags.DB_TYPE" "redis"
+ defaultTags()
+ }
+ }
+ }
+ trace(1, 1) {
+ span(0) {
+ serviceName "redis"
+ operationName "redis.query"
+ resourceName "RANDOMKEY"
+ spanType DDSpanTypes.REDIS
+ tags {
+ "$Tags.COMPONENT" "redis-command"
+ "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT
+ "$Tags.DB_TYPE" "redis"
+ defaultTags()
+ }
+ }
+ }
+ }
+ }
+}

MODIFY
settings.gradle
settings.gradle
@@ -84,6 +84,7 @@ include ':dd-java-agent:instrumentation:java-concurrent:akka-2.5-testing'
include ':dd-java-agent:instrumentation:jboss-classloading'
include ':dd-java-agent:instrumentation:jdbc'
include ':dd-java-agent:instrumentation:jedis-1.4'
+include ':dd-java-agent:instrumentation:jedis-3.0'
include ':dd-java-agent:instrumentation:jetty-8'
include ':dd-java-agent:instrumentation:jms'
include ':dd-java-agent:instrumentation:jsp-2.3'

Add support for jedis 3.0+