From cef86291e316f77fc156e2c0017147a28b05a217 Mon Sep 17 00:00:00 2001 From: Gihad Murad Date: Mon, 5 Feb 2018 12:59:42 -0500 Subject: [PATCH] Added Redis instrumentation via Jedis client 2.9 and changed spring-boot-jdbc to spring-boot-jdbc-redis --- .../jedis-2.9/jedis-2.9.gradle | 13 +++ .../jedis/JedisInstrumentation.java | 74 ++++++++++++++++++ examples/README.md | 2 +- .../README.md | 10 ++- .../docker-compose.yml | 4 + .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../gradlew | 0 .../gradlew.bat | 0 .../spring-boot-jdbc-redis.gradle} | 14 ++-- .../java/datadog/examples/Application.java | 0 .../datadog/examples/ApplicationConfig.java | 0 .../java/datadog/examples/entities/User.java | 0 .../examples/entities/UserRepository.java | 0 .../examples/resources/DBResource.java | 25 +++++- .../examples/resources/HomeResource.java | 6 +- .../main}/resources/application.properties | 0 .../src/main/resources/application.properties | 7 -- settings.gradle | 3 +- 19 files changed, 137 insertions(+), 21 deletions(-) create mode 100644 dd-java-agent/instrumentation/jedis-2.9/jedis-2.9.gradle create mode 100644 dd-java-agent/instrumentation/jedis-2.9/src/main/java/datadog/trace/instrumentation/jedis/JedisInstrumentation.java rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/README.md (82%) rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/docker-compose.yml (70%) rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/gradle/wrapper/gradle-wrapper.jar (100%) rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/gradle/wrapper/gradle-wrapper.properties (100%) rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/gradlew (100%) rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/gradlew.bat (100%) rename examples/{spring-boot-jdbc/spring-boot-jdbc.gradle => spring-boot-jdbc-redis/spring-boot-jdbc-redis.gradle} (70%) rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/src/main/java/datadog/examples/Application.java (100%) rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/src/main/java/datadog/examples/ApplicationConfig.java (100%) rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/src/main/java/datadog/examples/entities/User.java (100%) rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/src/main/java/datadog/examples/entities/UserRepository.java (100%) rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/src/main/java/datadog/examples/resources/DBResource.java (70%) rename examples/{spring-boot-jdbc => spring-boot-jdbc-redis}/src/main/java/datadog/examples/resources/HomeResource.java (69%) rename examples/{spring-boot-jdbc/out/production => spring-boot-jdbc-redis/src/main}/resources/application.properties (100%) delete mode 100644 examples/spring-boot-jdbc/src/main/resources/application.properties diff --git a/dd-java-agent/instrumentation/jedis-2.9/jedis-2.9.gradle b/dd-java-agent/instrumentation/jedis-2.9/jedis-2.9.gradle new file mode 100644 index 0000000000..11e7fa280e --- /dev/null +++ b/dd-java-agent/instrumentation/jedis-2.9/jedis-2.9.gradle @@ -0,0 +1,13 @@ + +apply from: "${rootDir}/gradle/java.gradle" + +dependencies { + compileOnly group: 'redis.clients', name: 'jedis', version: '2.9.0' + + compile project(':dd-trace-ot') + compile project(':dd-java-agent:tooling') + + compile deps.bytebuddy + compile deps.opentracing + compile deps.autoservice +} diff --git a/dd-java-agent/instrumentation/jedis-2.9/src/main/java/datadog/trace/instrumentation/jedis/JedisInstrumentation.java b/dd-java-agent/instrumentation/jedis-2.9/src/main/java/datadog/trace/instrumentation/jedis/JedisInstrumentation.java new file mode 100644 index 0000000000..cab3ec851b --- /dev/null +++ b/dd-java-agent/instrumentation/jedis-2.9/src/main/java/datadog/trace/instrumentation/jedis/JedisInstrumentation.java @@ -0,0 +1,74 @@ +package datadog.trace.instrumentation.jedis; + +import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isInterface; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isProtected; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.DDAdvice; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.api.DDTags; +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.tag.Tags; +import io.opentracing.util.GlobalTracer; +import java.util.Collections; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.asm.Advice; +import redis.clients.jedis.Protocol.Command; + +@AutoService(Instrumenter.class) +public final class JedisInstrumentation implements Instrumenter { + + @Override + public AgentBuilder instrument(final AgentBuilder agentBuilder) { + return agentBuilder + .type(not(isInterface()).and(hasSuperType(named("redis.clients.jedis.Connection")))) + .transform( + DDAdvice.create() + .advice( + isMethod() + .and(isProtected()) + .and(nameStartsWith("sendCommand")) + .and(takesArgument(1, byte[][].class)), + JedisAdvice.class.getName())) + .asDecorator(); + } + + public static class JedisAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Scope nameResource(@Advice.Argument(0) final Command command) { + + final Scope scope = GlobalTracer.get().buildSpan("redis.command").startActive(true); + + final Span span = scope.span(); + Tags.DB_TYPE.set(span, "redis"); + Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT); + Tags.COMPONENT.set(span, "redis-command"); + + span.setTag(DDTags.RESOURCE_NAME, command.name()); + span.setTag(DDTags.SERVICE_NAME, "redis"); + span.setTag(DDTags.SPAN_TYPE, "redis"); + span.setTag("span.origin.type", command.getClass().getName()); + + return scope; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Enter final Scope scope, @Advice.Thrown final Throwable throwable) { + if (throwable != null) { + final Span span = scope.span(); + Tags.ERROR.set(span, true); + span.log(Collections.singletonMap("error.object", throwable)); + } + scope.close(); + } + } +} diff --git a/examples/README.md b/examples/README.md index 872a3592fe..0a7348ddfd 100644 --- a/examples/README.md +++ b/examples/README.md @@ -7,7 +7,7 @@ using the OpenTracing API and the DD Tracer. Here are the examples * [Dropwizard (Jax-Rs) + Mongo database + HTTP Client](dropwizard-mongo-client/README.md) -* [Spring-boot + MySQL JDBC database](spring-boot-jdbc/README.md) +* [Spring-boot + MySQL JDBC database + Redis (Jedis client)](spring-boot-jdbc-redis/README.md) * [Instrumenting using a Java Agent](javaagent/README.md) diff --git a/examples/spring-boot-jdbc/README.md b/examples/spring-boot-jdbc-redis/README.md similarity index 82% rename from examples/spring-boot-jdbc/README.md rename to examples/spring-boot-jdbc-redis/README.md index 425e153b2a..065f1cc18a 100644 --- a/examples/spring-boot-jdbc/README.md +++ b/examples/spring-boot-jdbc-redis/README.md @@ -15,9 +15,9 @@ all libraries and examples launching from the ``dd-trace-java`` root folder: ./gradlew clean shadowJar bootRepackage ``` -Then you can launch the Datadog agent as follows: +Then you can launch the Datadog agent and a Redis instance as follows: ```bash -cd examples/spring-boot-jdbc +cd examples/spring-boot-jdbc-redis DD_API_KEY= docker-compose up -d ``` @@ -31,12 +31,12 @@ To launch the application, just: ``` *Note: The ``bootRun`` Gradle command appends automatically the ``-javaagent`` argument, so that you don't need to specify -the path of the Java Agent. Gradle executes the ``:examples:spring-boot-jdbc:bootRun`` task until you +the path of the Java Agent. Gradle executes the ``:examples:spring-boot-jdbc-redis:bootRun`` task until you stop it.* Or as an executable jar: ```bash -java -javaagent:../../dd-java-agent/build/libs/dd-java-agent-{version}.jar -Ddd.service.name=spring-boot-jdbc -jar build/libs/spring-boot-jdbc-demo.jar +java -javaagent:../../dd-java-agent/build/libs/dd-java-agent-{version}.jar -Ddd.service.name=spring-boot-jdbc-redis -jar build/libs/spring-boot-jdbc-redis-demo.jar ``` ### Generate traces @@ -45,6 +45,7 @@ Once the Gradle task is running. Go to the following urls: * [http://localhost:8080/user/add?name=foo&email=bar](http://localhost:8080/user/add?name=foo&email=bar) * [http://localhost:8080/user/all](http://localhost:8080/user/all) +* [http://localhost:8080/user/all](http://localhost:8080/user/random) Then get back to Datadog and wait a bit to see a trace coming. @@ -55,5 +56,6 @@ instruments: - The java servlet filters - The JDBC driver +- The Jedis Redis client The Java Agent embeds the [OpenTracing Java Agent](https://github.com/opentracing-contrib/java-agent). diff --git a/examples/spring-boot-jdbc/docker-compose.yml b/examples/spring-boot-jdbc-redis/docker-compose.yml similarity index 70% rename from examples/spring-boot-jdbc/docker-compose.yml rename to examples/spring-boot-jdbc-redis/docker-compose.yml index fb4e6e3fd6..2ef28b663c 100644 --- a/examples/spring-boot-jdbc/docker-compose.yml +++ b/examples/spring-boot-jdbc-redis/docker-compose.yml @@ -5,3 +5,7 @@ ddagent: - DD_API_KEY ports: - "127.0.0.1:8126:8126" +redis: + image: redis + ports: + - "127.0.0.1:6379:6379" diff --git a/examples/spring-boot-jdbc/gradle/wrapper/gradle-wrapper.jar b/examples/spring-boot-jdbc-redis/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from examples/spring-boot-jdbc/gradle/wrapper/gradle-wrapper.jar rename to examples/spring-boot-jdbc-redis/gradle/wrapper/gradle-wrapper.jar diff --git a/examples/spring-boot-jdbc/gradle/wrapper/gradle-wrapper.properties b/examples/spring-boot-jdbc-redis/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from examples/spring-boot-jdbc/gradle/wrapper/gradle-wrapper.properties rename to examples/spring-boot-jdbc-redis/gradle/wrapper/gradle-wrapper.properties diff --git a/examples/spring-boot-jdbc/gradlew b/examples/spring-boot-jdbc-redis/gradlew similarity index 100% rename from examples/spring-boot-jdbc/gradlew rename to examples/spring-boot-jdbc-redis/gradlew diff --git a/examples/spring-boot-jdbc/gradlew.bat b/examples/spring-boot-jdbc-redis/gradlew.bat similarity index 100% rename from examples/spring-boot-jdbc/gradlew.bat rename to examples/spring-boot-jdbc-redis/gradlew.bat diff --git a/examples/spring-boot-jdbc/spring-boot-jdbc.gradle b/examples/spring-boot-jdbc-redis/spring-boot-jdbc-redis.gradle similarity index 70% rename from examples/spring-boot-jdbc/spring-boot-jdbc.gradle rename to examples/spring-boot-jdbc-redis/spring-boot-jdbc-redis.gradle index 94339af63a..487dc27600 100644 --- a/examples/spring-boot-jdbc/spring-boot-jdbc.gradle +++ b/examples/spring-boot-jdbc-redis/spring-boot-jdbc-redis.gradle @@ -1,19 +1,21 @@ plugins { - id 'org.springframework.boot' version '1.5.4.RELEASE' + id 'org.springframework.boot' version '1.5.10.RELEASE' } apply from: "${rootDir}/gradle/java.gradle" apply from: "${rootDir}/gradle/jacoco.gradle" version = 'demo' -description = 'spring-boot-jdbc' +description = 'spring-boot-jdbc-redis' dependencies { compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3' compile group: 'com.h2database', name: 'h2', version: '1.4.196' - compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.5.4.RELEASE' - compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '1.5.4.RELEASE' + compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.5.10.RELEASE' + compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '1.5.10.RELEASE' + compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis', version: '1.5.10.RELEASE' + } bootRepackage { @@ -22,9 +24,9 @@ bootRepackage { bootRun { if (project.hasProperty('javaagent')) { - jvmArgs = ["-javaagent:$javaagent", "-Ddd.service.name=spring-boot-jdbc"] + jvmArgs = ["-javaagent:$javaagent", "-Ddd.service.name=spring-boot-jdbc-redis"] } else { - jvmArgs = ["-javaagent:${project(':dd-java-agent').tasks.shadowJar.outputs.files.getFiles().iterator().next()}", "-Ddd.service.name=spring-boot-jdbc"] + jvmArgs = ["-javaagent:${project(':dd-java-agent').tasks.shadowJar.outputs.files.getFiles().iterator().next()}", "-Ddd.service.name=spring-boot-jdbc-redis"] } } diff --git a/examples/spring-boot-jdbc/src/main/java/datadog/examples/Application.java b/examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/Application.java similarity index 100% rename from examples/spring-boot-jdbc/src/main/java/datadog/examples/Application.java rename to examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/Application.java diff --git a/examples/spring-boot-jdbc/src/main/java/datadog/examples/ApplicationConfig.java b/examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/ApplicationConfig.java similarity index 100% rename from examples/spring-boot-jdbc/src/main/java/datadog/examples/ApplicationConfig.java rename to examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/ApplicationConfig.java diff --git a/examples/spring-boot-jdbc/src/main/java/datadog/examples/entities/User.java b/examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/entities/User.java similarity index 100% rename from examples/spring-boot-jdbc/src/main/java/datadog/examples/entities/User.java rename to examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/entities/User.java diff --git a/examples/spring-boot-jdbc/src/main/java/datadog/examples/entities/UserRepository.java b/examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/entities/UserRepository.java similarity index 100% rename from examples/spring-boot-jdbc/src/main/java/datadog/examples/entities/UserRepository.java rename to examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/entities/UserRepository.java diff --git a/examples/spring-boot-jdbc/src/main/java/datadog/examples/resources/DBResource.java b/examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/resources/DBResource.java similarity index 70% rename from examples/spring-boot-jdbc/src/main/java/datadog/examples/resources/DBResource.java rename to examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/resources/DBResource.java index 5b5503b46a..93c4cc0759 100644 --- a/examples/spring-boot-jdbc/src/main/java/datadog/examples/resources/DBResource.java +++ b/examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/resources/DBResource.java @@ -3,6 +3,8 @@ package datadog.examples.resources; import datadog.examples.entities.User; import datadog.examples.entities.UserRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -17,9 +19,16 @@ public class DBResource { // Which is auto-generated by Spring, we will use it to handle the data private UserRepository userRepository; + private ValueOperations ops; + + public DBResource(@Autowired StringRedisTemplate template) { + this.ops = template.opsForValue(); + } + @GetMapping(path = "/add") // Map ONLY GET Requests public @ResponseBody String addNewUser( @RequestParam final String name, @RequestParam final String email) { + // @ResponseBody means the returned String is the response, not a view name // @RequestParam means it is a parameter from the GET or POST request @@ -27,6 +36,10 @@ public class DBResource { n.setName(name); n.setEmail(email); userRepository.save(n); + + // Also save to redis as key/value + ops.set(name, email); + return "Saved"; } @@ -38,7 +51,17 @@ public class DBResource { @GetMapping(path = "/get") public @ResponseBody User getUser(@RequestParam final int id) { - // This returns a JSON or XML with the users + // This returns a JSON or XML with the user return userRepository.findOne(id); } + + @GetMapping(path = "/getredis") + public @ResponseBody String getUserRedis(@RequestParam final String name) { + return ops.get(name); + } + + @GetMapping(path = "/random") + public @ResponseBody String flushRedis() { + return ops.getOperations().randomKey(); + } } diff --git a/examples/spring-boot-jdbc/src/main/java/datadog/examples/resources/HomeResource.java b/examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/resources/HomeResource.java similarity index 69% rename from examples/spring-boot-jdbc/src/main/java/datadog/examples/resources/HomeResource.java rename to examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/resources/HomeResource.java index 893603c3f8..c4fca29ccb 100644 --- a/examples/spring-boot-jdbc/src/main/java/datadog/examples/resources/HomeResource.java +++ b/examples/spring-boot-jdbc-redis/src/main/java/datadog/examples/resources/HomeResource.java @@ -15,9 +15,13 @@ public class HomeResource { template.append("Demo links"); template.append(""); return template.toString(); diff --git a/examples/spring-boot-jdbc/out/production/resources/application.properties b/examples/spring-boot-jdbc-redis/src/main/resources/application.properties similarity index 100% rename from examples/spring-boot-jdbc/out/production/resources/application.properties rename to examples/spring-boot-jdbc-redis/src/main/resources/application.properties diff --git a/examples/spring-boot-jdbc/src/main/resources/application.properties b/examples/spring-boot-jdbc/src/main/resources/application.properties deleted file mode 100644 index 57104fffac..0000000000 --- a/examples/spring-boot-jdbc/src/main/resources/application.properties +++ /dev/null @@ -1,7 +0,0 @@ -# you must set the following so that OpenTracing traced driver is used -spring.datasource.driver-class-name=org.h2.Driver -spring.datasource.url=jdbc:h2:mem:spring-test;DB_CLOSE_ON_EXIT=FALSE - -# set the logging level -logging.level.root=INFO -logging.level.datadog.trace=DEBUG diff --git a/settings.gradle b/settings.gradle index c54b760d5a..0b39cfac6c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,6 +12,7 @@ include ':dd-java-agent:instrumentation:apache-httpclient-4.3' include ':dd-java-agent:instrumentation:aws-sdk' include ':dd-java-agent:instrumentation:datastax-cassandra-3.2' include ':dd-java-agent:instrumentation:jdbc' +include ':dd-java-agent:instrumentation:jedis-2.9' include ':dd-java-agent:instrumentation:jms-1' include ':dd-java-agent:instrumentation:jms-2' include ':dd-java-agent:instrumentation:mongo-3.1' @@ -31,7 +32,7 @@ if (JavaVersion.current().isJava8Compatible()) { // examples include ':examples:dropwizard-mongo-client' - include ':examples:spring-boot-jdbc' + include ':examples:spring-boot-jdbc-redis' include ':examples:rest-spark' }