diff --git a/dd-java-agent/instrumentation/jedis/jedis-3.0/build.gradle b/dd-java-agent/instrumentation/jedis/jedis-3.0/build.gradle index b0adcf72ca6..0eb57076c47 100644 --- a/dd-java-agent/instrumentation/jedis/jedis-3.0/build.gradle +++ b/dd-java-agent/instrumentation/jedis/jedis-3.0/build.gradle @@ -20,8 +20,11 @@ addTestSuiteForDir('latestDepTest', 'test') dependencies { compileOnly group: 'redis.clients', name: 'jedis', version: '3.3.0' - testImplementation group: 'com.github.codemonstur', name: 'embedded-redis', version: '1.4.3' - testImplementation group: 'redis.clients', name: 'jedis', version: '3.3.0' + testImplementation group: 'redis.clients', name: 'jedis', version: '3.0.0' + testImplementation (group: 'com.github.codemonstur', name: 'embedded-redis', version: '1.4.3') { + // Excluding redis client to avoid conflicts in instrumentation code. + exclude group: 'redis.clients', module: 'jedis' + } // 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 testImplementation project(':dd-java-agent:instrumentation:jedis:jedis-1.4') diff --git a/dd-java-agent/instrumentation/jedis/jedis-3.0/gradle.lockfile b/dd-java-agent/instrumentation/jedis/jedis-3.0/gradle.lockfile deleted file mode 100644 index b120b893c29..00000000000 --- a/dd-java-agent/instrumentation/jedis/jedis-3.0/gradle.lockfile +++ /dev/null @@ -1,133 +0,0 @@ -# This is a Gradle generated file for dependency locking. -# Manual edits can break the build and are not advised. -# This file is expected to be part of source control. -# To regenerate this file, run: ./gradlew :dd-java-agent:instrumentation:jedis:jedis-3.0:dependencies --write-locks -cafe.cryptography:curve25519-elisabeth:0.1.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -cafe.cryptography:ed25519-elisabeth:0.1.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -ch.qos.logback:logback-classic:1.2.13=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -ch.qos.logback:logback-core:1.2.13=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.blogspot.mydailyjava:weak-lock-free:0.17=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq.okhttp3:okhttp:3.12.15=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq.okio:okio:1.17.6=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq:dd-instrument-java:0.0.4=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq:dd-javac-plugin-client:0.2.2=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq:java-dogstatsd-client:4.4.5=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.datadoghq:sketches-java:0.8.3=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.codemonstur:embedded-redis:1.4.3=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.github.javaparser:javaparser-core:3.25.6=codenarc -com.github.jnr:jffi:1.3.15=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-a64asm:1.0.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-constants:0.10.4=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-enxio:0.32.20=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-ffi:2.2.19=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-posix:3.1.22=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-unixsocket:0.38.25=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-x86asm:1.0.2=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.spotbugs:spotbugs-annotations:4.9.8=compileClasspath,spotbugs -com.github.spotbugs:spotbugs:4.9.8=spotbugs -com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs -com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,testAnnotationProcessor,testCompileClasspath -com.google.auto.service:auto-service:1.1.1=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.auto:auto-common:1.2.1=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,compileClasspath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,spotbugs,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath -com.google.code.gson:gson:2.13.2=spotbugs -com.google.errorprone:error_prone_annotations:2.18.0=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.errorprone:error_prone_annotations:2.41.0=spotbugs -com.google.errorprone:error_prone_annotations:2.47.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.guava:failureaccess:1.0.1=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.guava:failureaccess:1.0.3=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.guava:guava:32.0.1-jre=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.guava:guava:33.6.0-jre=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath -com.google.j2objc:j2objc-annotations:2.8=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.j2objc:j2objc-annotations:3.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.re2j:re2j:1.8=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.squareup.moshi:moshi:1.11.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.okhttp3:logging-interceptor:3.12.12=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.okhttp3:okhttp:3.12.12=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.okio:okio:1.17.5=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.thoughtworks.qdox:qdox:1.12.1=codenarc -commons-fileupload:commons-fileupload:1.5=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -commons-io:commons-io:2.11.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -commons-io:commons-io:2.20.0=spotbugs -de.thetaphi:forbiddenapis:3.10=compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.leangen.geantyref:geantyref:1.3.16=latestDepTestRuntimeClasspath,testRuntimeClasspath -io.sqreen:libsqreen:17.4.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -javax.servlet:javax.servlet-api:3.1.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -jaxen:jaxen:2.0.0=spotbugs -junit:junit:4.13.2=latestDepTestRuntimeClasspath,testRuntimeClasspath -net.bytebuddy:byte-buddy-agent:1.18.10=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -net.bytebuddy:byte-buddy:1.18.10=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -net.java.dev.jna:jna-platform:5.8.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -net.java.dev.jna:jna:5.8.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -net.sf.saxon:Saxon-HE:12.9=spotbugs -org.apache.ant:ant-antlr:1.10.14=codenarc -org.apache.ant:ant-junit:1.10.14=codenarc -org.apache.bcel:bcel:6.11.0=spotbugs -org.apache.commons:commons-lang3:3.19.0=spotbugs -org.apache.commons:commons-pool2:2.11.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.commons:commons-pool2:2.6.2=compileClasspath -org.apache.commons:commons-text:1.14.0=spotbugs -org.apache.logging.log4j:log4j-api:2.25.2=spotbugs -org.apache.logging.log4j:log4j-core:2.25.2=spotbugs -org.apiguardian:apiguardian-api:1.1.2=latestDepTestCompileClasspath,testCompileClasspath -org.checkerframework:checker-qual:3.33.0=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -org.codehaus.groovy:groovy-ant:3.0.23=codenarc -org.codehaus.groovy:groovy-docgenerator:3.0.23=codenarc -org.codehaus.groovy:groovy-groovydoc:3.0.23=codenarc -org.codehaus.groovy:groovy-json:3.0.23=codenarc -org.codehaus.groovy:groovy-json:3.0.25=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-templates:3.0.23=codenarc -org.codehaus.groovy:groovy-xml:3.0.23=codenarc -org.codehaus.groovy:groovy:3.0.23=codenarc -org.codehaus.groovy:groovy:3.0.25=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.codenarc:CodeNarc:3.7.0=codenarc -org.dom4j:dom4j:2.2.0=spotbugs -org.gmetrics:GMetrics:2.1.0=codenarc -org.hamcrest:hamcrest-core:1.3=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.hamcrest:hamcrest:3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jctools:jctools-core-jdk11:4.0.6=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.jctools:jctools-core:4.0.6=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.jspecify:jspecify:1.0.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-api:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-engine:5.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-params:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-commons:1.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-engine:1.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-launcher:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-runner:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-suite-api:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-suite-commons:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.junit:junit-bom:5.14.0=spotbugs -org.junit:junit-bom:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.mockito:mockito-core:4.4.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.objenesis:objenesis:3.3=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.opentest4j:opentest4j:1.3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-analysis:9.7.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-analysis:9.9=spotbugs -org.ow2.asm:asm-commons:9.10.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.9=spotbugs -org.ow2.asm:asm-tree:9.10.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-tree:9.9=spotbugs -org.ow2.asm:asm-util:9.7.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-util:9.9=spotbugs -org.ow2.asm:asm:9.10.1=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm:9.9=spotbugs -org.slf4j:jcl-over-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:jul-to-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:log4j-over-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:slf4j-api:1.7.30=buildTimeInstrumentationPlugin,compileClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath -org.slf4j:slf4j-api:1.7.32=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:slf4j-api:2.0.17=spotbugs,spotbugsSlf4j -org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j -org.snakeyaml:snakeyaml-engine:2.9=buildTimeInstrumentationPlugin,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testRuntimeClasspath -org.spockframework:spock-bom:2.4-groovy-3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.spockframework:spock-core:2.4-groovy-3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.tabletest:tabletest-junit:1.2.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.tabletest:tabletest-parser:1.2.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.xmlresolver:xmlresolver:5.3.3=spotbugs -redis.clients:jedis:3.10.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath -redis.clients:jedis:3.3.0=compileClasspath -redis.clients:jedis:3.8.0=testCompileClasspath,testRuntimeClasspath -empty=spotbugsPlugins diff --git a/dd-java-agent/instrumentation/jedis/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisClientDecorator.java b/dd-java-agent/instrumentation/jedis/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisClientDecorator.java index edaaa9278b3..294ccc207f3 100644 --- a/dd-java-agent/instrumentation/jedis/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisClientDecorator.java +++ b/dd-java-agent/instrumentation/jedis/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisClientDecorator.java @@ -7,14 +7,13 @@ import redis.clients.jedis.Connection; public class JedisClientDecorator extends DBTypeProcessingDatabaseClientDecorator { - public static final JedisClientDecorator DECORATE = new JedisClientDecorator(); - private static final String REDIS = "redis"; + public static final CharSequence COMPONENT_NAME = UTF8BytesString.create("redis-command"); public static final CharSequence OPERATION_NAME = UTF8BytesString.create(SpanNaming.instance().namingSchema().cache().operation(REDIS)); private static final String SERVICE_NAME = SpanNaming.instance().namingSchema().cache().service(REDIS); - private static final CharSequence COMPONENT_NAME = UTF8BytesString.create("redis-command"); + public static final JedisClientDecorator DECORATE = new JedisClientDecorator(); @Override protected String[] instrumentationNames() { diff --git a/dd-java-agent/instrumentation/jedis/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisInstrumentation.java b/dd-java-agent/instrumentation/jedis/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisInstrumentation.java index 371e84a784a..3c449ac3219 100644 --- a/dd-java-agent/instrumentation/jedis/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisInstrumentation.java +++ b/dd-java-agent/instrumentation/jedis/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisInstrumentation.java @@ -1,10 +1,13 @@ package datadog.trace.instrumentation.jedis30; +import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassNamed; import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; +import static datadog.trace.instrumentation.jedis30.JedisClientDecorator.COMPONENT_NAME; import static datadog.trace.instrumentation.jedis30.JedisClientDecorator.DECORATE; import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.not; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import com.google.auto.service.AutoService; @@ -14,6 +17,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatcher; import redis.clients.jedis.Connection; import redis.clients.jedis.Protocol; import redis.clients.jedis.commands.ProtocolCommand; @@ -27,10 +31,10 @@ public JedisInstrumentation() { } @Override - public String[] helperClassNames() { - return new String[] { - packageName + ".JedisClientDecorator", - }; + public ElementMatcher.Junction classLoaderMatcher() { + // Only match Jedis 3.x which has ProtocolCommand but not CommandObject (4.x+). + return hasClassNamed("redis.clients.jedis.commands.ProtocolCommand") + .and(not(hasClassNamed("redis.clients.jedis.CommandObject"))); } @Override @@ -38,6 +42,13 @@ public String instrumentedType() { return "redis.clients.jedis.Connection"; } + @Override + public String[] helperClassNames() { + return new String[] { + packageName + ".JedisClientDecorator", + }; + } + @Override public void methodAdvice(MethodTransformer transformer) { transformer.applyAdvice( @@ -45,7 +56,6 @@ public void methodAdvice(MethodTransformer transformer) { .and(named("sendCommand")) .and(takesArgument(0, 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 { @@ -56,15 +66,13 @@ public static AgentScope onEnter( if (CallDepthThreadLocalMap.incrementCallDepth(Connection.class) > 0) { return null; } - final AgentSpan span = startSpan("redis-command", JedisClientDecorator.OPERATION_NAME); + final AgentSpan span = + startSpan(COMPONENT_NAME.toString(), JedisClientDecorator.OPERATION_NAME); DECORATE.afterStart(span); DECORATE.onConnection(span, thiz); - if (command instanceof Protocol.Command) { DECORATE.onStatement(span, ((Protocol.Command) command).name()); } else { - // Protocol.Command is the only implementation in the Jedis lib as of 3.1 but this will save - // us if that changes DECORATE.onStatement(span, new String(command.getRaw())); } return activateSpan(span); diff --git a/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/groovy/Jedis30ClientTest.groovy b/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/groovy/Jedis30ClientTest.groovy deleted file mode 100644 index ad3ffb46153..00000000000 --- a/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/groovy/Jedis30ClientTest.groovy +++ /dev/null @@ -1,359 +0,0 @@ -import datadog.trace.agent.test.naming.VersionedNamingTestBase -import datadog.trace.agent.test.utils.PortUtils -import datadog.trace.api.Config -import datadog.trace.api.DDSpanTypes -import datadog.trace.bootstrap.instrumentation.api.Tags -import redis.clients.jedis.Jedis -import redis.embedded.RedisServer -import spock.lang.Shared - -import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace -import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_INSTANCE -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan - -abstract class Jedis30ClientTest extends VersionedNamingTestBase { - - @Shared - int port = PortUtils.randomOpenPort() - - @Shared - RedisServer redisServer = RedisServer.newRedisServer() - // 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) - - @Override - void configurePreAgent() { - super.configurePreAgent() - - // This setting should have no effect since decorator returns null for the instance. - injectSysConfig(DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "true") - } - - def setupSpec() { - println "Using redis: $redisServer.@args" - redisServer.start() - } - - def cleanupSpec() { - redisServer.stop() - jedis.close() - } - - def setup() { - def cleanupSpan = runUnderTrace("cleanup") { - jedis.flushAll() - activeSpan() - } - TEST_WRITER.waitUntilReported(cleanupSpan) - TEST_WRITER.start() - } - - def "set command"() { - when: - jedis.set("foo", "bar") - - then: - assertTraces(1) { - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "SET" - spanType DDSpanTypes.REDIS - measured true - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - } - } - - def "get command"() { - when: - jedis.set("foo", "bar") - def value = jedis.get("foo") - - then: - value == "bar" - - assertTraces(2) { - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "SET" - spanType DDSpanTypes.REDIS - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "GET" - spanType DDSpanTypes.REDIS - measured true - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - } - } - - def "command with no arguments"() { - when: - jedis.set("foo", "bar") - def value = jedis.randomKey() - - then: - value == "foo" - - assertTraces(2) { - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "SET" - spanType DDSpanTypes.REDIS - measured true - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "RANDOMKEY" - spanType DDSpanTypes.REDIS - measured true - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - } - } - - def "hmset and hgetAll commands"() { - when: - - Map h = new HashMap<>() - h.put("key1", "value1") - h.put("key2", "value2") - jedis.hmset("map", h) - - Map result = jedis.hgetAll("map") - - then: - result != null - result == h - - assertTraces(2) { - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "HMSET" - spanType DDSpanTypes.REDIS - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "HGETALL" - spanType DDSpanTypes.REDIS - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - } - } - - def "zadd and zrangeByScore commands"() { - when: - jedis.zadd("foo", 1d, "a") - jedis.zadd("foo", 10d, "b") - jedis.zadd("foo", 0.1d, "c") - jedis.zadd("foo", 2d, "d") - - Set verify = new HashSet() - verify.add("a") - verify.add("c") - verify.add("d") - Set val = jedis.zrangeByScore("foo", 0d, 2d) - - then: - val != null - val == verify - - assertTraces(5) { - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "ZADD" - spanType DDSpanTypes.REDIS - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "ZADD" - spanType DDSpanTypes.REDIS - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "ZADD" - spanType DDSpanTypes.REDIS - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "ZADD" - spanType DDSpanTypes.REDIS - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - trace(1) { - span { - serviceName service() - operationName operation() - resourceName "ZRANGEBYSCORE" - spanType DDSpanTypes.REDIS - tags { - "$Tags.COMPONENT" "redis-command" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.DB_TYPE" "redis" - "$Tags.PEER_HOSTNAME" "localhost" - peerServiceFrom(Tags.PEER_HOSTNAME) - defaultTags() - } - } - } - } - } -} - -class Jedis30ClientV0Test extends Jedis30ClientTest { - - @Override - int version() { - return 0 - } - - @Override - String service() { - return "redis" - } - - @Override - String operation() { - return "redis.query" - } -} - -class Jedis30ClientV1ForkedTest extends Jedis30ClientTest { - - @Override - int version() { - return 1 - } - - @Override - String service() { - return Config.get().getServiceName() - } - - @Override - String operation() { - return "redis.command" - } -} diff --git a/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/java/datadog/trace/instrumentation/jedis30/Jedis30ClientTest.java b/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/java/datadog/trace/instrumentation/jedis30/Jedis30ClientTest.java new file mode 100644 index 00000000000..26913d1b729 --- /dev/null +++ b/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/java/datadog/trace/instrumentation/jedis30/Jedis30ClientTest.java @@ -0,0 +1,261 @@ +package datadog.trace.instrumentation.jedis30; + +import static datadog.trace.agent.test.assertions.SpanMatcher.span; +import static datadog.trace.agent.test.assertions.TagsMatcher.defaultTags; +import static datadog.trace.agent.test.assertions.TagsMatcher.error; +import static datadog.trace.agent.test.assertions.TagsMatcher.tag; +import static datadog.trace.agent.test.assertions.TraceMatcher.trace; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; +import static datadog.trace.test.junit.utils.assertions.Matchers.any; +import static datadog.trace.test.junit.utils.assertions.Matchers.validates; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.trace.agent.test.AbstractInstrumentationTest; +import datadog.trace.agent.test.assertions.SpanMatcher; +import datadog.trace.agent.test.utils.PortUtils; +import datadog.trace.agent.test.utils.TraceUtils; +import datadog.trace.api.DDSpanTypes; +import datadog.trace.bootstrap.instrumentation.api.Tags; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeoutException; +import java.util.regex.Pattern; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.Pipeline; +import redis.clients.jedis.Response; +import redis.clients.jedis.Transaction; +import redis.clients.jedis.exceptions.JedisDataException; +import redis.embedded.RedisServer; + +abstract class Jedis30ClientTest extends AbstractInstrumentationTest { + + private static int port; + private static RedisServer redisServer; + private static Jedis jedis; + + protected abstract String service(); + + protected abstract String operation(); + + @BeforeAll + static void setupSpec() throws IOException { + port = PortUtils.randomOpenPort(); + redisServer = + RedisServer.newRedisServer() + .setting("bind 127.0.0.1") + .setting("maxmemory 128M") + .port(port) + .build(); + redisServer.start(); + jedis = new Jedis("localhost", port); + } + + @AfterAll + static void cleanupSpec() throws IOException { + redisServer.stop(); + jedis.close(); + } + + @BeforeEach + void setup() throws InterruptedException, TimeoutException { + TraceUtils.runUnderTrace( + "cleanup", + () -> { + jedis.flushAll(); + return activeSpan(); + }); + writer.waitForTraces(1); + writer.start(); + } + + private SpanMatcher redisSpan(String resourceName) { + return span() + .serviceName(service()) + .operationName(Pattern.compile(Pattern.quote(operation()))) + .resourceName(Pattern.compile(Pattern.quote(resourceName))) + .type(DDSpanTypes.REDIS) + .measured() + .tags( + tag(Tags.COMPONENT, equalsString("redis-command")), + tag(Tags.SPAN_KIND, equalsString(Tags.SPAN_KIND_CLIENT)), + tag(Tags.DB_TYPE, equalsString("redis")), + tag(Tags.PEER_HOSTNAME, equalsString("localhost")), + tag("_dd.peer.service.source", any()), + tag("_dd.svc_src", any()), + tag("peer.service", any()), + defaultTags()); + } + + /** Matches tag values using toString() to handle UTF8BytesString vs String comparison. */ + private static datadog.trace.test.junit.utils.assertions.Matcher equalsString( + String expected) { + return validates(v -> v != null && v.toString().equals(expected)); + } + + @Test + void setCommand() { + jedis.set("foo", "bar"); + + assertTraces(trace(redisSpan("SET"))); + } + + @Test + void getCommand() { + jedis.set("foo", "bar"); + String value = jedis.get("foo"); + + assertEquals("bar", value); + + assertTraces(trace(redisSpan("SET")), trace(redisSpan("GET"))); + } + + @Test + void commandWithNoArguments() { + jedis.set("foo", "bar"); + String value = jedis.randomKey(); + + assertEquals("foo", value); + + assertTraces(trace(redisSpan("SET")), trace(redisSpan("RANDOMKEY"))); + } + + @Test + void hmsetAndHgetAllCommands() { + Map h = new HashMap<>(); + h.put("key1", "value1"); + h.put("key2", "value2"); + jedis.hmset("map", h); + + Map result = jedis.hgetAll("map"); + + assertNotNull(result); + assertEquals(h, result); + + assertTraces(trace(redisSpan("HMSET")), trace(redisSpan("HGETALL"))); + } + + @Test + void zaddAndZrangeByScoreCommands() { + jedis.zadd("foo", 1d, "a"); + jedis.zadd("foo", 10d, "b"); + jedis.zadd("foo", 0.1d, "c"); + jedis.zadd("foo", 2d, "d"); + + Set expected = new HashSet<>(); + expected.add("a"); + expected.add("c"); + expected.add("d"); + Set val = jedis.zrangeByScore("foo", 0d, 2d); + + assertNotNull(val); + assertEquals(expected, val); + + assertTraces( + trace(redisSpan("ZADD")), + trace(redisSpan("ZADD")), + trace(redisSpan("ZADD")), + trace(redisSpan("ZADD")), + trace(redisSpan("ZRANGEBYSCORE"))); + } + + @Test + void pipelineOperations() { + Pipeline pipeline = jedis.pipelined(); + pipeline.set("pipe-key1", "value1"); + pipeline.set("pipe-key2", "value2"); + Response getResponse1 = pipeline.get("pipe-key1"); + Response getResponse2 = pipeline.get("pipe-key2"); + pipeline.sync(); + + assertEquals("value1", getResponse1.get()); + assertEquals("value2", getResponse2.get()); + + assertTraces( + trace(redisSpan("SET")), + trace(redisSpan("SET")), + trace(redisSpan("GET")), + trace(redisSpan("GET"))); + } + + @Test + void transactionOperations() { + Transaction transaction = jedis.multi(); + transaction.set("tx-key1", "tx-value1"); + transaction.set("tx-key2", "tx-value2"); + Response getResponse = transaction.get("tx-key1"); + transaction.exec(); + + assertEquals("tx-value1", getResponse.get()); + + // MULTI, SET, SET, GET, EXEC each produce a span + assertTraces( + trace(redisSpan("MULTI")), + trace(redisSpan("SET")), + trace(redisSpan("SET")), + trace(redisSpan("GET")), + trace(redisSpan("EXEC"))); + } + + @Test + void additionalCommandsDelIncrExpireTtlExists() { + jedis.set("count-key", "0"); + jedis.incr("count-key"); + jedis.incr("count-key"); + String countVal = jedis.get("count-key"); + jedis.expire("count-key", 3600); + Long ttlVal = jedis.ttl("count-key"); + Boolean existsVal = jedis.exists("count-key"); + jedis.del("count-key"); + Boolean existsAfterDel = jedis.exists("count-key"); + + assertEquals("2", countVal); + assertTrue(ttlVal > 0); + assertTrue(existsVal); + assertFalse(existsAfterDel); + + assertTraces( + trace(redisSpan("SET")), + trace(redisSpan("INCR")), + trace(redisSpan("INCR")), + trace(redisSpan("GET")), + trace(redisSpan("EXPIRE")), + trace(redisSpan("TTL")), + trace(redisSpan("EXISTS")), + trace(redisSpan("DEL")), + trace(redisSpan("EXISTS"))); + } + + @Test + void wrongTypeCommandSetsErrorTags() { + // SET stores a string value, then LPUSH expects a list — Redis returns a WRONGTYPE error + jedis.set("string-key", "value"); + assertThrows(JedisDataException.class, () -> jedis.lpush("string-key", "element")); + + assertTraces( + trace(redisSpan("SET").error(false)), + trace( + redisSpan("LPUSH") + .error() + .tags( + tag(Tags.COMPONENT, equalsString("redis-command")), + tag(Tags.SPAN_KIND, equalsString(Tags.SPAN_KIND_CLIENT)), + tag(Tags.DB_TYPE, equalsString("redis")), + tag(Tags.PEER_HOSTNAME, equalsString("localhost")), + tag("_dd.peer.service.source", any()), + tag("_dd.svc_src", any()), + tag("peer.service", any()), + error(JedisDataException.class), + defaultTags()))); + } +} diff --git a/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/java/datadog/trace/instrumentation/jedis30/Jedis30ClientV0ForkedTest.java b/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/java/datadog/trace/instrumentation/jedis30/Jedis30ClientV0ForkedTest.java new file mode 100644 index 00000000000..382c8593668 --- /dev/null +++ b/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/java/datadog/trace/instrumentation/jedis30/Jedis30ClientV0ForkedTest.java @@ -0,0 +1,18 @@ +package datadog.trace.instrumentation.jedis30; + +import datadog.trace.test.junit.utils.config.WithConfig; + +@WithConfig(key = "trace.span.attribute.schema", value = "v0") +@WithConfig(key = "trace.db.client.split-by-instance", value = "true") +class Jedis30ClientV0ForkedTest extends Jedis30ClientTest { + + @Override + protected String service() { + return "redis"; + } + + @Override + protected String operation() { + return "redis.query"; + } +} diff --git a/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/java/datadog/trace/instrumentation/jedis30/Jedis30ClientV1ForkedTest.java b/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/java/datadog/trace/instrumentation/jedis30/Jedis30ClientV1ForkedTest.java new file mode 100644 index 00000000000..2299106c233 --- /dev/null +++ b/dd-java-agent/instrumentation/jedis/jedis-3.0/src/test/java/datadog/trace/instrumentation/jedis30/Jedis30ClientV1ForkedTest.java @@ -0,0 +1,19 @@ +package datadog.trace.instrumentation.jedis30; + +import datadog.trace.api.Config; +import datadog.trace.test.junit.utils.config.WithConfig; + +@WithConfig(key = "trace.span.attribute.schema", value = "v1") +@WithConfig(key = "trace.db.client.split-by-instance", value = "true") +class Jedis30ClientV1ForkedTest extends Jedis30ClientTest { + + @Override + protected String service() { + return Config.get().getServiceName(); + } + + @Override + protected String operation() { + return "redis.command"; + } +}