Merge pull request #797 from DataDog/tyler/ratpack
Update ratpack instrumentation and remove default disabled.
This commit is contained in:
commit
dc2e435de9
|
@ -32,10 +32,10 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
* <p>For now we're hardcoding this to the instrumentation package so we only create references
|
||||
* from the method advice and helper classes.
|
||||
*/
|
||||
private static String REFERENCE_CREATION_PACKAGE = "datadog.trace.instrumentation.";
|
||||
private static final String REFERENCE_CREATION_PACKAGE = "datadog.trace.instrumentation.";
|
||||
|
||||
public static Map<String, Reference> createReferencesFrom(
|
||||
String entryPointClassName, ClassLoader loader) {
|
||||
final String entryPointClassName, final ClassLoader loader) {
|
||||
return ReferenceCreator.createReferencesFrom(entryPointClassName, loader, true);
|
||||
}
|
||||
|
||||
|
@ -46,9 +46,11 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
* @param loader Classloader used to read class bytes.
|
||||
* @param startFromMethodBodies if true only create refs from method bodies.
|
||||
* @return Map of [referenceClassName -> Reference]
|
||||
* @throws IllegalStateException if class is not found or unable to be loaded.
|
||||
*/
|
||||
private static Map<String, Reference> createReferencesFrom(
|
||||
String entryPointClassName, ClassLoader loader, boolean startFromMethodBodies) {
|
||||
final String entryPointClassName, final ClassLoader loader, boolean startFromMethodBodies)
|
||||
throws IllegalStateException {
|
||||
final Set<String> visitedSources = new HashSet<>();
|
||||
final Map<String, Reference> references = new HashMap<>();
|
||||
|
||||
|
@ -58,37 +60,38 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
while (!instrumentationQueue.isEmpty()) {
|
||||
final String className = instrumentationQueue.remove();
|
||||
visitedSources.add(className);
|
||||
final InputStream in = loader.getResourceAsStream(Utils.getResourceName(className));
|
||||
try {
|
||||
final InputStream in = loader.getResourceAsStream(Utils.getResourceName(className));
|
||||
try {
|
||||
final ReferenceCreator cv = new ReferenceCreator(null, startFromMethodBodies);
|
||||
// only start from method bodies on the first pass
|
||||
startFromMethodBodies = false;
|
||||
final ClassReader reader = new ClassReader(in);
|
||||
reader.accept(cv, ClassReader.SKIP_FRAMES);
|
||||
final ReferenceCreator cv = new ReferenceCreator(null, startFromMethodBodies);
|
||||
// only start from method bodies on the first pass
|
||||
startFromMethodBodies = false;
|
||||
final ClassReader reader = new ClassReader(in);
|
||||
reader.accept(cv, ClassReader.SKIP_FRAMES);
|
||||
|
||||
Map<String, Reference> instrumentationReferences = cv.getReferences();
|
||||
for (Map.Entry<String, Reference> entry : instrumentationReferences.entrySet()) {
|
||||
// Don't generate references created outside of the datadog instrumentation package.
|
||||
if (!visitedSources.contains(entry.getKey())
|
||||
&& entry.getKey().startsWith(REFERENCE_CREATION_PACKAGE)) {
|
||||
instrumentationQueue.add(entry.getKey());
|
||||
}
|
||||
if (references.containsKey(entry.getKey())) {
|
||||
references.put(
|
||||
entry.getKey(), references.get(entry.getKey()).merge(entry.getValue()));
|
||||
} else {
|
||||
references.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
final Map<String, Reference> instrumentationReferences = cv.getReferences();
|
||||
for (final Map.Entry<String, Reference> entry : instrumentationReferences.entrySet()) {
|
||||
// Don't generate references created outside of the datadog instrumentation package.
|
||||
if (!visitedSources.contains(entry.getKey())
|
||||
&& entry.getKey().startsWith(REFERENCE_CREATION_PACKAGE)) {
|
||||
instrumentationQueue.add(entry.getKey());
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
if (references.containsKey(entry.getKey())) {
|
||||
references.put(entry.getKey(), references.get(entry.getKey()).merge(entry.getValue()));
|
||||
} else {
|
||||
references.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (final IOException e) {
|
||||
throw new IllegalStateException("Error reading class " + className, e);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (final IOException e) {
|
||||
throw new IllegalStateException("Error closing class " + className, e);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new IllegalStateException(ioe);
|
||||
}
|
||||
}
|
||||
return references;
|
||||
|
@ -99,7 +102,7 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
*
|
||||
* <p>foo/bar/Baz -> foo/bar/
|
||||
*/
|
||||
private static String internalPackageName(String internalName) {
|
||||
private static String internalPackageName(final String internalName) {
|
||||
return internalName.replaceAll("/[^/]+$", "");
|
||||
}
|
||||
|
||||
|
@ -108,7 +111,7 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
*
|
||||
* @return A reference flag with the required level of access.
|
||||
*/
|
||||
private static Reference.Flag computeMinimumClassAccess(Type from, Type to) {
|
||||
private static Reference.Flag computeMinimumClassAccess(final Type from, final Type to) {
|
||||
if (from.getInternalName().equalsIgnoreCase(to.getInternalName())) {
|
||||
return Reference.Flag.PRIVATE_OR_HIGHER;
|
||||
} else if (internalPackageName(from.getInternalName())
|
||||
|
@ -124,7 +127,7 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
*
|
||||
* @return A reference flag with the required level of access.
|
||||
*/
|
||||
private static Reference.Flag computeMinimumFieldAccess(Type from, Type to) {
|
||||
private static Reference.Flag computeMinimumFieldAccess(final Type from, final Type to) {
|
||||
if (from.getInternalName().equalsIgnoreCase(to.getInternalName())) {
|
||||
return Reference.Flag.PRIVATE_OR_HIGHER;
|
||||
} else if (internalPackageName(from.getInternalName())
|
||||
|
@ -142,7 +145,8 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
*
|
||||
* @return A reference flag with the required level of access.
|
||||
*/
|
||||
private static Reference.Flag computeMinimumMethodAccess(Type from, Type to, Type methodType) {
|
||||
private static Reference.Flag computeMinimumMethodAccess(
|
||||
final Type from, final Type to, final Type methodType) {
|
||||
if (from.getInternalName().equalsIgnoreCase(to.getInternalName())) {
|
||||
return Reference.Flag.PRIVATE_OR_HIGHER;
|
||||
} else {
|
||||
|
@ -163,12 +167,13 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
return type;
|
||||
}
|
||||
|
||||
private Map<String, Reference> references = new HashMap<>();
|
||||
private final Map<String, Reference> references = new HashMap<>();
|
||||
private String refSourceClassName;
|
||||
private Type refSourceType;
|
||||
private boolean createFromMethodBodiesOnly;
|
||||
private final boolean createFromMethodBodiesOnly;
|
||||
|
||||
private ReferenceCreator(ClassVisitor classVisitor, boolean createFromMethodBodiesOnly) {
|
||||
private ReferenceCreator(
|
||||
final ClassVisitor classVisitor, final boolean createFromMethodBodiesOnly) {
|
||||
super(Opcodes.ASM7, classVisitor);
|
||||
this.createFromMethodBodiesOnly = createFromMethodBodiesOnly;
|
||||
}
|
||||
|
@ -177,7 +182,7 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
return references;
|
||||
}
|
||||
|
||||
private void addReference(Reference ref) {
|
||||
private void addReference(final Reference ref) {
|
||||
if (references.containsKey(ref.getClassName())) {
|
||||
references.put(ref.getClassName(), references.get(ref.getClassName()).merge(ref));
|
||||
} else {
|
||||
|
@ -203,7 +208,11 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
|
||||
@Override
|
||||
public FieldVisitor visitField(
|
||||
int access, String name, String descriptor, String signature, Object value) {
|
||||
final int access,
|
||||
final String name,
|
||||
final String descriptor,
|
||||
final String signature,
|
||||
final Object value) {
|
||||
// Additional references we could check
|
||||
// - annotations on field
|
||||
|
||||
|
@ -228,7 +237,7 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
private class AdviceReferenceMethodVisitor extends MethodVisitor {
|
||||
private int currentLineNumber = -1;
|
||||
|
||||
public AdviceReferenceMethodVisitor(MethodVisitor methodVisitor) {
|
||||
public AdviceReferenceMethodVisitor(final MethodVisitor methodVisitor) {
|
||||
super(Opcodes.ASM7, methodVisitor);
|
||||
}
|
||||
|
||||
|
@ -239,7 +248,8 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
|
||||
public void visitFieldInsn(
|
||||
final int opcode, final String owner, final String name, final String descriptor) {
|
||||
// Additional references we could check
|
||||
// * DONE owner class
|
||||
// * DONE owner class has a field (name)
|
||||
|
@ -253,7 +263,7 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
final Type ownerType = Type.getType("L" + owner + ";");
|
||||
final Type fieldType = Type.getType(descriptor);
|
||||
|
||||
List<Reference.Flag> fieldFlags = new ArrayList<>();
|
||||
final List<Reference.Flag> fieldFlags = new ArrayList<>();
|
||||
fieldFlags.add(computeMinimumFieldAccess(refSourceType, ownerType));
|
||||
fieldFlags.add(
|
||||
opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC
|
||||
|
@ -324,7 +334,7 @@ public class ReferenceCreator extends ClassVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
Type ownerType = Type.getType("L" + owner + ";");
|
||||
final Type ownerType = Type.getType("L" + owner + ";");
|
||||
|
||||
final List<Reference.Flag> methodFlags = new ArrayList<>();
|
||||
methodFlags.add(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package datadog.trace.instrumentation.netty40;
|
||||
|
||||
import datadog.trace.context.TraceScope;
|
||||
import datadog.trace.instrumentation.netty40.client.HttpClientTracingHandler;
|
||||
import datadog.trace.instrumentation.netty40.server.HttpServerTracingHandler;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.opentracing.Span;
|
||||
|
@ -14,5 +15,5 @@ public class AttributeKeys {
|
|||
new AttributeKey<>(HttpServerTracingHandler.class.getName() + ".span");
|
||||
|
||||
public static final AttributeKey<Span> CLIENT_ATTRIBUTE_KEY =
|
||||
new AttributeKey<>(HttpServerTracingHandler.class.getName() + ".span");
|
||||
new AttributeKey<>(HttpClientTracingHandler.class.getName() + ".span");
|
||||
}
|
||||
|
|
|
@ -43,6 +43,14 @@ public class ChannelFutureListenerInstrumentation extends Instrumenter.Default {
|
|||
return new String[] {
|
||||
packageName + ".AttributeKeys",
|
||||
"datadog.trace.agent.decorator.BaseDecorator",
|
||||
// client helpers
|
||||
"datadog.trace.agent.decorator.ClientDecorator",
|
||||
"datadog.trace.agent.decorator.HttpClientDecorator",
|
||||
packageName + ".client.NettyHttpClientDecorator",
|
||||
packageName + ".client.NettyResponseInjectAdapter",
|
||||
packageName + ".client.HttpClientRequestTracingHandler",
|
||||
packageName + ".client.HttpClientResponseTracingHandler",
|
||||
packageName + ".client.HttpClientTracingHandler",
|
||||
// server helpers
|
||||
"datadog.trace.agent.decorator.ServerDecorator",
|
||||
"datadog.trace.agent.decorator.HttpServerDecorator",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package datadog.trace.instrumentation.netty41;
|
||||
|
||||
import datadog.trace.context.TraceScope;
|
||||
import datadog.trace.instrumentation.netty41.client.HttpClientTracingHandler;
|
||||
import datadog.trace.instrumentation.netty41.server.HttpServerTracingHandler;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.opentracing.Span;
|
||||
|
@ -10,9 +11,13 @@ public class AttributeKeys {
|
|||
PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY =
|
||||
AttributeKey.valueOf("datadog.trace.instrumentation.netty41.parent.connect.continuation");
|
||||
|
||||
/**
|
||||
* This constant is copied over to datadog.trace.instrumentation.ratpack.server.TracingHandler, so
|
||||
* if this changes, that must also change.
|
||||
*/
|
||||
public static final AttributeKey<Span> SERVER_ATTRIBUTE_KEY =
|
||||
AttributeKey.valueOf(HttpServerTracingHandler.class.getName() + ".span");
|
||||
|
||||
public static final AttributeKey<Span> CLIENT_ATTRIBUTE_KEY =
|
||||
AttributeKey.valueOf(HttpServerTracingHandler.class.getName() + ".span");
|
||||
AttributeKey.valueOf(HttpClientTracingHandler.class.getName() + ".span");
|
||||
}
|
||||
|
|
|
@ -43,6 +43,14 @@ public class ChannelFutureListenerInstrumentation extends Instrumenter.Default {
|
|||
return new String[] {
|
||||
packageName + ".AttributeKeys",
|
||||
"datadog.trace.agent.decorator.BaseDecorator",
|
||||
// client helpers
|
||||
"datadog.trace.agent.decorator.ClientDecorator",
|
||||
"datadog.trace.agent.decorator.HttpClientDecorator",
|
||||
packageName + ".client.NettyHttpClientDecorator",
|
||||
packageName + ".client.NettyResponseInjectAdapter",
|
||||
packageName + ".client.HttpClientRequestTracingHandler",
|
||||
packageName + ".client.HttpClientResponseTracingHandler",
|
||||
packageName + ".client.HttpClientTracingHandler",
|
||||
// server helpers
|
||||
"datadog.trace.agent.decorator.ServerDecorator",
|
||||
"datadog.trace.agent.decorator.HttpServerDecorator",
|
||||
|
@ -102,7 +110,7 @@ public class ChannelFutureListenerInstrumentation extends Instrumenter.Default {
|
|||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void deactivateScope(@Advice.Enter final TraceScope scope) {
|
||||
if (scope != null) {
|
||||
((Scope) scope).close();
|
||||
scope.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,12 @@ muzzle {
|
|||
module = 'ratpack-core'
|
||||
versions = "[1.4.0,)"
|
||||
}
|
||||
// Some maven dependencies are missing for pre 1.0 ratpack, so we can't assertInverse.
|
||||
fail {
|
||||
group = "io.ratpack"
|
||||
module = 'ratpack-core'
|
||||
versions = "[1.0,1.4.0)"
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/gradle/java.gradle"
|
||||
|
@ -39,9 +45,7 @@ dependencies {
|
|||
apply plugin: 'org.unbroken-dome.test-sets'
|
||||
|
||||
testSets {
|
||||
latestDepTest {
|
||||
dirName = 'test'
|
||||
}
|
||||
latestDepTest
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -61,6 +65,7 @@ dependencies {
|
|||
|
||||
testCompile project(':dd-java-agent:testing')
|
||||
testCompile project(':dd-java-agent:instrumentation:java-concurrent')
|
||||
testCompile project(':dd-java-agent:instrumentation:netty-4.1')
|
||||
testCompile group: 'io.ratpack', name: 'ratpack-groovy-test', version: '1.4.0'
|
||||
latestDepTestCompile group: 'io.ratpack', name: 'ratpack-groovy-test', version: '+'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,702 @@
|
|||
import datadog.trace.agent.test.AgentTestRunner
|
||||
import datadog.trace.agent.test.utils.OkHttpUtils
|
||||
import datadog.trace.api.DDSpanTypes
|
||||
import datadog.trace.api.DDTags
|
||||
import datadog.trace.context.TraceScope
|
||||
import io.netty.channel.AbstractChannel
|
||||
import io.opentracing.Scope
|
||||
import io.opentracing.Span
|
||||
import io.opentracing.tag.Tags
|
||||
import io.opentracing.util.GlobalTracer
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import ratpack.exec.Promise
|
||||
import ratpack.exec.util.ParallelBatch
|
||||
import ratpack.groovy.test.embed.GroovyEmbeddedApp
|
||||
import ratpack.handling.internal.HandlerException
|
||||
import ratpack.http.HttpUrlBuilder
|
||||
import ratpack.http.client.HttpClient
|
||||
import ratpack.path.PathBinding
|
||||
import ratpack.test.exec.ExecHarness
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import static datadog.trace.agent.test.server.http.TestHttpServer.distributedRequestTrace
|
||||
import static datadog.trace.agent.test.server.http.TestHttpServer.httpServer
|
||||
import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT
|
||||
|
||||
class RatpackTest extends AgentTestRunner {
|
||||
|
||||
OkHttpClient client = OkHttpUtils.client()
|
||||
|
||||
def "test path call"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get {
|
||||
context.render("success")
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.toURL())
|
||||
.get()
|
||||
.build()
|
||||
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
|
||||
then:
|
||||
resp.code() == 200
|
||||
resp.body.string() == "success"
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(0))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "/"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test path with bindings call"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
prefix(":foo/:bar?") {
|
||||
get("baz") { ctx ->
|
||||
context.render(ctx.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(HttpUrl.get(app.address).newBuilder().addPathSegments("a/b/baz").build())
|
||||
.get()
|
||||
.build()
|
||||
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
|
||||
then:
|
||||
resp.code() == 200
|
||||
resp.body.string() == ":foo/:bar?/baz"
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /:foo/:bar?/baz"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "${app.address}a/b/baz"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /:foo/:bar?/baz"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(0))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "/a/b/baz"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test handler error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get {
|
||||
0 / 0
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.toURL())
|
||||
.get()
|
||||
.build()
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
then:
|
||||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
errorTags(ArithmeticException, Pattern.compile("Division( is)? undefined"))
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(0))
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "/"
|
||||
errorTags(HandlerException, Pattern.compile("java.lang.ArithmeticException: Division( is)? undefined"))
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test promise error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get {
|
||||
Promise.async {
|
||||
0 / 0
|
||||
}.then {
|
||||
context.render(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.toURL())
|
||||
.get()
|
||||
.build()
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
then:
|
||||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
errorTags(ArithmeticException, Pattern.compile("Division( is)? undefined"))
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(0))
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "/"
|
||||
"$Tags.ERROR.key" true
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test render error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get {
|
||||
context.render(Promise.sync {
|
||||
return "fail " + 0 / 0
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.toURL())
|
||||
.get()
|
||||
.build()
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
then:
|
||||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
errorTags(ArithmeticException, Pattern.compile("Division( is)? undefined"))
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(0))
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "/"
|
||||
"$Tags.ERROR.key" true
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test path call using ratpack http client"() {
|
||||
setup:
|
||||
|
||||
// Use jetty based server to avoid confusion.
|
||||
def external = httpServer {
|
||||
handlers {
|
||||
get("nested") {
|
||||
handleDistributedRequest()
|
||||
response.send("succ")
|
||||
}
|
||||
get("nested2") {
|
||||
handleDistributedRequest()
|
||||
response.send("ess")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get { HttpClient httpClient ->
|
||||
// 1st internal http client call to nested
|
||||
httpClient.get(HttpUrlBuilder.base(external.address).path("nested").build())
|
||||
.map { it.body.text }
|
||||
.flatMap { t ->
|
||||
// make a 2nd http request and concatenate the two bodies together
|
||||
httpClient.get(HttpUrlBuilder.base(external.address).path("nested2").build()) map { t + it.body.text }
|
||||
}
|
||||
.then {
|
||||
context.render(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.toURL())
|
||||
.get()
|
||||
.build()
|
||||
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
|
||||
then:
|
||||
resp.code() == 200
|
||||
resp.body().string() == "success"
|
||||
|
||||
// 3rd is the three traces, ratpack, http client 2 and http client 1
|
||||
// 2nd is nested2 from the external server (the result of the 2nd internal http client call)
|
||||
// 1st is nested from the external server (the result of the 1st internal http client call)
|
||||
assertTraces(3) {
|
||||
distributedRequestTrace(it, 0, trace(2).get(3))
|
||||
distributedRequestTrace(it, 1, trace(2).get(2))
|
||||
|
||||
trace(2, 4) {
|
||||
// main app span that processed the request from OKHTTP request
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(0))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "/"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
// Second http client call that receives the 'ess' of Success
|
||||
span(2) {
|
||||
resourceName "GET /?"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.client.request"
|
||||
spanType DDSpanTypes.HTTP_CLIENT
|
||||
childOf(span(3))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty-client"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "${external.address}/nested2"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
// First http client call that receives the 'Succ' of Success
|
||||
span(3) {
|
||||
resourceName "GET /nested"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.client.request"
|
||||
spanType DDSpanTypes.HTTP_CLIENT
|
||||
childOf(span(1))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty-client"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "${external.address}/nested"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test ratpack http client error handling"() {
|
||||
setup:
|
||||
def badAddress = new URI("http://localhost:$UNUSABLE_PORT")
|
||||
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get { HttpClient httpClient ->
|
||||
httpClient.get(badAddress)
|
||||
.map { it.body.text }
|
||||
.then {
|
||||
context.render(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.toURL())
|
||||
.get()
|
||||
.build()
|
||||
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
|
||||
then:
|
||||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 3) {
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
"$Tags.ERROR.key" true
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(0))
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "/"
|
||||
"$Tags.ERROR.key" true
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(2) {
|
||||
operationName "netty.connect"
|
||||
resourceName "netty.connect"
|
||||
childOf(span(1))
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
errorTags(AbstractChannel.AnnotatedConnectException, String)
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test forked path call and start span in handler (#startSpanInHandler)"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get {
|
||||
TraceScope scope
|
||||
if (startSpanInHandler) {
|
||||
Span childSpan = GlobalTracer.get()
|
||||
.buildSpan("ratpack.exec-test")
|
||||
.withTag(DDTags.RESOURCE_NAME, "INSIDE-TEST")
|
||||
.start()
|
||||
scope = GlobalTracer.get().scopeManager().activate(childSpan, true)
|
||||
}
|
||||
def latch = new CountDownLatch(1)
|
||||
try {
|
||||
scope?.setAsyncPropagation(true)
|
||||
GlobalTracer.get().activeSpan().setBaggageItem("test-baggage", "foo")
|
||||
|
||||
context.render(testPromise(latch).fork())
|
||||
} finally {
|
||||
scope?.close()
|
||||
latch.countDown()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.toURL())
|
||||
.get()
|
||||
.build()
|
||||
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
|
||||
then:
|
||||
resp.code() == 200
|
||||
resp.body().string() == "foo"
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, (startSpanInHandler ? 3 : 2)) {
|
||||
if (startSpanInHandler) {
|
||||
span(0) {
|
||||
resourceName "INSIDE-TEST"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.exec-test"
|
||||
childOf(span(2))
|
||||
errored false
|
||||
tags {
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
span(startSpanInHandler ? 1 : 0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(startSpanInHandler ? 2 : 1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(startSpanInHandler ? 1 : 0))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "/"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
startSpanInHandler << [true, false]
|
||||
}
|
||||
|
||||
def "forked executions inherit parent scope"() {
|
||||
when:
|
||||
def result = ExecHarness.yieldSingle({}, {
|
||||
final Scope scope =
|
||||
GlobalTracer.get()
|
||||
.buildSpan("ratpack.exec-test")
|
||||
.withTag(DDTags.RESOURCE_NAME, "INSIDE-TEST")
|
||||
.startActive(true)
|
||||
|
||||
((TraceScope) scope).setAsyncPropagation(true)
|
||||
scope.span().setBaggageItem("test-baggage", "foo")
|
||||
ParallelBatch.of(testPromise(), testPromise())
|
||||
.yield()
|
||||
.map({ now ->
|
||||
// close the scope now that we got the baggage inside the promises
|
||||
scope.close()
|
||||
return now
|
||||
})
|
||||
})
|
||||
|
||||
then:
|
||||
result.valueOrThrow == ["foo", "foo"]
|
||||
assertTraces(1) {
|
||||
trace(0, 1) {
|
||||
span(0) {
|
||||
resourceName "INSIDE-TEST"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.exec-test"
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns a promise that contains the active scope's "test-baggage" baggage
|
||||
Promise<String> testPromise(CountDownLatch latch = null) {
|
||||
Promise.sync {
|
||||
latch?.await()
|
||||
Scope tracerScope = GlobalTracer.get().scopeManager().active()
|
||||
return tracerScope?.span()?.getBaggageItem("test-baggage")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package datadog.trace.instrumentation.ratpack;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
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.Instrumenter;
|
||||
import datadog.trace.context.TraceScope;
|
||||
import io.opentracing.Scope;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import ratpack.exec.internal.Continuation;
|
||||
import ratpack.func.Action;
|
||||
import ratpack.path.PathBinding;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class ExecStreamInstrumentation extends Instrumenter.Default {
|
||||
|
||||
public ExecStreamInstrumentation() {
|
||||
super("ratpack");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return not(isInterface())
|
||||
.and(safeHasSuperType(named("ratpack.exec.internal.DefaultExecution")));
|
||||
}
|
||||
|
||||
// ratpack.exec.internal.DefaultExecution.delimit
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
packageName + ".ExecStreamInstrumentation$ActionWrapper",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return Collections.singletonMap(
|
||||
named("delimit")
|
||||
.or(named("delimitStream"))
|
||||
.and(takesArgument(0, named("ratpack.func.Action")))
|
||||
.and(takesArgument(1, named("ratpack.func.Action"))),
|
||||
WrapActionAdvice.class.getName());
|
||||
}
|
||||
|
||||
public static class WrapActionAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void wrap(
|
||||
@Advice.Argument(value = 0, readOnly = false) Action<Throwable> onError,
|
||||
@Advice.Argument(value = 1, readOnly = false) Action<Continuation> segment) {
|
||||
final Scope scope = GlobalTracer.get().scopeManager().active();
|
||||
if (scope instanceof TraceScope) {
|
||||
final TraceScope.Continuation continuation = ((TraceScope) scope).capture();
|
||||
onError = ActionWrapper.wrapIfNeeded(onError, continuation);
|
||||
segment = ActionWrapper.wrapIfNeeded(segment, continuation);
|
||||
}
|
||||
}
|
||||
|
||||
public void muzzleCheck(final PathBinding binding) {
|
||||
// This was added in 1.4. Added here to ensure consistency with other instrumentation.
|
||||
binding.getDescription();
|
||||
}
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public static class ActionWrapper<T> implements Action<T> {
|
||||
private final Action<T> delegate;
|
||||
private final TraceScope.Continuation traceContinuation;
|
||||
|
||||
private ActionWrapper(
|
||||
final Action<T> delegate, final TraceScope.Continuation traceContinuation) {
|
||||
this.delegate = delegate;
|
||||
this.traceContinuation = traceContinuation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final T subject) throws Exception {
|
||||
if (traceContinuation != null) {
|
||||
try (final TraceScope scope = traceContinuation.activate()) {
|
||||
scope.setAsyncPropagation(true);
|
||||
delegate.execute(subject);
|
||||
}
|
||||
} else {
|
||||
delegate.execute(subject);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Action<T> wrapIfNeeded(
|
||||
final Action<T> delegate, final TraceScope.Continuation traceContinuation) {
|
||||
if (delegate instanceof ActionWrapper) {
|
||||
return delegate;
|
||||
}
|
||||
log.debug("Wrapping action task {}", delegate);
|
||||
return new ActionWrapper<>(delegate, traceContinuation);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
package datadog.trace.instrumentation.ratpack;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import datadog.trace.instrumentation.ratpack.impl.RatpackHttpClientAdvice;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class RatpackHttpClientInstrumentation extends Instrumenter.Default {
|
||||
|
||||
public static final TypeDescription.ForLoadedType URI_TYPE_DESCRIPTION =
|
||||
new TypeDescription.ForLoadedType(URI.class);
|
||||
|
||||
public RatpackHttpClientInstrumentation() {
|
||||
super(RatpackInstrumentation.EXEC_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean defaultEnabled() {
|
||||
// FIXME: Injecting ContextualScopeManager is probably a bug. Verify and check all ratpack
|
||||
// helpers before enabling.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return not(isInterface()).and(safeHasSuperType(named("ratpack.http.client.HttpClient")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
// http helpers
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackHttpClientAdvice$RatpackHttpClientRequestAdvice",
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackHttpClientAdvice$RatpackHttpClientRequestStreamAdvice",
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackHttpClientAdvice$RatpackHttpGetAdvice",
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackHttpClientAdvice$RequestAction",
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackHttpClientAdvice$ResponseAction",
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackHttpClientAdvice$StreamedResponseAction",
|
||||
"datadog.trace.instrumentation.ratpack.impl.RequestSpecInjectAdapter",
|
||||
"datadog.trace.instrumentation.ratpack.impl.WrappedRequestSpec",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
||||
transformers.put(
|
||||
named("request")
|
||||
.and(
|
||||
takesArguments(
|
||||
URI_TYPE_DESCRIPTION, RatpackInstrumentation.ACTION_TYPE_DESCRIPTION)),
|
||||
RatpackHttpClientAdvice.RatpackHttpClientRequestAdvice.class.getName());
|
||||
transformers.put(
|
||||
named("requestStream")
|
||||
.and(
|
||||
takesArguments(
|
||||
URI_TYPE_DESCRIPTION, RatpackInstrumentation.ACTION_TYPE_DESCRIPTION)),
|
||||
RatpackHttpClientAdvice.RatpackHttpClientRequestStreamAdvice.class.getName());
|
||||
transformers.put(
|
||||
named("get")
|
||||
.and(
|
||||
takesArguments(
|
||||
URI_TYPE_DESCRIPTION, RatpackInstrumentation.ACTION_TYPE_DESCRIPTION)),
|
||||
RatpackHttpClientAdvice.RatpackHttpGetAdvice.class.getName());
|
||||
return transformers;
|
||||
}
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
package datadog.trace.instrumentation.ratpack;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import datadog.trace.instrumentation.ratpack.impl.RatpackServerAdvice;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
@Slf4j
|
||||
public final class RatpackInstrumentation extends Instrumenter.Default {
|
||||
|
||||
static final String EXEC_NAME = "ratpack";
|
||||
|
||||
static final TypeDescription.Latent ACTION_TYPE_DESCRIPTION =
|
||||
new TypeDescription.Latent("ratpack.func.Action", Modifier.PUBLIC, null);
|
||||
|
||||
public RatpackInstrumentation() {
|
||||
super(EXEC_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean defaultEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return named("ratpack.server.internal.ServerRegistry");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
// service registry helpers
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackRequestExtractAdapter",
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackServerAdvice",
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackServerAdvice$RatpackServerRegistryAdvice",
|
||||
"datadog.trace.instrumentation.ratpack.impl.TracingHandler"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
isMethod().and(isStatic()).and(named("buildBaseRegistry")),
|
||||
RatpackServerAdvice.RatpackServerRegistryAdvice.class.getName());
|
||||
}
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public static class ExecStarterInstrumentation extends Instrumenter.Default {
|
||||
|
||||
public ExecStarterInstrumentation() {
|
||||
super(EXEC_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean defaultEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return not(isInterface()).and(safeHasSuperType(named("ratpack.exec.ExecStarter")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
// exec helpers
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackServerAdvice$ExecStarterAdvice",
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackServerAdvice$ExecStarterAction"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
named("register").and(takesArguments(ACTION_TYPE_DESCRIPTION)),
|
||||
RatpackServerAdvice.ExecStarterAdvice.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public static class ExecutionInstrumentation extends Default {
|
||||
|
||||
public ExecutionInstrumentation() {
|
||||
super(EXEC_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean defaultEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return named("ratpack.exec.Execution")
|
||||
.or(not(isInterface()).and(safeHasSuperType(named("ratpack.exec.Execution"))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
// exec helpers
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackServerAdvice$ExecStarterAdvice",
|
||||
"datadog.trace.instrumentation.ratpack.impl.RatpackServerAdvice$ExecStarterAction"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
named("fork").and(returns(named("ratpack.exec.ExecStarter"))),
|
||||
RatpackServerAdvice.ExecutionAdvice.class.getName());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package datadog.trace.instrumentation.ratpack;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static datadog.trace.instrumentation.ratpack.RatpackServerDecorator.DECORATE;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
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.Instrumenter;
|
||||
import io.opentracing.Span;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public class ServerErrorHandlerInstrumentation extends Instrumenter.Default {
|
||||
|
||||
public ServerErrorHandlerInstrumentation() {
|
||||
super("ratpack");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return named("ratpack.exec.Execution")
|
||||
.or(not(isInterface()).and(safeHasSuperType(named("ratpack.error.ServerErrorHandler"))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
"datadog.trace.agent.decorator.BaseDecorator",
|
||||
"datadog.trace.agent.decorator.ServerDecorator",
|
||||
"datadog.trace.agent.decorator.HttpServerDecorator",
|
||||
packageName + ".RatpackServerDecorator",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
named("error").and(takesArgument(1, Throwable.class)), ErrorHandlerAdvice.class.getName());
|
||||
}
|
||||
|
||||
public static class ErrorHandlerAdvice {
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void captureThrowable(@Advice.Argument(1) final Throwable throwable) {
|
||||
final Span span = GlobalTracer.get().activeSpan();
|
||||
if (span != null) {
|
||||
DECORATE.onError(span, throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package datadog.trace.instrumentation.ratpack;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public class ServerRegistryInstrumentation extends Instrumenter.Default {
|
||||
|
||||
public ServerRegistryInstrumentation() {
|
||||
super("ratpack");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return named("ratpack.server.internal.ServerRegistry");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
"datadog.trace.agent.decorator.BaseDecorator",
|
||||
"datadog.trace.agent.decorator.ServerDecorator",
|
||||
"datadog.trace.agent.decorator.HttpServerDecorator",
|
||||
packageName + ".RatpackServerDecorator",
|
||||
packageName + ".TracingHandler",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
isMethod().and(isStatic()).and(named("buildBaseRegistry")),
|
||||
packageName + ".ServerRegistryAdvice");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package datadog.trace.instrumentation.ratpack;
|
||||
|
||||
import datadog.trace.agent.decorator.HttpServerDecorator;
|
||||
import datadog.trace.api.DDTags;
|
||||
import io.opentracing.Span;
|
||||
import ratpack.handling.Context;
|
||||
import ratpack.http.Request;
|
||||
import ratpack.http.Response;
|
||||
import ratpack.http.Status;
|
||||
|
||||
public class RatpackServerDecorator extends HttpServerDecorator<Request, Response> {
|
||||
public static final RatpackServerDecorator DECORATE = new RatpackServerDecorator();
|
||||
|
||||
@Override
|
||||
protected String[] instrumentationNames() {
|
||||
return new String[] {"ratpack"};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String component() {
|
||||
return "ratpack";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String method(final Request request) {
|
||||
return request.getMethod().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String url(final Request request) {
|
||||
return request.getUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String hostname(final Request request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer port(final Request request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer status(final Response response) {
|
||||
final Status status = response.getStatus();
|
||||
if (status != null) {
|
||||
return status.getCode();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Span onContext(final Span span, final Context ctx) {
|
||||
|
||||
String description = ctx.getPathBinding().getDescription();
|
||||
if (description == null || description.isEmpty()) {
|
||||
description = ctx.getRequest().getUri();
|
||||
}
|
||||
if (!description.startsWith("/")) {
|
||||
description = "/" + description;
|
||||
}
|
||||
|
||||
final String resourceName = ctx.getRequest().getMethod().getName() + " " + description;
|
||||
span.setTag(DDTags.RESOURCE_NAME, resourceName);
|
||||
|
||||
return span;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package datadog.trace.instrumentation.ratpack;
|
||||
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import ratpack.handling.HandlerDecorator;
|
||||
import ratpack.registry.Registry;
|
||||
|
||||
public class ServerRegistryAdvice {
|
||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||
public static void injectTracing(@Advice.Return(readOnly = false) Registry registry) {
|
||||
registry =
|
||||
registry.join(
|
||||
Registry.builder().add(HandlerDecorator.prepend(TracingHandler.INSTANCE)).build());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package datadog.trace.instrumentation.ratpack;
|
||||
|
||||
import static datadog.trace.instrumentation.ratpack.RatpackServerDecorator.DECORATE;
|
||||
|
||||
import datadog.trace.context.TraceScope;
|
||||
import io.netty.util.Attribute;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.opentracing.Scope;
|
||||
import io.opentracing.Span;
|
||||
import io.opentracing.Tracer;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
import ratpack.handling.Context;
|
||||
import ratpack.handling.Handler;
|
||||
import ratpack.http.Request;
|
||||
|
||||
/**
|
||||
* This Ratpack handler reads tracing headers from the incoming request, starts a span and ensures
|
||||
* that the span is closed when the response is sent
|
||||
*/
|
||||
public final class TracingHandler implements Handler {
|
||||
public static Handler INSTANCE = new TracingHandler();
|
||||
|
||||
/**
|
||||
* This constant is copied over from datadog.trace.instrumentation.netty41.AttributeKeys. The key
|
||||
* string must be kept consistent.
|
||||
*/
|
||||
public static final AttributeKey<Span> SERVER_ATTRIBUTE_KEY =
|
||||
AttributeKey.valueOf(
|
||||
"datadog.trace.instrumentation.netty41.server.HttpServerTracingHandler.span");
|
||||
|
||||
@Override
|
||||
public void handle(final Context ctx) {
|
||||
final Tracer tracer = GlobalTracer.get();
|
||||
final Request request = ctx.getRequest();
|
||||
|
||||
final Attribute<Span> spanAttribute =
|
||||
ctx.getDirectChannelAccess().getChannel().attr(SERVER_ATTRIBUTE_KEY);
|
||||
final Span nettySpan = spanAttribute.get();
|
||||
|
||||
// Relying on executor instrumentation to assume the netty span is in context as the parent.
|
||||
final Span ratpackSpan = tracer.buildSpan("ratpack.handler").start();
|
||||
DECORATE.afterStart(ratpackSpan);
|
||||
DECORATE.onRequest(ratpackSpan, request);
|
||||
|
||||
try (final Scope scope = tracer.scopeManager().activate(ratpackSpan, false)) {
|
||||
if (scope instanceof TraceScope) {
|
||||
((TraceScope) scope).setAsyncPropagation(true);
|
||||
}
|
||||
|
||||
ctx.getResponse()
|
||||
.beforeSend(
|
||||
response -> {
|
||||
try (final Scope ignored = tracer.scopeManager().activate(ratpackSpan, false)) {
|
||||
if (nettySpan != null) {
|
||||
// Rename the netty span resource name with the ratpack route.
|
||||
DECORATE.onContext(nettySpan, ctx);
|
||||
}
|
||||
DECORATE.onResponse(ratpackSpan, response);
|
||||
DECORATE.onContext(ratpackSpan, ctx);
|
||||
DECORATE.beforeFinish(ratpackSpan);
|
||||
ratpackSpan.finish();
|
||||
}
|
||||
});
|
||||
|
||||
ctx.next();
|
||||
} catch (final Throwable e) {
|
||||
DECORATE.onError(ratpackSpan, e);
|
||||
DECORATE.beforeFinish(ratpackSpan);
|
||||
// finish since the callback probably didn't get added.
|
||||
ratpackSpan.finish();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
package datadog.trace.instrumentation.ratpack.impl;
|
||||
|
||||
import static io.opentracing.log.Fields.ERROR_OBJECT;
|
||||
|
||||
import io.opentracing.Span;
|
||||
import io.opentracing.tag.Tags;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import ratpack.exec.Promise;
|
||||
import ratpack.exec.Result;
|
||||
import ratpack.func.Action;
|
||||
import ratpack.http.client.ReceivedResponse;
|
||||
import ratpack.http.client.RequestSpec;
|
||||
import ratpack.http.client.StreamedResponse;
|
||||
|
||||
public class RatpackHttpClientAdvice {
|
||||
public static class RequestAction implements Action<RequestSpec> {
|
||||
|
||||
private final Action<? super RequestSpec> requestAction;
|
||||
private final AtomicReference<Span> spanRef;
|
||||
|
||||
public RequestAction(Action<? super RequestSpec> requestAction, AtomicReference<Span> spanRef) {
|
||||
this.requestAction = requestAction;
|
||||
this.spanRef = spanRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(RequestSpec requestSpec) throws Exception {
|
||||
WrappedRequestSpec wrappedRequestSpec;
|
||||
if (requestSpec instanceof WrappedRequestSpec) {
|
||||
wrappedRequestSpec = (WrappedRequestSpec) requestSpec;
|
||||
} else {
|
||||
wrappedRequestSpec =
|
||||
new WrappedRequestSpec(
|
||||
requestSpec,
|
||||
GlobalTracer.get(),
|
||||
GlobalTracer.get().scopeManager().active(),
|
||||
spanRef);
|
||||
}
|
||||
requestAction.execute(wrappedRequestSpec);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ResponseAction implements Action<Result<ReceivedResponse>> {
|
||||
private final AtomicReference<Span> spanRef;
|
||||
|
||||
public ResponseAction(AtomicReference<Span> spanRef) {
|
||||
this.spanRef = spanRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Result<ReceivedResponse> result) {
|
||||
Span span = spanRef.get();
|
||||
if (span == null) {
|
||||
return;
|
||||
}
|
||||
span.finish();
|
||||
if (result.isError()) {
|
||||
Tags.ERROR.set(span, true);
|
||||
span.log(Collections.singletonMap(ERROR_OBJECT, result.getThrowable()));
|
||||
} else {
|
||||
Tags.HTTP_STATUS.set(span, result.getValue().getStatusCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class StreamedResponseAction implements Action<Result<StreamedResponse>> {
|
||||
private final Span span;
|
||||
|
||||
public StreamedResponseAction(Span span) {
|
||||
this.span = span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Result<StreamedResponse> result) {
|
||||
span.finish();
|
||||
if (result.isError()) {
|
||||
Tags.ERROR.set(span, true);
|
||||
span.log(Collections.singletonMap(ERROR_OBJECT, result.getThrowable()));
|
||||
} else {
|
||||
Tags.HTTP_STATUS.set(span, result.getValue().getStatusCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class RatpackHttpClientRequestAdvice {
|
||||
@Advice.OnMethodEnter
|
||||
public static AtomicReference<Span> injectTracing(
|
||||
@Advice.Argument(value = 1, readOnly = false) Action<? super RequestSpec> requestAction) {
|
||||
AtomicReference<Span> span = new AtomicReference<>();
|
||||
|
||||
//noinspection UnusedAssignment
|
||||
requestAction = new RequestAction(requestAction, span);
|
||||
|
||||
return span;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit
|
||||
public static void finishTracing(
|
||||
@Advice.Return(readOnly = false) Promise<ReceivedResponse> promise,
|
||||
@Advice.Enter AtomicReference<Span> ref) {
|
||||
|
||||
//noinspection UnusedAssignment
|
||||
promise = promise.wiretap(new ResponseAction(ref));
|
||||
}
|
||||
}
|
||||
|
||||
public static class RatpackHttpClientRequestStreamAdvice {
|
||||
@Advice.OnMethodEnter
|
||||
public static AtomicReference<Span> injectTracing(
|
||||
@Advice.Argument(value = 1, readOnly = false) Action<? super RequestSpec> requestAction) {
|
||||
AtomicReference<Span> span = new AtomicReference<>();
|
||||
|
||||
//noinspection UnusedAssignment
|
||||
requestAction = new RequestAction(requestAction, span);
|
||||
|
||||
return span;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit
|
||||
public static void finishTracing(
|
||||
@Advice.Return(readOnly = false) Promise<StreamedResponse> promise,
|
||||
@Advice.Enter AtomicReference<Span> ref) {
|
||||
Span span = ref.get();
|
||||
if (span == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
//noinspection UnusedAssignment
|
||||
promise = promise.wiretap(new StreamedResponseAction(span));
|
||||
}
|
||||
}
|
||||
|
||||
public static class RatpackHttpGetAdvice {
|
||||
@Advice.OnMethodEnter
|
||||
public static void ensureGetMethodSet(
|
||||
@Advice.Argument(value = 1, readOnly = false) Action<? super RequestSpec> requestAction) {
|
||||
//noinspection UnusedAssignment
|
||||
requestAction = requestAction.prepend(RequestSpec::get);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package datadog.trace.instrumentation.ratpack.impl;
|
||||
|
||||
import io.opentracing.propagation.TextMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import ratpack.http.Request;
|
||||
import ratpack.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Simple request extractor in the same vein as @see
|
||||
* io.opentracing.contrib.web.servlet.filter.HttpServletRequestExtractAdapter
|
||||
*/
|
||||
public class RatpackRequestExtractAdapter implements TextMap {
|
||||
private final MultiValueMap<String, String> headers;
|
||||
|
||||
RatpackRequestExtractAdapter(final Request request) {
|
||||
headers = request.getHeaders().asMultiValueMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
return headers.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(final String key, final String value) {
|
||||
throw new UnsupportedOperationException("This class should be used only with Tracer.inject()!");
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package datadog.trace.instrumentation.ratpack.impl;
|
||||
|
||||
import io.opentracing.Scope;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import ratpack.exec.ExecStarter;
|
||||
import ratpack.func.Action;
|
||||
import ratpack.handling.HandlerDecorator;
|
||||
import ratpack.registry.Registry;
|
||||
import ratpack.registry.RegistrySpec;
|
||||
|
||||
@Slf4j
|
||||
public class RatpackServerAdvice {
|
||||
public static class RatpackServerRegistryAdvice {
|
||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||
public static void injectTracing(@Advice.Return(readOnly = false) Registry registry) {
|
||||
registry =
|
||||
registry.join(
|
||||
Registry.builder().add(HandlerDecorator.prepend(new TracingHandler())).build());
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExecStarterAdvice {
|
||||
@Advice.OnMethodEnter
|
||||
public static void addScopeToRegistry(
|
||||
@Advice.Argument(value = 0, readOnly = false) Action<? super RegistrySpec> action) {
|
||||
final Scope active = GlobalTracer.get().scopeManager().active();
|
||||
if (active != null) {
|
||||
action = new ExecStarterAction(active).append(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExecutionAdvice {
|
||||
@Advice.OnMethodExit
|
||||
public static void addScopeToRegistry(@Advice.Return final ExecStarter starter) {
|
||||
final Scope active = GlobalTracer.get().scopeManager().active();
|
||||
if (active != null) {
|
||||
starter.register(new ExecStarterAction(active));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExecStarterAction implements Action<RegistrySpec> {
|
||||
private final Scope active;
|
||||
|
||||
public ExecStarterAction(final Scope active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final RegistrySpec spec) {
|
||||
if (active != null) {
|
||||
spec.add(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package datadog.trace.instrumentation.ratpack.impl;
|
||||
|
||||
import io.opentracing.propagation.TextMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import ratpack.http.client.RequestSpec;
|
||||
|
||||
/**
|
||||
* SimpleTextMap to add headers to an outgoing Ratpack HttpClient request
|
||||
*
|
||||
* @see datadog.trace.instrumentation.apachehttpclient.DDTracingClientExec.HttpHeadersInjectAdapter
|
||||
*/
|
||||
public class RequestSpecInjectAdapter implements TextMap {
|
||||
private final RequestSpec requestSpec;
|
||||
|
||||
public RequestSpecInjectAdapter(RequestSpec requestSpec) {
|
||||
this.requestSpec = requestSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
throw new UnsupportedOperationException("Should be used only with tracer#inject()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, String value) {
|
||||
requestSpec.getHeaders().add(key, value);
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
package datadog.trace.instrumentation.ratpack.impl;
|
||||
|
||||
import datadog.trace.api.DDSpanTypes;
|
||||
import datadog.trace.api.DDTags;
|
||||
import datadog.trace.context.TraceScope;
|
||||
import io.opentracing.Scope;
|
||||
import io.opentracing.Span;
|
||||
import io.opentracing.SpanContext;
|
||||
import io.opentracing.propagation.Format;
|
||||
import io.opentracing.tag.Tags;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
import ratpack.handling.Context;
|
||||
import ratpack.handling.Handler;
|
||||
import ratpack.http.Request;
|
||||
import ratpack.http.Status;
|
||||
|
||||
/**
|
||||
* This Ratpack handler reads tracing headers from the incoming request, starts a scope and ensures
|
||||
* that the scope is closed when the response is sent
|
||||
*/
|
||||
public final class TracingHandler implements Handler {
|
||||
@Override
|
||||
public void handle(final Context ctx) {
|
||||
final Request request = ctx.getRequest();
|
||||
|
||||
final SpanContext extractedContext =
|
||||
GlobalTracer.get()
|
||||
.extract(Format.Builtin.HTTP_HEADERS, new RatpackRequestExtractAdapter(request));
|
||||
|
||||
final Scope scope =
|
||||
GlobalTracer.get()
|
||||
.buildSpan("ratpack.handler")
|
||||
.asChildOf(extractedContext)
|
||||
.withTag(Tags.COMPONENT.getKey(), "ratpack")
|
||||
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
|
||||
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.HTTP_SERVER)
|
||||
.withTag(Tags.HTTP_METHOD.getKey(), request.getMethod().getName())
|
||||
.withTag(Tags.HTTP_URL.getKey(), request.getUri())
|
||||
.startActive(false);
|
||||
|
||||
if (scope instanceof TraceScope) {
|
||||
((TraceScope) scope).setAsyncPropagation(true);
|
||||
}
|
||||
|
||||
final Span rootSpan = scope.span();
|
||||
|
||||
ctx.getResponse()
|
||||
.beforeSend(
|
||||
response -> {
|
||||
final Scope responseScope = GlobalTracer.get().scopeManager().active();
|
||||
|
||||
if (responseScope instanceof TraceScope) {
|
||||
((TraceScope) responseScope).setAsyncPropagation(false);
|
||||
}
|
||||
|
||||
rootSpan.setTag(DDTags.RESOURCE_NAME, getResourceName(ctx));
|
||||
final Status status = response.getStatus();
|
||||
if (status != null) {
|
||||
if (status.is5xx()) {
|
||||
Tags.ERROR.set(rootSpan, true);
|
||||
}
|
||||
Tags.HTTP_STATUS.set(rootSpan, status.getCode());
|
||||
}
|
||||
|
||||
rootSpan.finish();
|
||||
});
|
||||
|
||||
ctx.onClose(
|
||||
requestOutcome -> {
|
||||
final Scope activeScope = GlobalTracer.get().scopeManager().active();
|
||||
if (activeScope != null) {
|
||||
activeScope.close();
|
||||
}
|
||||
});
|
||||
|
||||
ctx.next();
|
||||
}
|
||||
|
||||
private static String getResourceName(final Context ctx) {
|
||||
String description = ctx.getPathBinding().getDescription();
|
||||
if (description == null || description.isEmpty()) {
|
||||
description = ctx.getRequest().getUri();
|
||||
}
|
||||
if (!description.startsWith("/")) {
|
||||
description = "/" + description;
|
||||
}
|
||||
return ctx.getRequest().getMethod().getName() + " " + description;
|
||||
}
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
package datadog.trace.instrumentation.ratpack.impl;
|
||||
|
||||
import datadog.trace.api.DDSpanTypes;
|
||||
import datadog.trace.api.DDTags;
|
||||
import io.opentracing.Scope;
|
||||
import io.opentracing.Span;
|
||||
import io.opentracing.Tracer;
|
||||
import io.opentracing.propagation.Format;
|
||||
import io.opentracing.tag.Tags;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import ratpack.func.Action;
|
||||
import ratpack.func.Function;
|
||||
import ratpack.http.HttpMethod;
|
||||
import ratpack.http.MutableHeaders;
|
||||
import ratpack.http.client.ReceivedResponse;
|
||||
import ratpack.http.client.RequestSpec;
|
||||
|
||||
/**
|
||||
* RequestSpec wrapper that captures the method type, sets up redirect handling and starts new spans
|
||||
* when a method type is set.
|
||||
*/
|
||||
public final class WrappedRequestSpec implements RequestSpec {
|
||||
|
||||
private final RequestSpec delegate;
|
||||
private final Tracer tracer;
|
||||
private final Scope scope;
|
||||
private final AtomicReference<Span> spanRef;
|
||||
|
||||
WrappedRequestSpec(
|
||||
final RequestSpec spec,
|
||||
final Tracer tracer,
|
||||
final Scope scope,
|
||||
final AtomicReference<Span> spanRef) {
|
||||
delegate = spec;
|
||||
this.tracer = tracer;
|
||||
this.scope = scope;
|
||||
this.spanRef = spanRef;
|
||||
delegate.onRedirect(this::redirectHandler);
|
||||
}
|
||||
|
||||
/*
|
||||
* Default redirect handler that ensures the span is marked as received before
|
||||
* a new span is created.
|
||||
*
|
||||
*/
|
||||
private Action<? super RequestSpec> redirectHandler(final ReceivedResponse response) {
|
||||
// handler.handleReceive(response.getStatusCode(), null, span.get());
|
||||
return (s) -> new WrappedRequestSpec(s, tracer, scope, spanRef);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpec redirects(final int maxRedirects) {
|
||||
delegate.redirects(maxRedirects);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpec onRedirect(
|
||||
final Function<? super ReceivedResponse, Action<? super RequestSpec>> function) {
|
||||
|
||||
final Function<? super ReceivedResponse, Action<? super RequestSpec>> wrapped =
|
||||
(ReceivedResponse response) -> redirectHandler(response).append(function.apply(response));
|
||||
|
||||
delegate.onRedirect(wrapped);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpec sslContext(final SSLContext sslContext) {
|
||||
delegate.sslContext(sslContext);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableHeaders getHeaders() {
|
||||
return delegate.getHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpec maxContentLength(final int numBytes) {
|
||||
delegate.maxContentLength(numBytes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpec headers(final Action<? super MutableHeaders> action) throws Exception {
|
||||
delegate.headers(action);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpec method(final HttpMethod method) {
|
||||
final Span span =
|
||||
tracer
|
||||
.buildSpan("ratpack.client-request")
|
||||
.asChildOf(scope != null ? scope.span() : null)
|
||||
.withTag(Tags.COMPONENT.getKey(), "ratpack-httpclient")
|
||||
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
|
||||
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.HTTP_CLIENT)
|
||||
.withTag(Tags.HTTP_URL.getKey(), getUri().toString())
|
||||
.withTag(Tags.HTTP_METHOD.getKey(), method.getName())
|
||||
.start();
|
||||
spanRef.set(span);
|
||||
delegate.method(method);
|
||||
tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new RequestSpecInjectAdapter(this));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpec decompressResponse(final boolean shouldDecompress) {
|
||||
delegate.decompressResponse(shouldDecompress);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return delegate.getUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpec connectTimeout(final Duration duration) {
|
||||
delegate.connectTimeout(duration);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpec readTimeout(final Duration duration) {
|
||||
delegate.readTimeout(duration);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Body getBody() {
|
||||
return delegate.getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpec body(final Action<? super Body> action) throws Exception {
|
||||
delegate.body(action);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -13,21 +13,23 @@ import okhttp3.Request
|
|||
import ratpack.exec.Promise
|
||||
import ratpack.exec.util.ParallelBatch
|
||||
import ratpack.groovy.test.embed.GroovyEmbeddedApp
|
||||
import ratpack.handling.internal.HandlerException
|
||||
import ratpack.http.HttpUrlBuilder
|
||||
import ratpack.http.client.HttpClient
|
||||
import ratpack.path.PathBinding
|
||||
import ratpack.test.exec.ExecHarness
|
||||
import spock.lang.Retry
|
||||
|
||||
@Retry
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import static datadog.trace.agent.test.server.http.TestHttpServer.distributedRequestTrace
|
||||
import static datadog.trace.agent.test.server.http.TestHttpServer.httpServer
|
||||
import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT
|
||||
|
||||
class RatpackTest extends AgentTestRunner {
|
||||
static {
|
||||
System.setProperty("dd.integration.ratpack.enabled", "true")
|
||||
}
|
||||
|
||||
OkHttpClient client = OkHttpUtils.client()
|
||||
|
||||
|
||||
def "test path call"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
|
@ -50,13 +52,32 @@ class RatpackTest extends AgentTestRunner {
|
|||
resp.body.string() == "success"
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
childOf(span(0))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
|
@ -95,13 +116,32 @@ class RatpackTest extends AgentTestRunner {
|
|||
resp.body.string() == ":foo/:bar?/baz"
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /:foo/:bar?/baz"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "${app.address}a/b/baz"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /:foo/:bar?/baz"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
childOf(span(0))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
|
@ -116,7 +156,133 @@ class RatpackTest extends AgentTestRunner {
|
|||
}
|
||||
}
|
||||
|
||||
def "test error response"() {
|
||||
def "test handler error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get {
|
||||
0 / 0
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.toURL())
|
||||
.get()
|
||||
.build()
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
then:
|
||||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
errorTags(ArithmeticException, Pattern.compile("Division( is)? undefined"))
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(0))
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "/"
|
||||
errorTags(HandlerException, Pattern.compile("java.lang.ArithmeticException: Division( is)? undefined"))
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test promise error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get {
|
||||
Promise.async {
|
||||
0 / 0
|
||||
}.then {
|
||||
context.render(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.toURL())
|
||||
.get()
|
||||
.build()
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
then:
|
||||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
errorTags(ArithmeticException, Pattern.compile("Division( is)? undefined"))
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(0))
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "/"
|
||||
"$Tags.ERROR.key" true
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test render error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
|
@ -137,13 +303,33 @@ class RatpackTest extends AgentTestRunner {
|
|||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
errorTags(ArithmeticException, Pattern.compile("Division( is)? undefined"))
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
childOf(span(0))
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
|
@ -151,8 +337,7 @@ class RatpackTest extends AgentTestRunner {
|
|||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "/"
|
||||
"error" true
|
||||
// errorTags(Exception, String) // TODO: find out how to get throwable in instrumentation
|
||||
"$Tags.ERROR.key" true
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
|
@ -163,13 +348,16 @@ class RatpackTest extends AgentTestRunner {
|
|||
def "test path call using ratpack http client"() {
|
||||
setup:
|
||||
|
||||
def external = GroovyEmbeddedApp.ratpack {
|
||||
// Use jetty based server to avoid confusion.
|
||||
def external = httpServer {
|
||||
handlers {
|
||||
get("nested") {
|
||||
context.render("succ")
|
||||
handleDistributedRequest()
|
||||
response.send("succ")
|
||||
}
|
||||
get("nested2") {
|
||||
context.render("ess")
|
||||
handleDistributedRequest()
|
||||
response.send("ess")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,53 +394,37 @@ class RatpackTest extends AgentTestRunner {
|
|||
// 2nd is nested2 from the external server (the result of the 2nd internal http client call)
|
||||
// 1st is nested from the external server (the result of the 1st internal http client call)
|
||||
assertTraces(3) {
|
||||
// simulated external system, first call
|
||||
trace(0, 1) {
|
||||
span(0) {
|
||||
resourceName "GET /nested"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(trace(2).get(2))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "/nested"
|
||||
defaultTags(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
// simulated external system, second call
|
||||
trace(1, 1) {
|
||||
span(0) {
|
||||
resourceName "GET /nested2"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(trace(2).get(1))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "/nested2"
|
||||
defaultTags(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
trace(2, 3) {
|
||||
distributedRequestTrace(it, 0, trace(2).get(3))
|
||||
distributedRequestTrace(it, 1, trace(2).get(2))
|
||||
|
||||
trace(2, 4) {
|
||||
// main app span that processed the request from OKHTTP request
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(0))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
|
@ -263,36 +435,122 @@ class RatpackTest extends AgentTestRunner {
|
|||
}
|
||||
}
|
||||
// Second http client call that receives the 'ess' of Success
|
||||
span(1) {
|
||||
span(2) {
|
||||
resourceName "GET /?"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.client-request"
|
||||
operationName "netty.client.request"
|
||||
spanType DDSpanTypes.HTTP_CLIENT
|
||||
childOf(span(0))
|
||||
childOf(span(3))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack-httpclient"
|
||||
"$Tags.COMPONENT.key" "netty-client"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "${external.address}nested2"
|
||||
"$Tags.HTTP_URL.key" "${external.address}/nested2"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
// First http client call that receives the 'Succ' of Success
|
||||
span(2) {
|
||||
span(3) {
|
||||
resourceName "GET /nested"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.client-request"
|
||||
operationName "netty.client.request"
|
||||
spanType DDSpanTypes.HTTP_CLIENT
|
||||
childOf(span(0))
|
||||
childOf(span(1))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack-httpclient"
|
||||
"$Tags.COMPONENT.key" "netty-client"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "${external.address}nested"
|
||||
"$Tags.HTTP_URL.key" "${external.address}/nested"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test ratpack http client error handling"() {
|
||||
setup:
|
||||
def badAddress = new URI("http://localhost:$UNUSABLE_PORT")
|
||||
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get { HttpClient httpClient ->
|
||||
httpClient.get(badAddress)
|
||||
.map { it.body.text }
|
||||
.then {
|
||||
context.render(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.toURL())
|
||||
.get()
|
||||
.build()
|
||||
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
|
||||
then:
|
||||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 3) {
|
||||
span(0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
"$Tags.ERROR.key" true
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(span(0))
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 500
|
||||
"$Tags.HTTP_URL.key" "/"
|
||||
errorTags(ConnectException, String)
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(2) {
|
||||
operationName "netty.connect"
|
||||
resourceName "netty.connect"
|
||||
childOf(span(1))
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
errorTags(ConnectException, String)
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
|
@ -305,26 +563,24 @@ class RatpackTest extends AgentTestRunner {
|
|||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get {
|
||||
final Scope scope = !startSpanInHandler ? GlobalTracer.get().scopeManager().active() :
|
||||
GlobalTracer.get()
|
||||
TraceScope scope
|
||||
if (startSpanInHandler) {
|
||||
Span childSpan = GlobalTracer.get()
|
||||
.buildSpan("ratpack.exec-test")
|
||||
.withTag(DDTags.RESOURCE_NAME, "INSIDE-TEST")
|
||||
.startActive(false)
|
||||
|
||||
if (startSpanInHandler) {
|
||||
((TraceScope) scope).setAsyncPropagation(true)
|
||||
.start()
|
||||
scope = GlobalTracer.get().scopeManager().activate(childSpan, true)
|
||||
}
|
||||
scope.span().setBaggageItem("test-baggage", "foo")
|
||||
def latch = new CountDownLatch(1)
|
||||
try {
|
||||
scope?.setAsyncPropagation(true)
|
||||
GlobalTracer.get().activeSpan().setBaggageItem("test-baggage", "foo")
|
||||
|
||||
final Span startedSpan = startSpanInHandler ? scope.span() : null
|
||||
if (startSpanInHandler) {
|
||||
scope.close()
|
||||
context.onClose {
|
||||
startedSpan.finish()
|
||||
}
|
||||
context.render(testPromise(latch).fork())
|
||||
} finally {
|
||||
scope?.close()
|
||||
latch.countDown()
|
||||
}
|
||||
|
||||
context.render(testPromise().fork())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -341,13 +597,44 @@ class RatpackTest extends AgentTestRunner {
|
|||
resp.body().string() == "foo"
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, (startSpanInHandler ? 2 : 1)) {
|
||||
trace(0, (startSpanInHandler ? 3 : 2)) {
|
||||
if (startSpanInHandler) {
|
||||
span(0) {
|
||||
resourceName "INSIDE-TEST"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.exec-test"
|
||||
childOf(span(2))
|
||||
errored false
|
||||
tags {
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
span(startSpanInHandler ? 1 : 0) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "netty.request"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "netty"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" 200
|
||||
"$Tags.HTTP_URL.key" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(startSpanInHandler ? 2 : 1) {
|
||||
resourceName "GET /"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
childOf(span(startSpanInHandler ? 1 : 0))
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "ratpack"
|
||||
|
@ -358,18 +645,6 @@ class RatpackTest extends AgentTestRunner {
|
|||
defaultTags()
|
||||
}
|
||||
}
|
||||
if (startSpanInHandler) {
|
||||
span(0) {
|
||||
resourceName "INSIDE-TEST"
|
||||
serviceName "unnamed-java-app"
|
||||
operationName "ratpack.exec-test"
|
||||
childOf(span(1))
|
||||
errored false
|
||||
tags {
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,10 +691,11 @@ class RatpackTest extends AgentTestRunner {
|
|||
}
|
||||
|
||||
// returns a promise that contains the active scope's "test-baggage" baggage
|
||||
Promise<String> testPromise() {
|
||||
Promise<String> testPromise(CountDownLatch latch = null) {
|
||||
Promise.sync {
|
||||
latch?.await()
|
||||
Scope tracerScope = GlobalTracer.get().scopeManager().active()
|
||||
return tracerScope.span().getBaggageItem("test-baggage")
|
||||
return tracerScope?.span()?.getBaggageItem("test-baggage")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,13 +52,13 @@ class TagsAssert {
|
|||
errorTags(errorType, null)
|
||||
}
|
||||
|
||||
def errorTags(Class<Throwable> errorType, Object message) {
|
||||
methodMissing("error", [true].toArray())
|
||||
methodMissing("error.type", [errorType.name].toArray())
|
||||
methodMissing("error.stack", [String].toArray())
|
||||
def errorTags(Class<Throwable> errorType, message) {
|
||||
tag("error", true)
|
||||
tag("error.type", errorType.name)
|
||||
tag("error.stack", String)
|
||||
|
||||
if (message != null) {
|
||||
methodMissing("error.msg", [message].toArray())
|
||||
tag("error.msg", message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -298,6 +298,7 @@ class TestHttpServer implements AutoCloseable {
|
|||
assert body != null
|
||||
|
||||
send()
|
||||
resp.setContentLength(body.bytes.length)
|
||||
resp.writer.print(body)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package datadog.trace.context;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
/** An object when can propagate a datadog trace across multiple threads. */
|
||||
public interface TraceScope {
|
||||
public interface TraceScope extends Closeable {
|
||||
/**
|
||||
* Prevent the trace attached to this TraceScope from reporting until the returned Continuation
|
||||
* finishes.
|
||||
|
@ -11,6 +13,7 @@ public interface TraceScope {
|
|||
Continuation capture();
|
||||
|
||||
/** Close the activated context and allow any underlying spans to finish. */
|
||||
@Override
|
||||
void close();
|
||||
|
||||
/** If true, this context will propagate across async boundaries. */
|
||||
|
|
|
@ -293,6 +293,7 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
spanContextDecorators.put(decorator.getMatchingTag(), list);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void addScopeContext(final ScopeContext context) {
|
||||
scopeManager.addScopeContext(context);
|
||||
}
|
||||
|
|
|
@ -39,12 +39,13 @@ public class ContextualScopeManager implements ScopeManager {
|
|||
return tlsScope.get();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void addScopeContext(final ScopeContext context) {
|
||||
scopeContexts.addFirst(context);
|
||||
}
|
||||
|
||||
/** Attach a listener to scope activation events */
|
||||
public void addScopeListener(ScopeListener listener) {
|
||||
public void addScopeListener(final ScopeListener listener) {
|
||||
scopeListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package datadog.opentracing.scopemanager;
|
|||
import io.opentracing.ScopeManager;
|
||||
|
||||
/** Represents a ScopeManager that is only valid in certain cases such as on a specific thread. */
|
||||
@Deprecated
|
||||
public interface ScopeContext extends ScopeManager {
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue