Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class AWSClientTest extends AgentTestRunner {
false | 1
}

@Timeout(10)
def "send request with mocked back end"() {
setup:
def receivedHeaders = new AtomicReference<Headers>()
Expand Down
28 changes: 28 additions & 0 deletions dd-java-agent/instrumentation/jedis-1.4/jedis-1.4.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
apply plugin: 'version-scan'

versionScan {
group = "redis.clients"
module = "jedis"
versions = "[1.4.0,)"
legacyModule = "jms-api"
verifyPresent = [
'redis.clients.jedis.Protocol$Command': null,
]
}

apply from: "${rootDir}/gradle/java.gradle"

dependencies {
compileOnly group: 'redis.clients', name: 'jedis', version: '1.4.0'

compile project(':dd-java-agent:agent-tooling')

compile deps.bytebuddy
compile deps.opentracing
compile deps.autoservice

testCompile project(':dd-java-agent:testing')
testCompile group: 'com.github.kstyrc', name: 'embedded-redis', version: '0.6'
testCompile group: 'redis.clients', name: 'jedis', version: '1.4.0'

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package datadog.trace.instrumentation.jedis;

import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
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.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 extends Instrumenter.Configurable {

private static final String SERVICE_NAME = "redis";
private static final String COMPONENT_NAME = SERVICE_NAME + "-command";

public JedisInstrumentation() {
super(SERVICE_NAME);
}

@Override
protected boolean defaultEnabled() {
return false;
}

@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("redis.clients.jedis.Protocol"),
classLoaderHasClasses("redis.clients.jedis.Protocol$Command"))
.transform(
DDAdvice.create()
.advice(
isMethod()
.and(isPublic())
.and(named("sendCommand"))
.and(takesArgument(1, named("redis.clients.jedis.Protocol$Command"))),
JedisAdvice.class.getName()))
.asDecorator();
}

public static class JedisAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static Scope startSpan(@Advice.Argument(1) final Command command) {

final Scope scope = GlobalTracer.get().buildSpan("redis.command").startActive(true);

final Span span = scope.span();
Tags.DB_TYPE.set(span, SERVICE_NAME);
Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT);
Tags.COMPONENT.set(span, COMPONENT_NAME);

span.setTag(DDTags.RESOURCE_NAME, command.name());
span.setTag(DDTags.SERVICE_NAME, SERVICE_NAME);
span.setTag(DDTags.SPAN_TYPE, SERVICE_NAME);

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();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import datadog.opentracing.DDSpan
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.api.DDTags
import datadog.trace.instrumentation.jedis.JedisInstrumentation
import io.opentracing.tag.Tags
import redis.clients.jedis.Jedis
import redis.embedded.RedisServer
import spock.lang.Shared
import spock.lang.Timeout

@Timeout(5)
class JedisClientTest extends AgentTestRunner {

public static final int PORT = 6399

static {
System.setProperty("dd.integration.redis.enabled", "true")
}

@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()
}

def cleanupSpec() {
redisServer.stop()
// jedis.close() // not available in the early version we're using.
}

def setup() {
jedis.flushAll()
TEST_WRITER.start()
}

def "set command"() {
jedis.set("foo", "bar")

expect:
TEST_WRITER.size() == 1
def trace = TEST_WRITER.firstTrace()
trace.size() == 1
final DDSpan setTrace = trace.get(0)
setTrace.getServiceName() == JedisInstrumentation.SERVICE_NAME
setTrace.getOperationName() == "redis.query"
setTrace.getResourceName() == "SET"
setTrace.getTags().get(Tags.COMPONENT.getKey()) == JedisInstrumentation.COMPONENT_NAME
setTrace.getTags().get(Tags.DB_TYPE.getKey()) == JedisInstrumentation.SERVICE_NAME
setTrace.getTags().get(Tags.SPAN_KIND.getKey()) == Tags.SPAN_KIND_CLIENT
setTrace.getTags().get(DDTags.SPAN_TYPE) == JedisInstrumentation.SERVICE_NAME
}

def "get command"() {
jedis.set("foo", "bar")
def value = jedis.get("foo")

expect:
value == "bar"
TEST_WRITER.size() == 2
def trace = TEST_WRITER.get(1)
trace.size() == 1
final DDSpan getSpan = trace.get(0)
getSpan.getServiceName() == JedisInstrumentation.SERVICE_NAME
getSpan.getOperationName() == "redis.query"
getSpan.getResourceName() == "GET"
getSpan.getTags().get(Tags.COMPONENT.getKey()) == JedisInstrumentation.COMPONENT_NAME
getSpan.getTags().get(Tags.DB_TYPE.getKey()) == JedisInstrumentation.SERVICE_NAME
getSpan.getTags().get(Tags.SPAN_KIND.getKey()) == Tags.SPAN_KIND_CLIENT
getSpan.getTags().get(DDTags.SPAN_TYPE) == JedisInstrumentation.SERVICE_NAME
}

def "command with no arguments"() {
jedis.set("foo", "bar")
def value = jedis.randomKey()

expect:
value == "foo"
TEST_WRITER.size() == 2
def trace = TEST_WRITER.get(1)
trace.size() == 1
final DDSpan randomKeySpan = trace.get(0)
randomKeySpan.getServiceName() == JedisInstrumentation.SERVICE_NAME
randomKeySpan.getOperationName() == "redis.query"
randomKeySpan.getResourceName() == "RANDOMKEY"
randomKeySpan.getTags().get(Tags.COMPONENT.getKey()) == JedisInstrumentation.COMPONENT_NAME
randomKeySpan.getTags().get(Tags.DB_TYPE.getKey()) == JedisInstrumentation.SERVICE_NAME
randomKeySpan.getTags().get(Tags.SPAN_KIND.getKey()) == Tags.SPAN_KIND_CLIENT
randomKeySpan.getTags().get(DDTags.SPAN_TYPE) == JedisInstrumentation.SERVICE_NAME
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import spock.lang.Timeout
import java.util.concurrent.Phaser
import java.util.concurrent.atomic.AtomicInteger

@Timeout(1)
@Timeout(5)
class WriterQueueTest extends Specification {

def "instantiate a empty queue throws an exception"() {
Expand Down Expand Up @@ -120,7 +120,6 @@ class WriterQueueTest extends Specification {
capacity = 100
numberThreads << [1, 10, 100]
numberInsertionsPerThread = 100

}


Expand Down Expand Up @@ -180,7 +179,5 @@ class WriterQueueTest extends Specification {
numberThreadsReads << [1, 5, 10]
numberInsertionsPerThread = 100
numberGetsPerThread = 5

}

}
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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=<your_datadog_api_key> docker-compose up -d
```

Expand All @@ -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
Expand All @@ -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.

Expand All @@ -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).
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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"]
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
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;
Expand All @@ -17,16 +19,27 @@ public class DBResource {
// Which is auto-generated by Spring, we will use it to handle the data
private UserRepository userRepository;

private ValueOperations<String, String> 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

final User n = new User();
n.setName(name);
n.setEmail(email);
userRepository.save(n);

// Also save to redis as key/value
ops.set(name, email);

return "Saved";
}

Expand All @@ -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();
}
}
Loading