Migrate Jedis/Lettuce instrumentation to Decorator
This commit is contained in:
parent
c42c4b1f76
commit
d842612697
|
@ -0,0 +1,44 @@
|
||||||
|
package datadog.trace.instrumentation.jedis;
|
||||||
|
|
||||||
|
import datadog.trace.agent.decorator.DatabaseClientDecorator;
|
||||||
|
import datadog.trace.api.DDSpanTypes;
|
||||||
|
import redis.clients.jedis.Protocol;
|
||||||
|
|
||||||
|
public class JedisClientDecorator extends DatabaseClientDecorator<Protocol.Command> {
|
||||||
|
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 Protocol.Command session) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbInstance(final Protocol.Command session) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package datadog.trace.instrumentation.jedis;
|
package datadog.trace.instrumentation.jedis;
|
||||||
|
|
||||||
import static io.opentracing.log.Fields.ERROR_OBJECT;
|
import static datadog.trace.instrumentation.jedis.JedisClientDecorator.DECORATE;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
@ -9,13 +9,8 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
import datadog.trace.api.DDSpanTypes;
|
|
||||||
import datadog.trace.api.DDTags;
|
|
||||||
import io.opentracing.Scope;
|
import io.opentracing.Scope;
|
||||||
import io.opentracing.Span;
|
|
||||||
import io.opentracing.tag.Tags;
|
|
||||||
import io.opentracing.util.GlobalTracer;
|
import io.opentracing.util.GlobalTracer;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
@ -30,7 +25,7 @@ public final class JedisInstrumentation extends Instrumenter.Default {
|
||||||
private static final String COMPONENT_NAME = SERVICE_NAME + "-command";
|
private static final String COMPONENT_NAME = SERVICE_NAME + "-command";
|
||||||
|
|
||||||
public JedisInstrumentation() {
|
public JedisInstrumentation() {
|
||||||
super(SERVICE_NAME);
|
super("jedis", "redis");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -40,7 +35,12 @@ public final class JedisInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return new String[] {};
|
return new String[] {
|
||||||
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
|
packageName + ".JedisClientDecorator",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,35 +51,24 @@ public final class JedisInstrumentation extends Instrumenter.Default {
|
||||||
.and(named("sendCommand"))
|
.and(named("sendCommand"))
|
||||||
.and(takesArgument(1, named("redis.clients.jedis.Protocol$Command"))),
|
.and(takesArgument(1, named("redis.clients.jedis.Protocol$Command"))),
|
||||||
JedisAdvice.class.getName());
|
JedisAdvice.class.getName());
|
||||||
|
// FIXME: This instrumentation only incorporates sending the command, not processing the result.
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class JedisAdvice {
|
public static class JedisAdvice {
|
||||||
|
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static Scope startSpan(@Advice.Argument(1) final Command command) {
|
public static Scope startSpan(@Advice.Argument(1) final Command command) {
|
||||||
|
|
||||||
final Scope scope = GlobalTracer.get().buildSpan("redis.command").startActive(true);
|
final Scope scope = GlobalTracer.get().buildSpan("redis.command").startActive(true);
|
||||||
|
DECORATE.afterStart(scope.span());
|
||||||
final Span span = scope.span();
|
DECORATE.onStatement(scope.span(), command.name());
|
||||||
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, DDSpanTypes.REDIS);
|
|
||||||
|
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
public static void stopSpan(
|
public static void stopSpan(
|
||||||
@Advice.Enter final Scope scope, @Advice.Thrown final Throwable throwable) {
|
@Advice.Enter final Scope scope, @Advice.Thrown final Throwable throwable) {
|
||||||
if (throwable != null) {
|
DECORATE.onError(scope.span(), throwable);
|
||||||
final Span span = scope.span();
|
DECORATE.beforeFinish(scope.span());
|
||||||
Tags.ERROR.set(span, true);
|
|
||||||
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
|
|
||||||
}
|
|
||||||
scope.close();
|
scope.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import datadog.opentracing.DDSpan
|
|
||||||
import datadog.trace.agent.test.AgentTestRunner
|
import datadog.trace.agent.test.AgentTestRunner
|
||||||
import datadog.trace.api.DDSpanTypes
|
import datadog.trace.api.DDSpanTypes
|
||||||
import datadog.trace.api.DDTags
|
import datadog.trace.api.DDTags
|
||||||
import datadog.trace.instrumentation.jedis.JedisInstrumentation
|
|
||||||
import io.opentracing.tag.Tags
|
import io.opentracing.tag.Tags
|
||||||
import redis.clients.jedis.Jedis
|
import redis.clients.jedis.Jedis
|
||||||
import redis.embedded.RedisServer
|
import redis.embedded.RedisServer
|
||||||
|
@ -38,60 +36,110 @@ class JedisClientTest extends AgentTestRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
def "set command"() {
|
def "set command"() {
|
||||||
|
when:
|
||||||
jedis.set("foo", "bar")
|
jedis.set("foo", "bar")
|
||||||
|
|
||||||
expect:
|
then:
|
||||||
TEST_WRITER.size() == 1
|
assertTraces(1) {
|
||||||
def trace = TEST_WRITER.firstTrace()
|
trace(0, 1) {
|
||||||
trace.size() == 1
|
span(0) {
|
||||||
final DDSpan setTrace = trace.get(0)
|
serviceName "redis"
|
||||||
setTrace.getServiceName() == JedisInstrumentation.SERVICE_NAME
|
operationName "redis.query"
|
||||||
setTrace.getOperationName() == "redis.query"
|
resourceName "SET"
|
||||||
setTrace.getResourceName() == "SET"
|
spanType DDSpanTypes.REDIS
|
||||||
setTrace.getSpanType() == DDSpanTypes.REDIS
|
tags {
|
||||||
setTrace.getTags().get(Tags.COMPONENT.getKey()) == JedisInstrumentation.COMPONENT_NAME
|
"$Tags.COMPONENT.key" "redis-command"
|
||||||
setTrace.getTags().get(Tags.DB_TYPE.getKey()) == JedisInstrumentation.SERVICE_NAME
|
"$Tags.DB_TYPE.key" "redis"
|
||||||
setTrace.getTags().get(Tags.SPAN_KIND.getKey()) == Tags.SPAN_KIND_CLIENT
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
setTrace.getTags().get(DDTags.SPAN_TYPE) == JedisInstrumentation.SERVICE_NAME
|
"$DDTags.SPAN_TYPE" DDSpanTypes.REDIS
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def "get command"() {
|
def "get command"() {
|
||||||
|
when:
|
||||||
jedis.set("foo", "bar")
|
jedis.set("foo", "bar")
|
||||||
def value = jedis.get("foo")
|
def value = jedis.get("foo")
|
||||||
|
|
||||||
expect:
|
then:
|
||||||
value == "bar"
|
value == "bar"
|
||||||
TEST_WRITER.size() == 2
|
|
||||||
def trace = TEST_WRITER.get(1)
|
assertTraces(2) {
|
||||||
trace.size() == 1
|
trace(0, 1) {
|
||||||
final DDSpan getSpan = trace.get(0)
|
span(0) {
|
||||||
getSpan.getServiceName() == JedisInstrumentation.SERVICE_NAME
|
serviceName "redis"
|
||||||
getSpan.getOperationName() == "redis.query"
|
operationName "redis.query"
|
||||||
getSpan.getResourceName() == "GET"
|
resourceName "SET"
|
||||||
getSpan.getSpanType() == DDSpanTypes.REDIS
|
spanType DDSpanTypes.REDIS
|
||||||
getSpan.getTags().get(Tags.COMPONENT.getKey()) == JedisInstrumentation.COMPONENT_NAME
|
tags {
|
||||||
getSpan.getTags().get(Tags.DB_TYPE.getKey()) == JedisInstrumentation.SERVICE_NAME
|
"$Tags.COMPONENT.key" "redis-command"
|
||||||
getSpan.getTags().get(Tags.SPAN_KIND.getKey()) == Tags.SPAN_KIND_CLIENT
|
"$Tags.DB_TYPE.key" "redis"
|
||||||
getSpan.getTags().get(DDTags.SPAN_TYPE) == JedisInstrumentation.SERVICE_NAME
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
"$DDTags.SPAN_TYPE" DDSpanTypes.REDIS
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace(1, 1) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "redis"
|
||||||
|
operationName "redis.query"
|
||||||
|
resourceName "GET"
|
||||||
|
spanType DDSpanTypes.REDIS
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "redis-command"
|
||||||
|
"$Tags.DB_TYPE.key" "redis"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
"$DDTags.SPAN_TYPE" DDSpanTypes.REDIS
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def "command with no arguments"() {
|
def "command with no arguments"() {
|
||||||
|
when:
|
||||||
jedis.set("foo", "bar")
|
jedis.set("foo", "bar")
|
||||||
def value = jedis.randomKey()
|
def value = jedis.randomKey()
|
||||||
|
|
||||||
expect:
|
then:
|
||||||
value == "foo"
|
value == "foo"
|
||||||
TEST_WRITER.size() == 2
|
|
||||||
def trace = TEST_WRITER.get(1)
|
assertTraces(2) {
|
||||||
trace.size() == 1
|
trace(0, 1) {
|
||||||
final DDSpan randomKeySpan = trace.get(0)
|
span(0) {
|
||||||
randomKeySpan.getServiceName() == JedisInstrumentation.SERVICE_NAME
|
serviceName "redis"
|
||||||
randomKeySpan.getOperationName() == "redis.query"
|
operationName "redis.query"
|
||||||
randomKeySpan.getResourceName() == "RANDOMKEY"
|
resourceName "SET"
|
||||||
randomKeySpan.getSpanType() == DDSpanTypes.REDIS
|
spanType DDSpanTypes.REDIS
|
||||||
randomKeySpan.getTags().get(Tags.COMPONENT.getKey()) == JedisInstrumentation.COMPONENT_NAME
|
tags {
|
||||||
randomKeySpan.getTags().get(Tags.DB_TYPE.getKey()) == JedisInstrumentation.SERVICE_NAME
|
"$Tags.COMPONENT.key" "redis-command"
|
||||||
randomKeySpan.getTags().get(Tags.SPAN_KIND.getKey()) == Tags.SPAN_KIND_CLIENT
|
"$Tags.DB_TYPE.key" "redis"
|
||||||
randomKeySpan.getTags().get(DDTags.SPAN_TYPE) == JedisInstrumentation.SERVICE_NAME
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
"$DDTags.SPAN_TYPE" DDSpanTypes.REDIS
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace(1, 1) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "redis"
|
||||||
|
operationName "redis.query"
|
||||||
|
resourceName "RANDOMKEY"
|
||||||
|
spanType DDSpanTypes.REDIS
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "redis-command"
|
||||||
|
"$Tags.DB_TYPE.key" "redis"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
"$DDTags.SPAN_TYPE" DDSpanTypes.REDIS
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,6 @@ import net.bytebuddy.matcher.ElementMatcher;
|
||||||
@AutoService(Instrumenter.class)
|
@AutoService(Instrumenter.class)
|
||||||
public class LettuceAsyncCommandsInstrumentation extends Instrumenter.Default {
|
public class LettuceAsyncCommandsInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
public static final String PACKAGE =
|
|
||||||
LettuceAsyncCommandsInstrumentation.class.getPackage().getName();
|
|
||||||
|
|
||||||
public LettuceAsyncCommandsInstrumentation() {
|
public LettuceAsyncCommandsInstrumentation() {
|
||||||
super("lettuce", "lettuce-5-async");
|
super("lettuce", "lettuce-5-async");
|
||||||
}
|
}
|
||||||
|
@ -30,7 +27,12 @@ public class LettuceAsyncCommandsInstrumentation extends Instrumenter.Default {
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return new String[] {
|
return new String[] {
|
||||||
PACKAGE + ".LettuceAsyncBiFunction", PACKAGE + ".LettuceInstrumentationUtil"
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
|
packageName + ".LettuceClientDecorator",
|
||||||
|
packageName + ".LettuceAsyncBiFunction",
|
||||||
|
packageName + ".LettuceInstrumentationUtil"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +43,6 @@ public class LettuceAsyncCommandsInstrumentation extends Instrumenter.Default {
|
||||||
.and(named("dispatch"))
|
.and(named("dispatch"))
|
||||||
.and(takesArgument(0, named("io.lettuce.core.protocol.RedisCommand"))),
|
.and(takesArgument(0, named("io.lettuce.core.protocol.RedisCommand"))),
|
||||||
// Cannot reference class directly here because it would lead to class load failure on Java7
|
// Cannot reference class directly here because it would lead to class load failure on Java7
|
||||||
PACKAGE + ".LettuceAsyncCommandsAdvice");
|
packageName + ".LettuceAsyncCommandsAdvice");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,15 +19,6 @@ import net.bytebuddy.matcher.ElementMatcher;
|
||||||
@AutoService(Instrumenter.class)
|
@AutoService(Instrumenter.class)
|
||||||
public final class LettuceClientInstrumentation extends Instrumenter.Default {
|
public final class LettuceClientInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
public static final String PACKAGE = LettuceClientInstrumentation.class.getPackage().getName();
|
|
||||||
|
|
||||||
private static final String[] HELPER_CLASS_NAMES =
|
|
||||||
new String[] {
|
|
||||||
LettuceReactiveCommandsInstrumentation.class.getPackage().getName()
|
|
||||||
+ ".LettuceInstrumentationUtil",
|
|
||||||
PACKAGE + ".LettuceAsyncBiFunction"
|
|
||||||
};
|
|
||||||
|
|
||||||
public LettuceClientInstrumentation() {
|
public LettuceClientInstrumentation() {
|
||||||
super("lettuce");
|
super("lettuce");
|
||||||
}
|
}
|
||||||
|
@ -39,7 +30,14 @@ public final class LettuceClientInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return HELPER_CLASS_NAMES;
|
return new String[] {
|
||||||
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
|
packageName + ".LettuceClientDecorator",
|
||||||
|
packageName + ".LettuceInstrumentationUtil",
|
||||||
|
packageName + ".LettuceAsyncBiFunction"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -52,6 +50,6 @@ public final class LettuceClientInstrumentation extends Instrumenter.Default {
|
||||||
.and(nameEndsWith("Async"))
|
.and(nameEndsWith("Async"))
|
||||||
.and(takesArgument(1, named("io.lettuce.core.RedisURI"))),
|
.and(takesArgument(1, named("io.lettuce.core.RedisURI"))),
|
||||||
// Cannot reference class directly here because it would lead to class load failure on Java7
|
// Cannot reference class directly here because it would lead to class load failure on Java7
|
||||||
PACKAGE + ".ConnectionFutureAdvice");
|
packageName + ".ConnectionFutureAdvice");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,6 @@ import net.bytebuddy.matcher.ElementMatcher;
|
||||||
@AutoService(Instrumenter.class)
|
@AutoService(Instrumenter.class)
|
||||||
public class LettuceReactiveCommandsInstrumentation extends Instrumenter.Default {
|
public class LettuceReactiveCommandsInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
public static final String PACKAGE =
|
|
||||||
LettuceReactiveCommandsInstrumentation.class.getPackage().getName();
|
|
||||||
|
|
||||||
public LettuceReactiveCommandsInstrumentation() {
|
public LettuceReactiveCommandsInstrumentation() {
|
||||||
super("lettuce", "lettuce-5-rx");
|
super("lettuce", "lettuce-5-rx");
|
||||||
}
|
}
|
||||||
|
@ -33,12 +30,16 @@ public class LettuceReactiveCommandsInstrumentation extends Instrumenter.Default
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return new String[] {
|
return new String[] {
|
||||||
PACKAGE + ".LettuceInstrumentationUtil",
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
PACKAGE + ".rx.LettuceMonoCreationAdvice",
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
PACKAGE + ".rx.LettuceMonoDualConsumer",
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
PACKAGE + ".rx.LettuceFluxCreationAdvice",
|
packageName + ".LettuceClientDecorator",
|
||||||
PACKAGE + ".rx.LettuceFluxTerminationRunnable",
|
packageName + ".LettuceInstrumentationUtil",
|
||||||
PACKAGE + ".rx.LettuceFluxTerminationRunnable$FluxOnSubscribeConsumer"
|
packageName + ".rx.LettuceMonoCreationAdvice",
|
||||||
|
packageName + ".rx.LettuceMonoDualConsumer",
|
||||||
|
packageName + ".rx.LettuceFluxCreationAdvice",
|
||||||
|
packageName + ".rx.LettuceFluxTerminationRunnable",
|
||||||
|
packageName + ".rx.LettuceFluxTerminationRunnable$FluxOnSubscribeConsumer"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ public class LettuceReactiveCommandsInstrumentation extends Instrumenter.Default
|
||||||
.and(takesArgument(0, named("java.util.function.Supplier")))
|
.and(takesArgument(0, named("java.util.function.Supplier")))
|
||||||
.and(returns(named("reactor.core.publisher.Mono"))),
|
.and(returns(named("reactor.core.publisher.Mono"))),
|
||||||
// Cannot reference class directly here because it would lead to class load failure on Java7
|
// Cannot reference class directly here because it would lead to class load failure on Java7
|
||||||
PACKAGE + ".rx.LettuceMonoCreationAdvice");
|
packageName + ".rx.LettuceMonoCreationAdvice");
|
||||||
transformers.put(
|
transformers.put(
|
||||||
isMethod()
|
isMethod()
|
||||||
.and(nameStartsWith("create"))
|
.and(nameStartsWith("create"))
|
||||||
|
@ -59,7 +60,7 @@ public class LettuceReactiveCommandsInstrumentation extends Instrumenter.Default
|
||||||
.and(takesArgument(0, named("java.util.function.Supplier")))
|
.and(takesArgument(0, named("java.util.function.Supplier")))
|
||||||
.and(returns(named("reactor.core.publisher.Flux"))),
|
.and(returns(named("reactor.core.publisher.Flux"))),
|
||||||
// Cannot reference class directly here because it would lead to class load failure on Java7
|
// Cannot reference class directly here because it would lead to class load failure on Java7
|
||||||
PACKAGE + ".rx.LettuceFluxCreationAdvice");
|
packageName + ".rx.LettuceFluxCreationAdvice");
|
||||||
|
|
||||||
return transformers;
|
return transformers;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
package datadog.trace.instrumentation.lettuce;
|
package datadog.trace.instrumentation.lettuce;
|
||||||
|
|
||||||
import static io.opentracing.log.Fields.ERROR_OBJECT;
|
import static datadog.trace.instrumentation.lettuce.LettuceClientDecorator.DECORATE;
|
||||||
|
|
||||||
import datadog.trace.api.DDSpanTypes;
|
|
||||||
import datadog.trace.api.DDTags;
|
|
||||||
import io.lettuce.core.ConnectionFuture;
|
import io.lettuce.core.ConnectionFuture;
|
||||||
import io.lettuce.core.RedisURI;
|
import io.lettuce.core.RedisURI;
|
||||||
import io.opentracing.Scope;
|
import io.opentracing.Scope;
|
||||||
import io.opentracing.Span;
|
import io.opentracing.Span;
|
||||||
import io.opentracing.tag.Tags;
|
|
||||||
import io.opentracing.util.GlobalTracer;
|
import io.opentracing.util.GlobalTracer;
|
||||||
import java.util.Collections;
|
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
public class ConnectionFutureAdvice {
|
public class ConnectionFutureAdvice {
|
||||||
|
@ -18,24 +14,9 @@ public class ConnectionFutureAdvice {
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static Scope startSpan(@Advice.Argument(1) final RedisURI redisURI) {
|
public static Scope startSpan(@Advice.Argument(1) final RedisURI redisURI) {
|
||||||
final Scope scope = GlobalTracer.get().buildSpan("redis.query").startActive(false);
|
final Scope scope = GlobalTracer.get().buildSpan("redis.query").startActive(false);
|
||||||
|
|
||||||
final Span span = scope.span();
|
final Span span = scope.span();
|
||||||
Tags.DB_TYPE.set(span, LettuceInstrumentationUtil.SERVICE_NAME);
|
DECORATE.afterStart(span);
|
||||||
Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT);
|
DECORATE.onConnection(span, redisURI);
|
||||||
Tags.COMPONENT.set(span, LettuceInstrumentationUtil.COMPONENT_NAME);
|
|
||||||
|
|
||||||
final int redisPort = redisURI.getPort();
|
|
||||||
Tags.PEER_PORT.set(span, redisPort);
|
|
||||||
final String redisHost = redisURI.getHost();
|
|
||||||
Tags.PEER_HOSTNAME.set(span, redisHost);
|
|
||||||
|
|
||||||
final String url = redisHost + ":" + redisPort + "/" + redisURI.getDatabase();
|
|
||||||
span.setTag("db.redis.url", url);
|
|
||||||
span.setTag("db.redis.dbIndex", redisURI.getDatabase());
|
|
||||||
span.setTag(DDTags.RESOURCE_NAME, "CONNECT:" + url);
|
|
||||||
span.setTag(DDTags.SERVICE_NAME, LettuceInstrumentationUtil.SERVICE_NAME);
|
|
||||||
span.setTag(DDTags.SPAN_TYPE, DDSpanTypes.REDIS);
|
|
||||||
|
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,17 +24,16 @@ public class ConnectionFutureAdvice {
|
||||||
public static void stopSpan(
|
public static void stopSpan(
|
||||||
@Advice.Enter final Scope scope,
|
@Advice.Enter final Scope scope,
|
||||||
@Advice.Thrown final Throwable throwable,
|
@Advice.Thrown final Throwable throwable,
|
||||||
@Advice.Return final ConnectionFuture connectionFuture) {
|
@Advice.Return final ConnectionFuture<?> connectionFuture) {
|
||||||
|
final Span span = scope.span();
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
final Span span = scope.span();
|
DECORATE.onError(span, throwable);
|
||||||
Tags.ERROR.set(span, true);
|
DECORATE.beforeFinish(span);
|
||||||
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
|
span.finish();
|
||||||
scope.close();
|
scope.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
connectionFuture.handleAsync(new LettuceAsyncBiFunction<>(span));
|
||||||
// close spans on error or normal completion
|
|
||||||
connectionFuture.handleAsync(new LettuceAsyncBiFunction<>(scope.span()));
|
|
||||||
scope.close();
|
scope.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package datadog.trace.instrumentation.lettuce;
|
package datadog.trace.instrumentation.lettuce;
|
||||||
|
|
||||||
import static io.opentracing.log.Fields.ERROR_OBJECT;
|
import static datadog.trace.instrumentation.lettuce.LettuceClientDecorator.DECORATE;
|
||||||
|
|
||||||
import io.opentracing.Span;
|
import io.opentracing.Span;
|
||||||
import io.opentracing.tag.Tags;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
@ -22,21 +20,19 @@ public class LettuceAsyncBiFunction<T extends Object, U extends Throwable, R ext
|
||||||
|
|
||||||
private final Span span;
|
private final Span span;
|
||||||
|
|
||||||
public LettuceAsyncBiFunction(Span span) {
|
public LettuceAsyncBiFunction(final Span span) {
|
||||||
this.span = span;
|
this.span = span;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public R apply(T t, Throwable throwable) {
|
public R apply(final T t, final Throwable throwable) {
|
||||||
if (throwable != null) {
|
if (throwable instanceof CancellationException) {
|
||||||
if (throwable instanceof CancellationException) {
|
span.setTag("db.command.cancelled", true);
|
||||||
this.span.setTag("db.command.cancelled", true);
|
} else {
|
||||||
} else {
|
DECORATE.onError(span, throwable);
|
||||||
Tags.ERROR.set(this.span, true);
|
|
||||||
this.span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.span.finish();
|
DECORATE.beforeFinish(span);
|
||||||
|
span.finish();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,26 @@
|
||||||
package datadog.trace.instrumentation.lettuce;
|
package datadog.trace.instrumentation.lettuce;
|
||||||
|
|
||||||
import static io.opentracing.log.Fields.ERROR_OBJECT;
|
import static datadog.trace.instrumentation.lettuce.LettuceClientDecorator.DECORATE;
|
||||||
|
import static datadog.trace.instrumentation.lettuce.LettuceInstrumentationUtil.doFinishSpanEarly;
|
||||||
|
|
||||||
import datadog.trace.api.DDSpanTypes;
|
|
||||||
import datadog.trace.api.DDTags;
|
|
||||||
import io.lettuce.core.protocol.AsyncCommand;
|
import io.lettuce.core.protocol.AsyncCommand;
|
||||||
import io.lettuce.core.protocol.RedisCommand;
|
import io.lettuce.core.protocol.RedisCommand;
|
||||||
import io.opentracing.Scope;
|
import io.opentracing.Scope;
|
||||||
import io.opentracing.Span;
|
import io.opentracing.Span;
|
||||||
import io.opentracing.tag.Tags;
|
|
||||||
import io.opentracing.util.GlobalTracer;
|
import io.opentracing.util.GlobalTracer;
|
||||||
import java.util.Collections;
|
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
public class LettuceAsyncCommandsAdvice {
|
public class LettuceAsyncCommandsAdvice {
|
||||||
|
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static Scope startSpan(@Advice.Argument(0) final RedisCommand command) {
|
public static Scope startSpan(@Advice.Argument(0) final RedisCommand command) {
|
||||||
final String commandName = LettuceInstrumentationUtil.getCommandName(command);
|
|
||||||
|
|
||||||
final Scope scope =
|
final Scope scope =
|
||||||
GlobalTracer.get()
|
GlobalTracer.get().buildSpan("redis.query").startActive(doFinishSpanEarly(command));
|
||||||
.buildSpan(LettuceInstrumentationUtil.SERVICE_NAME + ".query")
|
|
||||||
.startActive(LettuceInstrumentationUtil.doFinishSpanEarly(commandName));
|
|
||||||
|
|
||||||
final Span span = scope.span();
|
final Span span = scope.span();
|
||||||
Tags.DB_TYPE.set(span, LettuceInstrumentationUtil.SERVICE_NAME);
|
DECORATE.afterStart(span);
|
||||||
Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT);
|
DECORATE.onCommand(span, command);
|
||||||
Tags.COMPONENT.set(span, LettuceInstrumentationUtil.COMPONENT_NAME);
|
|
||||||
|
|
||||||
span.setTag(
|
|
||||||
DDTags.RESOURCE_NAME, LettuceInstrumentationUtil.getCommandResourceName(commandName));
|
|
||||||
span.setTag(DDTags.SERVICE_NAME, LettuceInstrumentationUtil.SERVICE_NAME);
|
|
||||||
span.setTag(DDTags.SPAN_TYPE, DDSpanTypes.REDIS);
|
|
||||||
|
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,17 +33,16 @@ public class LettuceAsyncCommandsAdvice {
|
||||||
|
|
||||||
final Span span = scope.span();
|
final Span span = scope.span();
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
Tags.ERROR.set(span, true);
|
DECORATE.onError(span, throwable);
|
||||||
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
|
DECORATE.beforeFinish(span);
|
||||||
span.finish();
|
span.finish();
|
||||||
scope.close();
|
scope.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String commandName = LettuceInstrumentationUtil.getCommandName(command);
|
|
||||||
// close spans on error or normal completion
|
// close spans on error or normal completion
|
||||||
if (!LettuceInstrumentationUtil.doFinishSpanEarly(commandName)) {
|
if (!doFinishSpanEarly(command)) {
|
||||||
asyncCommand.handleAsync(new LettuceAsyncBiFunction<>(scope.span()));
|
asyncCommand.handleAsync(new LettuceAsyncBiFunction<>(span));
|
||||||
}
|
}
|
||||||
scope.close();
|
scope.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
package datadog.trace.instrumentation.lettuce;
|
||||||
|
|
||||||
|
import datadog.trace.agent.decorator.DatabaseClientDecorator;
|
||||||
|
import datadog.trace.api.DDSpanTypes;
|
||||||
|
import datadog.trace.api.DDTags;
|
||||||
|
import io.lettuce.core.RedisURI;
|
||||||
|
import io.lettuce.core.protocol.RedisCommand;
|
||||||
|
import io.opentracing.Span;
|
||||||
|
import io.opentracing.tag.Tags;
|
||||||
|
|
||||||
|
public class LettuceClientDecorator extends DatabaseClientDecorator<RedisURI> {
|
||||||
|
public static final LettuceClientDecorator DECORATE = new LettuceClientDecorator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String[] instrumentationNames() {
|
||||||
|
return new String[] {"lettuce"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String service() {
|
||||||
|
return "redis";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String component() {
|
||||||
|
return "redis-client";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String spanType() {
|
||||||
|
return DDSpanTypes.REDIS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbType() {
|
||||||
|
return "redis";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbUser(final RedisURI connection) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbInstance(final RedisURI connection) {
|
||||||
|
return connection.getHost() + ":" + connection.getPort() + "/" + connection.getDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Span onConnection(final Span span, final RedisURI connection) {
|
||||||
|
if (connection != null) {
|
||||||
|
Tags.PEER_HOSTNAME.set(span, connection.getHost());
|
||||||
|
Tags.PEER_PORT.set(span, connection.getPort());
|
||||||
|
|
||||||
|
span.setTag("db.redis.dbIndex", connection.getDatabase());
|
||||||
|
span.setTag(DDTags.RESOURCE_NAME, "CONNECT:" + dbInstance(connection));
|
||||||
|
}
|
||||||
|
return super.onConnection(span, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span onCommand(final Span span, final RedisCommand command) {
|
||||||
|
final String commandName = LettuceInstrumentationUtil.getCommandName(command);
|
||||||
|
span.setTag(
|
||||||
|
DDTags.RESOURCE_NAME, LettuceInstrumentationUtil.getCommandResourceName(commandName));
|
||||||
|
return span;
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,16 +7,13 @@ import java.util.Set;
|
||||||
|
|
||||||
public class LettuceInstrumentationUtil {
|
public class LettuceInstrumentationUtil {
|
||||||
|
|
||||||
public static final String SERVICE_NAME = "redis";
|
|
||||||
public static final String COMPONENT_NAME = SERVICE_NAME + "-client";
|
|
||||||
|
|
||||||
public static final String[] NON_INSTRUMENTING_COMMAND_WORDS =
|
public static final String[] NON_INSTRUMENTING_COMMAND_WORDS =
|
||||||
new String[] {"SHUTDOWN", "DEBUG", "OOM", "SEGFAULT"};
|
new String[] {"SHUTDOWN", "DEBUG", "OOM", "SEGFAULT"};
|
||||||
|
|
||||||
public static final String[] AGENT_CRASHING_COMMANDS_WORDS =
|
public static final String[] AGENT_CRASHING_COMMANDS_WORDS =
|
||||||
new String[] {"CLIENT", "CLUSTER", "COMMAND", "CONFIG", "DEBUG", "SCRIPT"};
|
new String[] {"CLIENT", "CLUSTER", "COMMAND", "CONFIG", "DEBUG", "SCRIPT"};
|
||||||
|
|
||||||
public static final String AGENT_CRASHING_COMMAND_PREFIX = "COMMAMND-NAME:";
|
public static final String AGENT_CRASHING_COMMAND_PREFIX = "COMMAND-NAME:";
|
||||||
|
|
||||||
public static final Set<String> nonInstrumentingCommands =
|
public static final Set<String> nonInstrumentingCommands =
|
||||||
new HashSet<>(Arrays.asList(NON_INSTRUMENTING_COMMAND_WORDS));
|
new HashSet<>(Arrays.asList(NON_INSTRUMENTING_COMMAND_WORDS));
|
||||||
|
@ -29,10 +26,11 @@ public class LettuceInstrumentationUtil {
|
||||||
* added and the command is executed) because these commands have no return values/call backs, so
|
* added and the command is executed) because these commands have no return values/call backs, so
|
||||||
* we must close the span early in order to provide info for the users
|
* we must close the span early in order to provide info for the users
|
||||||
*
|
*
|
||||||
* @param commandName a redis command, without any prefixes
|
* @param command
|
||||||
* @return true if finish the span early (the command will not have a return value)
|
* @return true if finish the span early (the command will not have a return value)
|
||||||
*/
|
*/
|
||||||
public static boolean doFinishSpanEarly(final String commandName) {
|
public static boolean doFinishSpanEarly(final RedisCommand command) {
|
||||||
|
final String commandName = LettuceInstrumentationUtil.getCommandName(command);
|
||||||
return nonInstrumentingCommands.contains(commandName);
|
return nonInstrumentingCommands.contains(commandName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package datadog.trace.instrumentation.lettuce.rx;
|
package datadog.trace.instrumentation.lettuce.rx;
|
||||||
|
|
||||||
import datadog.trace.instrumentation.lettuce.LettuceInstrumentationUtil;
|
import static datadog.trace.instrumentation.lettuce.LettuceInstrumentationUtil.doFinishSpanEarly;
|
||||||
|
|
||||||
import io.lettuce.core.protocol.RedisCommand;
|
import io.lettuce.core.protocol.RedisCommand;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
|
@ -9,19 +10,20 @@ import reactor.core.publisher.Flux;
|
||||||
public class LettuceFluxCreationAdvice {
|
public class LettuceFluxCreationAdvice {
|
||||||
|
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static String extractCommandName(
|
public static RedisCommand extractCommandName(
|
||||||
@Advice.Argument(0) final Supplier<RedisCommand> supplier) {
|
@Advice.Argument(0) final Supplier<RedisCommand> supplier) {
|
||||||
return LettuceInstrumentationUtil.getCommandName(supplier.get());
|
return supplier.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is an exception thrown, then don't make spans
|
// if there is an exception thrown, then don't make spans
|
||||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
public static void monitorSpan(
|
public static void monitorSpan(
|
||||||
@Advice.Enter final String commandName, @Advice.Return(readOnly = false) Flux<?> publisher) {
|
@Advice.Enter final RedisCommand command,
|
||||||
|
@Advice.Return(readOnly = false) Flux<?> publisher) {
|
||||||
|
|
||||||
boolean finishSpanOnClose = LettuceInstrumentationUtil.doFinishSpanEarly(commandName);
|
final boolean finishSpanOnClose = doFinishSpanEarly(command);
|
||||||
LettuceFluxTerminationRunnable handler =
|
final LettuceFluxTerminationRunnable handler =
|
||||||
new LettuceFluxTerminationRunnable(commandName, finishSpanOnClose);
|
new LettuceFluxTerminationRunnable(command, finishSpanOnClose);
|
||||||
publisher = publisher.doOnSubscribe(handler.getOnSubscribeConsumer());
|
publisher = publisher.doOnSubscribe(handler.getOnSubscribeConsumer());
|
||||||
// don't register extra callbacks to finish the spans if the command being instrumented is one
|
// don't register extra callbacks to finish the spans if the command being instrumented is one
|
||||||
// of those that return
|
// of those that return
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
package datadog.trace.instrumentation.lettuce.rx;
|
package datadog.trace.instrumentation.lettuce.rx;
|
||||||
|
|
||||||
import static io.opentracing.log.Fields.ERROR_OBJECT;
|
import static datadog.trace.instrumentation.lettuce.LettuceClientDecorator.DECORATE;
|
||||||
|
|
||||||
import datadog.trace.api.DDSpanTypes;
|
import io.lettuce.core.protocol.RedisCommand;
|
||||||
import datadog.trace.api.DDTags;
|
|
||||||
import datadog.trace.instrumentation.lettuce.LettuceInstrumentationUtil;
|
|
||||||
import io.opentracing.Scope;
|
|
||||||
import io.opentracing.Span;
|
import io.opentracing.Span;
|
||||||
import io.opentracing.tag.Tags;
|
|
||||||
import io.opentracing.util.GlobalTracer;
|
import io.opentracing.util.GlobalTracer;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.reactivestreams.Subscription;
|
import org.reactivestreams.Subscription;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -23,8 +18,9 @@ public class LettuceFluxTerminationRunnable implements Consumer<Signal>, Runnabl
|
||||||
private int numResults = 0;
|
private int numResults = 0;
|
||||||
private FluxOnSubscribeConsumer onSubscribeConsumer = null;
|
private FluxOnSubscribeConsumer onSubscribeConsumer = null;
|
||||||
|
|
||||||
public LettuceFluxTerminationRunnable(final String commandName, final boolean finishSpanOnClose) {
|
public LettuceFluxTerminationRunnable(
|
||||||
this.onSubscribeConsumer = new FluxOnSubscribeConsumer(this, commandName, finishSpanOnClose);
|
final RedisCommand command, final boolean finishSpanOnClose) {
|
||||||
|
onSubscribeConsumer = new FluxOnSubscribeConsumer(this, command, finishSpanOnClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluxOnSubscribeConsumer getOnSubscribeConsumer() {
|
public FluxOnSubscribeConsumer getOnSubscribeConsumer() {
|
||||||
|
@ -32,16 +28,14 @@ public class LettuceFluxTerminationRunnable implements Consumer<Signal>, Runnabl
|
||||||
}
|
}
|
||||||
|
|
||||||
private void finishSpan(final boolean isCommandCancelled, final Throwable throwable) {
|
private void finishSpan(final boolean isCommandCancelled, final Throwable throwable) {
|
||||||
if (this.span != null) {
|
if (span != null) {
|
||||||
this.span.setTag("db.command.results.count", this.numResults);
|
span.setTag("db.command.results.count", numResults);
|
||||||
if (isCommandCancelled) {
|
if (isCommandCancelled) {
|
||||||
this.span.setTag("db.command.cancelled", true);
|
span.setTag("db.command.cancelled", true);
|
||||||
}
|
}
|
||||||
if (throwable != null) {
|
DECORATE.onError(span, throwable);
|
||||||
Tags.ERROR.set(this.span, true);
|
DECORATE.beforeFinish(span);
|
||||||
this.span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
|
span.finish();
|
||||||
}
|
|
||||||
this.span.finish();
|
|
||||||
} else {
|
} else {
|
||||||
LoggerFactory.getLogger(Flux.class)
|
LoggerFactory.getLogger(Flux.class)
|
||||||
.error(
|
.error(
|
||||||
|
@ -56,13 +50,13 @@ public class LettuceFluxTerminationRunnable implements Consumer<Signal>, Runnabl
|
||||||
|| SignalType.ON_ERROR.equals(signal.getType())) {
|
|| SignalType.ON_ERROR.equals(signal.getType())) {
|
||||||
finishSpan(false, signal.getThrowable());
|
finishSpan(false, signal.getThrowable());
|
||||||
} else if (SignalType.ON_NEXT.equals(signal.getType())) {
|
} else if (SignalType.ON_NEXT.equals(signal.getType())) {
|
||||||
++this.numResults;
|
++numResults;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (this.span != null) {
|
if (span != null) {
|
||||||
finishSpan(true, null);
|
finishSpan(true, null);
|
||||||
} else {
|
} else {
|
||||||
LoggerFactory.getLogger(Flux.class)
|
LoggerFactory.getLogger(Flux.class)
|
||||||
|
@ -75,39 +69,28 @@ public class LettuceFluxTerminationRunnable implements Consumer<Signal>, Runnabl
|
||||||
public static class FluxOnSubscribeConsumer implements Consumer<Subscription> {
|
public static class FluxOnSubscribeConsumer implements Consumer<Subscription> {
|
||||||
|
|
||||||
private final LettuceFluxTerminationRunnable owner;
|
private final LettuceFluxTerminationRunnable owner;
|
||||||
private final String commandName;
|
private final RedisCommand command;
|
||||||
private final boolean finishSpanOnClose;
|
private final boolean finishSpanOnClose;
|
||||||
|
|
||||||
public FluxOnSubscribeConsumer(
|
public FluxOnSubscribeConsumer(
|
||||||
final LettuceFluxTerminationRunnable owner,
|
final LettuceFluxTerminationRunnable owner,
|
||||||
final String commandName,
|
final RedisCommand command,
|
||||||
final boolean finishSpanOnClose) {
|
final boolean finishSpanOnClose) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.commandName = commandName;
|
this.command = command;
|
||||||
this.finishSpanOnClose = finishSpanOnClose;
|
this.finishSpanOnClose = finishSpanOnClose;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(final Subscription subscription) {
|
public void accept(final Subscription subscription) {
|
||||||
final Scope scope =
|
final Span span = GlobalTracer.get().buildSpan("redis.query").start();
|
||||||
GlobalTracer.get()
|
owner.span = span;
|
||||||
.buildSpan(LettuceInstrumentationUtil.SERVICE_NAME + ".query")
|
DECORATE.afterStart(span);
|
||||||
.startActive(finishSpanOnClose);
|
DECORATE.onCommand(span, command);
|
||||||
final Span span = scope.span();
|
if (finishSpanOnClose) {
|
||||||
this.owner.span = span;
|
DECORATE.beforeFinish(span);
|
||||||
|
span.finish();
|
||||||
Tags.DB_TYPE.set(span, LettuceInstrumentationUtil.SERVICE_NAME);
|
}
|
||||||
Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT);
|
|
||||||
Tags.COMPONENT.set(span, LettuceInstrumentationUtil.COMPONENT_NAME);
|
|
||||||
|
|
||||||
// should be command name only, but use workaround to prepend string to agent crashing
|
|
||||||
// commands
|
|
||||||
span.setTag(
|
|
||||||
DDTags.RESOURCE_NAME,
|
|
||||||
LettuceInstrumentationUtil.getCommandResourceName(this.commandName));
|
|
||||||
span.setTag(DDTags.SERVICE_NAME, LettuceInstrumentationUtil.SERVICE_NAME);
|
|
||||||
span.setTag(DDTags.SPAN_TYPE, DDSpanTypes.REDIS);
|
|
||||||
scope.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,19 +9,19 @@ import reactor.core.publisher.Mono;
|
||||||
public class LettuceMonoCreationAdvice {
|
public class LettuceMonoCreationAdvice {
|
||||||
|
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static String extractCommandName(
|
public static RedisCommand extractCommandName(
|
||||||
@Advice.Argument(0) final Supplier<RedisCommand> supplier) {
|
@Advice.Argument(0) final Supplier<RedisCommand> supplier) {
|
||||||
return LettuceInstrumentationUtil.getCommandName(supplier.get());
|
return supplier.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
// throwables wouldn't matter here, because no spans have been started due to redis command not
|
// throwables wouldn't matter here, because no spans have been started due to redis command not
|
||||||
// being run until the user subscribes to the Mono publisher
|
// being run until the user subscribes to the Mono publisher
|
||||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
public static void monitorSpan(
|
public static void monitorSpan(
|
||||||
@Advice.Enter final String commandName, @Advice.Return(readOnly = false) Mono<?> publisher) {
|
@Advice.Enter final RedisCommand command,
|
||||||
|
@Advice.Return(readOnly = false) Mono<?> publisher) {
|
||||||
boolean finishSpanOnClose = LettuceInstrumentationUtil.doFinishSpanEarly(commandName);
|
final boolean finishSpanOnClose = LettuceInstrumentationUtil.doFinishSpanEarly(command);
|
||||||
LettuceMonoDualConsumer mdc = new LettuceMonoDualConsumer(commandName, finishSpanOnClose);
|
final LettuceMonoDualConsumer mdc = new LettuceMonoDualConsumer(command, finishSpanOnClose);
|
||||||
publisher = publisher.doOnSubscribe(mdc);
|
publisher = publisher.doOnSubscribe(mdc);
|
||||||
// register the call back to close the span only if necessary
|
// register the call back to close the span only if necessary
|
||||||
if (!finishSpanOnClose) {
|
if (!finishSpanOnClose) {
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
package datadog.trace.instrumentation.lettuce.rx;
|
package datadog.trace.instrumentation.lettuce.rx;
|
||||||
|
|
||||||
import static io.opentracing.log.Fields.ERROR_OBJECT;
|
import static datadog.trace.instrumentation.lettuce.LettuceClientDecorator.DECORATE;
|
||||||
|
|
||||||
import datadog.trace.api.DDSpanTypes;
|
import io.lettuce.core.protocol.RedisCommand;
|
||||||
import datadog.trace.api.DDTags;
|
|
||||||
import datadog.trace.instrumentation.lettuce.LettuceInstrumentationUtil;
|
|
||||||
import io.opentracing.Scope;
|
import io.opentracing.Scope;
|
||||||
import io.opentracing.Span;
|
import io.opentracing.Span;
|
||||||
import io.opentracing.tag.Tags;
|
|
||||||
import io.opentracing.util.GlobalTracer;
|
import io.opentracing.util.GlobalTracer;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -19,22 +15,29 @@ public class LettuceMonoDualConsumer<R, T, U extends Throwable>
|
||||||
implements Consumer<R>, BiConsumer<T, Throwable> {
|
implements Consumer<R>, BiConsumer<T, Throwable> {
|
||||||
|
|
||||||
private Span span = null;
|
private Span span = null;
|
||||||
private final String commandName;
|
private final RedisCommand command;
|
||||||
private final boolean finishSpanOnClose;
|
private final boolean finishSpanOnClose;
|
||||||
|
|
||||||
public LettuceMonoDualConsumer(final String commandName, final boolean finishSpanOnClose) {
|
public LettuceMonoDualConsumer(final RedisCommand command, final boolean finishSpanOnClose) {
|
||||||
this.commandName = commandName;
|
this.command = command;
|
||||||
this.finishSpanOnClose = finishSpanOnClose;
|
this.finishSpanOnClose = finishSpanOnClose;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(final R r) {
|
||||||
|
final Scope scope = GlobalTracer.get().buildSpan("redis.query").startActive(finishSpanOnClose);
|
||||||
|
span = scope.span();
|
||||||
|
DECORATE.afterStart(span);
|
||||||
|
DECORATE.onCommand(span, command);
|
||||||
|
scope.close();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(final T t, final Throwable throwable) {
|
public void accept(final T t, final Throwable throwable) {
|
||||||
if (this.span != null) {
|
if (span != null) {
|
||||||
if (throwable != null) {
|
DECORATE.onError(span, throwable);
|
||||||
Tags.ERROR.set(this.span, true);
|
DECORATE.beforeFinish(span);
|
||||||
this.span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
|
span.finish();
|
||||||
}
|
|
||||||
this.span.finish();
|
|
||||||
} else {
|
} else {
|
||||||
LoggerFactory.getLogger(Mono.class)
|
LoggerFactory.getLogger(Mono.class)
|
||||||
.error(
|
.error(
|
||||||
|
@ -42,24 +45,4 @@ public class LettuceMonoDualConsumer<R, T, U extends Throwable>
|
||||||
+ "it probably wasn't started.");
|
+ "it probably wasn't started.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(final R r) {
|
|
||||||
final Scope scope =
|
|
||||||
GlobalTracer.get()
|
|
||||||
.buildSpan(LettuceInstrumentationUtil.SERVICE_NAME + ".query")
|
|
||||||
.startActive(finishSpanOnClose);
|
|
||||||
this.span = scope.span();
|
|
||||||
|
|
||||||
Tags.DB_TYPE.set(this.span, LettuceInstrumentationUtil.SERVICE_NAME);
|
|
||||||
Tags.SPAN_KIND.set(this.span, Tags.SPAN_KIND_CLIENT);
|
|
||||||
Tags.COMPONENT.set(this.span, LettuceInstrumentationUtil.COMPONENT_NAME);
|
|
||||||
|
|
||||||
// should be command name only, but use workaround to prepend string to agent crashing commands
|
|
||||||
this.span.setTag(
|
|
||||||
DDTags.RESOURCE_NAME, LettuceInstrumentationUtil.getCommandResourceName(this.commandName));
|
|
||||||
this.span.setTag(DDTags.SERVICE_NAME, LettuceInstrumentationUtil.SERVICE_NAME);
|
|
||||||
this.span.setTag(DDTags.SPAN_TYPE, DDSpanTypes.REDIS);
|
|
||||||
scope.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,7 +124,7 @@ class LettuceAsyncClientTest extends AgentTestRunner {
|
||||||
tags {
|
tags {
|
||||||
defaultTags()
|
defaultTags()
|
||||||
"component" "redis-client"
|
"component" "redis-client"
|
||||||
"db.redis.url" dbAddr
|
"db.instance" dbAddr
|
||||||
"db.redis.dbIndex" 0
|
"db.redis.dbIndex" 0
|
||||||
"db.type" "redis"
|
"db.type" "redis"
|
||||||
"peer.hostname" HOST
|
"peer.hostname" HOST
|
||||||
|
@ -165,7 +165,7 @@ class LettuceAsyncClientTest extends AgentTestRunner {
|
||||||
tags {
|
tags {
|
||||||
defaultTags()
|
defaultTags()
|
||||||
"component" "redis-client"
|
"component" "redis-client"
|
||||||
"db.redis.url" dbAddrNonExistent
|
"db.instance" dbAddrNonExistent
|
||||||
"db.redis.dbIndex" 0
|
"db.redis.dbIndex" 0
|
||||||
"db.type" "redis"
|
"db.type" "redis"
|
||||||
errorTags CompletionException, String
|
errorTags CompletionException, String
|
||||||
|
|
|
@ -104,7 +104,7 @@ class LettuceSyncClientTest extends AgentTestRunner {
|
||||||
tags {
|
tags {
|
||||||
defaultTags()
|
defaultTags()
|
||||||
"component" "redis-client"
|
"component" "redis-client"
|
||||||
"db.redis.url" dbAddr
|
"db.instance" dbAddr
|
||||||
"db.redis.dbIndex" 0
|
"db.redis.dbIndex" 0
|
||||||
"db.type" "redis"
|
"db.type" "redis"
|
||||||
"peer.hostname" HOST
|
"peer.hostname" HOST
|
||||||
|
@ -142,7 +142,7 @@ class LettuceSyncClientTest extends AgentTestRunner {
|
||||||
tags {
|
tags {
|
||||||
defaultTags()
|
defaultTags()
|
||||||
"component" "redis-client"
|
"component" "redis-client"
|
||||||
"db.redis.url" dbAddrNonExistent
|
"db.instance" dbAddrNonExistent
|
||||||
"db.redis.dbIndex" 0
|
"db.redis.dbIndex" 0
|
||||||
"db.type" "redis"
|
"db.type" "redis"
|
||||||
errorTags CompletionException, String
|
errorTags CompletionException, String
|
||||||
|
|
Loading…
Reference in New Issue