parent
de350c7c4f
commit
3db2d654f1
|
@ -181,7 +181,9 @@ class VersionScanPlugin implements Plugin<Project> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// println "Scanning ${includeVersionSet.size()} included and ${excludeVersionSet.size()} excluded versions. Included: ${includeVersionSet.collect { it.version }}}"
|
// println "Scanning ${includeVersionSet.size()} included and ${excludeVersionSet.size()} excluded versions."
|
||||||
|
// println "Included: ${includeVersionSet.collect { it.version }}}"
|
||||||
|
// println "Excluded: ${excludeVersionSet.collect { it.version }}}"
|
||||||
|
|
||||||
includeVersionSet.each { version ->
|
includeVersionSet.each { version ->
|
||||||
addScanTask("Include", new DefaultArtifact(version.groupId, version.artifactId, "jar", version.version), keyPresent, allInclude, project)
|
addScanTask("Include", new DefaultArtifact(version.groupId, version.artifactId, "jar", version.version), keyPresent, allInclude, project)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import spock.lang.Timeout
|
||||||
|
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
@Timeout(1)
|
@Timeout(10)
|
||||||
class ShadowPackageRenamingTest extends Specification {
|
class ShadowPackageRenamingTest extends Specification {
|
||||||
def "agent dependencies renamed"() {
|
def "agent dependencies renamed"() {
|
||||||
setup:
|
setup:
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
apply plugin: 'version-scan'
|
apply plugin: 'version-scan'
|
||||||
|
|
||||||
versionScan {
|
versionScan {
|
||||||
group = "org.eclipse.jetty.server"
|
group = "org.eclipse.jetty"
|
||||||
module = 'org.eclipse.jetty.server-Handler'
|
module = 'jetty-server'
|
||||||
versions = "[8.0.0.v20110901,)"
|
versions = "[8.0.0.v20110901,)"
|
||||||
verifyPresent = [
|
verifyMissing = [
|
||||||
"javax.servlet.AsyncEvent" : null,
|
"org.eclipse.jetty.server.AsyncContext",
|
||||||
"javax.servlet.AsyncListener": null,
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +28,5 @@ dependencies {
|
||||||
testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version: '8.0.0.v20110901'
|
testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version: '8.0.0.v20110901'
|
||||||
testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '8.0.0.v20110901'
|
testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '8.0.0.v20110901'
|
||||||
|
|
||||||
testCompile project(':dd-java-agent:instrumentation:okhttp-3') // used in the tests
|
|
||||||
testCompile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.6.0'
|
testCompile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.6.0'
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,12 @@ package datadog.trace.instrumentation.jetty8;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
|
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
|
||||||
import static io.opentracing.log.Fields.ERROR_OBJECT;
|
import static io.opentracing.log.Fields.ERROR_OBJECT;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.*;
|
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
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 com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.DDAdvice;
|
import datadog.trace.agent.tooling.DDAdvice;
|
||||||
|
@ -45,8 +50,10 @@ public final class HandlerInstrumentation extends Instrumenter.Configurable {
|
||||||
public AgentBuilder apply(final AgentBuilder agentBuilder) {
|
public AgentBuilder apply(final AgentBuilder agentBuilder) {
|
||||||
return agentBuilder
|
return agentBuilder
|
||||||
.type(
|
.type(
|
||||||
not(isInterface()).and(hasSuperType(named("org.eclipse.jetty.server.Handler"))),
|
not(isInterface())
|
||||||
classLoaderHasClasses("javax.servlet.AsyncEvent", "javax.servlet.AsyncListener"))
|
.and(hasSuperType(named("org.eclipse.jetty.server.Handler")))
|
||||||
|
.and(not(named("org.eclipse.jetty.server.handler.HandlerWrapper"))),
|
||||||
|
not(classLoaderHasClasses("org.eclipse.jetty.server.AsyncContext")))
|
||||||
.transform(
|
.transform(
|
||||||
new HelperInjector(
|
new HelperInjector(
|
||||||
"io.opentracing.contrib.web.servlet.filter.HttpServletRequestExtractAdapter",
|
"io.opentracing.contrib.web.servlet.filter.HttpServletRequestExtractAdapter",
|
||||||
|
@ -60,10 +67,10 @@ public final class HandlerInstrumentation extends Instrumenter.Configurable {
|
||||||
DDAdvice.create()
|
DDAdvice.create()
|
||||||
.advice(
|
.advice(
|
||||||
named("handle")
|
named("handle")
|
||||||
.and(takesArgument(0, named("String")))
|
.and(takesArgument(0, named("java.lang.String")))
|
||||||
.and(takesArgument(1, named("org.eclipse.jetty.server.Request")))
|
.and(takesArgument(1, named("org.eclipse.jetty.server.Request")))
|
||||||
.and(takesArgument(2, named("javax.servlet.HttpServletRequest")))
|
.and(takesArgument(2, named("javax.servlet.http.HttpServletRequest")))
|
||||||
.and(takesArgument(3, named("javax.servlet.HttpServletResponse")))
|
.and(takesArgument(3, named("javax.servlet.http.HttpServletResponse")))
|
||||||
.and(isPublic()),
|
.and(isPublic()),
|
||||||
HandlerInstrumentationAdvice.class.getName()))
|
HandlerInstrumentationAdvice.class.getName()))
|
||||||
.asDecorator();
|
.asDecorator();
|
||||||
|
@ -73,7 +80,8 @@ public final class HandlerInstrumentation extends Instrumenter.Configurable {
|
||||||
|
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static Scope startSpan(
|
public static Scope startSpan(
|
||||||
@Advice.Argument(0) final String target, @Advice.Argument(2) final HttpServletRequest req) {
|
@Advice.This final Object source, @Advice.Argument(2) final HttpServletRequest req) {
|
||||||
|
|
||||||
if (GlobalTracer.get().activeSpan() != null) {
|
if (GlobalTracer.get().activeSpan() != null) {
|
||||||
// Tracing might already be applied. If so ignore this.
|
// Tracing might already be applied. If so ignore this.
|
||||||
return null;
|
return null;
|
||||||
|
@ -82,18 +90,19 @@ public final class HandlerInstrumentation extends Instrumenter.Configurable {
|
||||||
final SpanContext extractedContext =
|
final SpanContext extractedContext =
|
||||||
GlobalTracer.get()
|
GlobalTracer.get()
|
||||||
.extract(Format.Builtin.HTTP_HEADERS, new HttpServletRequestExtractAdapter(req));
|
.extract(Format.Builtin.HTTP_HEADERS, new HttpServletRequestExtractAdapter(req));
|
||||||
final String resourceName = req.getMethod() + target;
|
final String resourceName = req.getMethod() + " " + source.getClass().getName();
|
||||||
final Scope scope =
|
final Scope scope =
|
||||||
GlobalTracer.get()
|
GlobalTracer.get()
|
||||||
.buildSpan(SERVLET_OPERATION_NAME)
|
.buildSpan(SERVLET_OPERATION_NAME)
|
||||||
.asChildOf(extractedContext)
|
.asChildOf(extractedContext)
|
||||||
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
|
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
|
||||||
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.WEB_SERVLET)
|
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.WEB_SERVLET)
|
||||||
.withTag("span.origin.type", HandlerInstrumentationAdvice.class.getName())
|
.withTag("span.origin.type", source.getClass().getName())
|
||||||
.withTag(DDTags.RESOURCE_NAME, resourceName)
|
|
||||||
.startActive(false);
|
.startActive(false);
|
||||||
|
|
||||||
ServletFilterSpanDecorator.STANDARD_TAGS.onRequest(req, scope.span());
|
ServletFilterSpanDecorator.STANDARD_TAGS.onRequest(req, scope.span());
|
||||||
|
Tags.COMPONENT.set(scope.span(), "jetty-handler");
|
||||||
|
scope.span().setTag(DDTags.RESOURCE_NAME, resourceName);
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +112,7 @@ public final class HandlerInstrumentation extends Instrumenter.Configurable {
|
||||||
@Advice.Argument(3) final HttpServletResponse resp,
|
@Advice.Argument(3) final HttpServletResponse resp,
|
||||||
@Advice.Enter final Scope scope,
|
@Advice.Enter final Scope scope,
|
||||||
@Advice.Thrown final Throwable throwable) {
|
@Advice.Thrown final Throwable throwable) {
|
||||||
|
|
||||||
if (scope != null) {
|
if (scope != null) {
|
||||||
final Span span = scope.span();
|
final Span span = scope.span();
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
import datadog.trace.agent.test.AgentTestRunner
|
||||||
|
import datadog.trace.agent.test.TestUtils
|
||||||
|
import datadog.trace.api.DDSpanTypes
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import org.eclipse.jetty.server.Handler
|
||||||
|
import org.eclipse.jetty.server.Request
|
||||||
|
import org.eclipse.jetty.server.Server
|
||||||
|
import org.eclipse.jetty.server.handler.AbstractHandler
|
||||||
|
|
||||||
|
import javax.servlet.ServletException
|
||||||
|
import javax.servlet.http.HttpServletRequest
|
||||||
|
import javax.servlet.http.HttpServletResponse
|
||||||
|
|
||||||
|
class JettyHandlerTest extends AgentTestRunner {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.setProperty("dd.integration.jetty.enabled", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
int port = TestUtils.randomOpenPort()
|
||||||
|
Server server = new Server(port)
|
||||||
|
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
|
// Uncomment when debugging:
|
||||||
|
// .connectTimeout(1, TimeUnit.HOURS)
|
||||||
|
// .writeTimeout(1, TimeUnit.HOURS)
|
||||||
|
// .readTimeout(1, TimeUnit.HOURS)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
def cleanup() {
|
||||||
|
server.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void afterTest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
def "call to jetty creates a trace"() {
|
||||||
|
setup:
|
||||||
|
Handler handler = new AbstractHandler() {
|
||||||
|
@Override
|
||||||
|
void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||||
|
response.setContentType("text/plain;charset=utf-8")
|
||||||
|
response.setStatus(HttpServletResponse.SC_OK)
|
||||||
|
baseRequest.setHandled(true)
|
||||||
|
response.getWriter().println("Hello World")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server.setHandler(handler)
|
||||||
|
server.start()
|
||||||
|
def request = new okhttp3.Request.Builder()
|
||||||
|
.url("http://localhost:$port/")
|
||||||
|
.get()
|
||||||
|
.build()
|
||||||
|
def response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
response.body().string().trim() == "Hello World"
|
||||||
|
TEST_WRITER.waitForTraces(1)
|
||||||
|
TEST_WRITER.size() == 1
|
||||||
|
def trace = TEST_WRITER.firstTrace()
|
||||||
|
trace.size() == 1
|
||||||
|
def context = trace[0].context()
|
||||||
|
context.serviceName == "unnamed-java-app"
|
||||||
|
context.operationName == "jetty.request"
|
||||||
|
context.resourceName == "GET ${handler.class.name}"
|
||||||
|
context.spanType == DDSpanTypes.WEB_SERVLET
|
||||||
|
!context.getErrorFlag()
|
||||||
|
context.parentId == 0
|
||||||
|
def tags = context.tags
|
||||||
|
tags["http.url"] == "http://localhost:$port/"
|
||||||
|
tags["http.method"] == "GET"
|
||||||
|
tags["span.kind"] == "server"
|
||||||
|
tags["span.type"] == "web"
|
||||||
|
tags["component"] == "jetty-handler"
|
||||||
|
tags["http.status_code"] == 200
|
||||||
|
tags["thread.name"] != null
|
||||||
|
tags["thread.id"] != null
|
||||||
|
tags["span.origin.type"] == handler.class.name
|
||||||
|
tags.size() == 9
|
||||||
|
}
|
||||||
|
|
||||||
|
def "call to jetty with error creates a trace"() {
|
||||||
|
setup:
|
||||||
|
Handler handler = new AbstractHandler() {
|
||||||
|
@Override
|
||||||
|
void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||||
|
throw new RuntimeException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server.setHandler(handler)
|
||||||
|
server.start()
|
||||||
|
def request = new okhttp3.Request.Builder()
|
||||||
|
.url("http://localhost:$port/")
|
||||||
|
.get()
|
||||||
|
.build()
|
||||||
|
def response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
response.body().string().trim() == ""
|
||||||
|
TEST_WRITER.waitForTraces(1)
|
||||||
|
TEST_WRITER.size() == 1
|
||||||
|
def trace = TEST_WRITER.firstTrace()
|
||||||
|
trace.size() == 1
|
||||||
|
def context = trace[0].context()
|
||||||
|
context.serviceName == "unnamed-java-app"
|
||||||
|
context.operationName == "jetty.request"
|
||||||
|
context.resourceName == "GET ${handler.class.name}"
|
||||||
|
context.spanType == DDSpanTypes.WEB_SERVLET
|
||||||
|
context.getErrorFlag()
|
||||||
|
context.parentId == 0
|
||||||
|
def tags = context.tags
|
||||||
|
tags["http.url"] == "http://localhost:$port/"
|
||||||
|
tags["http.method"] == "GET"
|
||||||
|
tags["span.kind"] == "server"
|
||||||
|
tags["span.type"] == "web"
|
||||||
|
tags["component"] == "jetty-handler"
|
||||||
|
tags["http.status_code"] == 500
|
||||||
|
tags["thread.name"] != null
|
||||||
|
tags["thread.id"] != null
|
||||||
|
tags["span.origin.type"] == handler.class.name
|
||||||
|
tags["error"] == true
|
||||||
|
tags["error.type"] == RuntimeException.name
|
||||||
|
tags["error.stack"] != null
|
||||||
|
tags.size() == 12
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ dependencies {
|
||||||
compile deps.autoservice
|
compile deps.autoservice
|
||||||
|
|
||||||
testCompile project(':dd-java-agent:testing')
|
testCompile project(':dd-java-agent:testing')
|
||||||
|
testCompile project(':dd-java-agent:instrumentation:jetty-8') // See if there's any conflicts.
|
||||||
testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version: '8.2.0.v20160908'
|
testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version: '8.2.0.v20160908'
|
||||||
testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '8.2.0.v20160908'
|
testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '8.2.0.v20160908'
|
||||||
testCompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.0.41'
|
testCompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.0.41'
|
||||||
|
|
|
@ -16,7 +16,7 @@ import spock.lang.Unroll
|
||||||
|
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
@Timeout(5)
|
@Timeout(15)
|
||||||
class TomcatServletTest extends AgentTestRunner {
|
class TomcatServletTest extends AgentTestRunner {
|
||||||
|
|
||||||
static final int PORT = TestUtils.randomOpenPort()
|
static final int PORT = TestUtils.randomOpenPort()
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
package datadog.trace.instrumentation.sparkjava;
|
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.*;
|
|
||||||
|
|
||||||
import datadog.trace.agent.tooling.DDAdvice;
|
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
|
||||||
import datadog.trace.api.DDTags;
|
|
||||||
import io.opentracing.Scope;
|
|
||||||
import io.opentracing.Span;
|
|
||||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
|
||||||
import net.bytebuddy.asm.Advice;
|
|
||||||
import spark.route.HttpMethod;
|
|
||||||
import spark.routematch.RouteMatch;
|
|
||||||
|
|
||||||
public class RoutesInstrumentation extends Instrumenter.Configurable {
|
|
||||||
|
|
||||||
public RoutesInstrumentation() {
|
|
||||||
super("sparkjava", "sparkjava-2.3");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean defaultEnabled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentBuilder apply(final AgentBuilder agentBuilder) {
|
|
||||||
return agentBuilder
|
|
||||||
.type(
|
|
||||||
is(named("spark.route.Routes")),
|
|
||||||
classLoaderHasClasses("spark.embeddedserver.jetty.EmbeddedJettyServer"))
|
|
||||||
.transform(
|
|
||||||
DDAdvice.create()
|
|
||||||
.advice(
|
|
||||||
named("find")
|
|
||||||
.and(takesArgument(0, named("spark.route.HttpMethod")))
|
|
||||||
.and(takesArgument(1, named("String")))
|
|
||||||
.and(takesArgument(2, named("String")))
|
|
||||||
.and(isPublic()),
|
|
||||||
RoutesInstrumentationAdvice.class.getName()))
|
|
||||||
.asDecorator();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class RoutesInstrumentationAdvice {
|
|
||||||
|
|
||||||
@Advice.OnMethodExit()
|
|
||||||
public static void routeMatchEnricher(
|
|
||||||
@Advice.Argument(0) final HttpMethod method,
|
|
||||||
@Advice.Enter final Scope scope,
|
|
||||||
@Advice.Return final RouteMatch routeMatch) {
|
|
||||||
if (scope != null && routeMatch != null) {
|
|
||||||
final Span span = scope.span();
|
|
||||||
final String resourceName = method.name() + " " + routeMatch.getMatchUri();
|
|
||||||
span.setTag(DDTags.RESOURCE_NAME, resourceName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +1,34 @@
|
||||||
apply plugin: 'version-scan'
|
apply plugin: 'version-scan'
|
||||||
|
|
||||||
versionScan {
|
versionScan {
|
||||||
group = "spark"
|
group = "com.sparkjava"
|
||||||
module = 'spark'
|
module = 'spark-core'
|
||||||
versions = "[2.3,)"
|
versions = "[2.4,)"
|
||||||
verifyPresent = [
|
verifyPresent = [
|
||||||
"spark.embeddedserver.jetty.EmbeddedJettyServer" : null
|
"spark.route.Routes": null
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "${rootDir}/gradle/java.gradle"
|
apply from: "${rootDir}/gradle/java.gradle"
|
||||||
|
|
||||||
if (!JavaVersion.current().isJava8Compatible()) {
|
testJava8Minimum += '**/SparkJavaBasedTest.class'
|
||||||
sourceSets {
|
|
||||||
test {
|
compileTestJava {
|
||||||
groovy {
|
sourceCompatibility = "1.8"
|
||||||
// Sparkjava is not compatible with < Java 8
|
targetCompatibility = "1.8"
|
||||||
exclude '**/SparkJavaBasedTest.groovy'
|
|
||||||
}
|
|
||||||
java {
|
|
||||||
exclude '**/TestSparkJavaApplication.java'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
sourceCompatibility = "1.8"
|
compileOnly group: 'com.sparkjava', name: 'spark-core', version: '2.4'
|
||||||
targetCompatibility = "1.8"
|
|
||||||
compile project(':dd-java-agent:agent-tooling')
|
|
||||||
|
|
||||||
|
compile project(':dd-java-agent:agent-tooling')
|
||||||
compile deps.bytebuddy
|
compile deps.bytebuddy
|
||||||
compile deps.opentracing
|
compile deps.opentracing
|
||||||
compile deps.autoservice
|
compile deps.autoservice
|
||||||
compile group: 'com.sparkjava', name: 'spark-core', version: '2.3'
|
|
||||||
|
|
||||||
testCompile project(':dd-java-agent:instrumentation:jetty-8')
|
testCompile project(':dd-java-agent:instrumentation:jetty-8')
|
||||||
testCompile project(':dd-java-agent:testing')
|
testCompile project(':dd-java-agent:testing')
|
||||||
|
|
||||||
|
testCompile group: 'com.sparkjava', name: 'spark-core', version: '2.4'
|
||||||
testCompile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.6.0'
|
testCompile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.6.0'
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package datadog.trace.instrumentation.sparkjava;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import datadog.trace.agent.tooling.DDAdvice;
|
||||||
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
import datadog.trace.api.DDTags;
|
||||||
|
import io.opentracing.Scope;
|
||||||
|
import io.opentracing.util.GlobalTracer;
|
||||||
|
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import spark.route.HttpMethod;
|
||||||
|
import spark.routematch.RouteMatch;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class RoutesInstrumentation extends Instrumenter.Configurable {
|
||||||
|
|
||||||
|
public RoutesInstrumentation() {
|
||||||
|
super("sparkjava", "sparkjava-2.4");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean defaultEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AgentBuilder apply(final AgentBuilder agentBuilder) {
|
||||||
|
return agentBuilder
|
||||||
|
.type(named("spark.route.Routes"))
|
||||||
|
.transform(
|
||||||
|
DDAdvice.create()
|
||||||
|
.advice(
|
||||||
|
named("find")
|
||||||
|
.and(takesArgument(0, named("spark.route.HttpMethod")))
|
||||||
|
.and(returns(named("spark.routematch.RouteMatch")))
|
||||||
|
.and(isPublic()),
|
||||||
|
RoutesAdvice.class.getName()))
|
||||||
|
.asDecorator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RoutesAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void routeMatchEnricher(
|
||||||
|
@Advice.Argument(0) final HttpMethod method, @Advice.Return final RouteMatch routeMatch) {
|
||||||
|
|
||||||
|
final Scope scope = GlobalTracer.get().scopeManager().active();
|
||||||
|
if (scope != null && routeMatch != null) {
|
||||||
|
final String resourceName = method.name().toUpperCase() + " " + routeMatch.getMatchUri();
|
||||||
|
scope.span().setTag(DDTags.RESOURCE_NAME, resourceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
import datadog.trace.agent.test.AgentTestRunner
|
import datadog.trace.agent.test.AgentTestRunner
|
||||||
|
import datadog.trace.agent.test.TestUtils
|
||||||
import datadog.trace.api.DDSpanTypes
|
import datadog.trace.api.DDSpanTypes
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import spark.Spark
|
import spark.Spark
|
||||||
|
import spark.embeddedserver.jetty.JettyHandler
|
||||||
import spock.lang.Timeout
|
import spock.lang.Timeout
|
||||||
|
|
||||||
|
|
||||||
@Timeout(20)
|
@Timeout(20)
|
||||||
class SparkJavaBasedTest extends AgentTestRunner {
|
class SparkJavaBasedTest extends AgentTestRunner {
|
||||||
|
|
||||||
|
@ -13,45 +14,46 @@ class SparkJavaBasedTest extends AgentTestRunner {
|
||||||
System.setProperty("dd.integration.jetty.enabled", "true")
|
System.setProperty("dd.integration.jetty.enabled", "true")
|
||||||
System.setProperty("dd.integration.sparkjava.enabled", "true")
|
System.setProperty("dd.integration.sparkjava.enabled", "true")
|
||||||
}
|
}
|
||||||
|
static final int PORT = TestUtils.randomOpenPort()
|
||||||
|
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
|
// Uncomment when debugging:
|
||||||
|
// .connectTimeout(1, TimeUnit.HOURS)
|
||||||
|
// .writeTimeout(1, TimeUnit.HOURS)
|
||||||
|
// .readTimeout(1, TimeUnit.HOURS)
|
||||||
|
.build()
|
||||||
|
|
||||||
def setupSpec() {
|
def setupSpec() {
|
||||||
TestSparkJavaApplication.initSpark()
|
TestSparkJavaApplication.initSpark(PORT)
|
||||||
}
|
}
|
||||||
|
|
||||||
def cleanupSpec() {
|
def cleanupSpec() {
|
||||||
Spark.stop()
|
Spark.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
def setup() {
|
|
||||||
TEST_WRITER.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
private int port = 4567
|
|
||||||
OkHttpClient client = new OkHttpClient.Builder().build()
|
|
||||||
|
|
||||||
def "valid response"() {
|
def "valid response"() {
|
||||||
setup:
|
setup:
|
||||||
def request = new Request.Builder()
|
def request = new Request.Builder()
|
||||||
.url("http://localhost:$port/")
|
.url("http://localhost:$PORT/")
|
||||||
.get()
|
.get()
|
||||||
.build()
|
.build()
|
||||||
def response = client.newCall(request).execute()
|
def response = client.newCall(request).execute()
|
||||||
|
|
||||||
expect:
|
expect:
|
||||||
port != 0
|
PORT != 0
|
||||||
response.body().string() == "Hello World"
|
response.body().string() == "Hello World"
|
||||||
}
|
}
|
||||||
|
|
||||||
def "valid response with registered trace"() {
|
def "valid response with registered trace"() {
|
||||||
setup:
|
setup:
|
||||||
def request = new Request.Builder()
|
def request = new Request.Builder()
|
||||||
.url("http://localhost:$port/")
|
.url("http://localhost:$PORT/")
|
||||||
.get()
|
.get()
|
||||||
.build()
|
.build()
|
||||||
def response = client.newCall(request).execute()
|
def response = client.newCall(request).execute()
|
||||||
|
|
||||||
expect:
|
expect:
|
||||||
port != 0
|
PORT != 0
|
||||||
response.body().string() == "Hello World"
|
response.body().string() == "Hello World"
|
||||||
|
|
||||||
and:
|
and:
|
||||||
|
@ -63,7 +65,7 @@ class SparkJavaBasedTest extends AgentTestRunner {
|
||||||
def "generates spans"() {
|
def "generates spans"() {
|
||||||
setup:
|
setup:
|
||||||
def request = new Request.Builder()
|
def request = new Request.Builder()
|
||||||
.url("http://localhost:$port/param/asdf1234")
|
.url("http://localhost:$PORT/param/asdf1234")
|
||||||
.get()
|
.get()
|
||||||
.build()
|
.build()
|
||||||
def response = client.newCall(request).execute()
|
def response = client.newCall(request).execute()
|
||||||
|
@ -75,22 +77,24 @@ class SparkJavaBasedTest extends AgentTestRunner {
|
||||||
|
|
||||||
def trace = TEST_WRITER.firstTrace()
|
def trace = TEST_WRITER.firstTrace()
|
||||||
trace.size() == 1
|
trace.size() == 1
|
||||||
def spanContext = trace[0].context()
|
def context = trace[0].context()
|
||||||
|
context.serviceName == "unnamed-java-app"
|
||||||
spanContext.operationName == "jetty.request"
|
context.operationName == "jetty.request"
|
||||||
spanContext.resourceName == "GET /param/:param/"
|
context.resourceName == "GET /param/:param"
|
||||||
spanContext.spanType == DDSpanTypes.WEB_SERVLET
|
context.spanType == DDSpanTypes.WEB_SERVLET
|
||||||
!spanContext.getErrorFlag()
|
!context.getErrorFlag()
|
||||||
spanContext.parentId == 0
|
context.parentId == 0
|
||||||
spanContext.tags["http.url"] == "http://localhost:$port/param/asdf1234/"
|
def tags = context.tags
|
||||||
spanContext.tags["http.method"] == "GET"
|
tags["http.url"] == "http://localhost:$PORT/param/asdf1234"
|
||||||
spanContext.tags["span.kind"] == "server"
|
tags["http.method"] == "GET"
|
||||||
spanContext.tags["span.type"] == "web"
|
tags["span.kind"] == "server"
|
||||||
spanContext.tags["component"] == "java-web-servlet"
|
tags["span.type"] == "web"
|
||||||
spanContext.tags["http.status_code"] == 200
|
tags["component"] == "jetty-handler"
|
||||||
spanContext.tags["thread.name"] != null
|
tags["http.status_code"] == 200
|
||||||
spanContext.tags["thread.id"] != null
|
tags["thread.name"] != null
|
||||||
spanContext.tags.size() == 8
|
tags["thread.id"] != null
|
||||||
|
tags["span.origin.type"] == JettyHandler.name
|
||||||
|
tags.size() == 9
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,8 +2,8 @@ import spark.Spark;
|
||||||
|
|
||||||
public class TestSparkJavaApplication {
|
public class TestSparkJavaApplication {
|
||||||
|
|
||||||
public static void initSpark() {
|
public static void initSpark(final int port) {
|
||||||
Spark.port(4567);
|
Spark.port(port);
|
||||||
Spark.get("/", (req, res) -> "Hello World");
|
Spark.get("/", (req, res) -> "Hello World");
|
||||||
|
|
||||||
Spark.get("/param/:param", (req, res) -> "Hello " + req.params("param"));
|
Spark.get("/param/:param", (req, res) -> "Hello " + req.params("param"));
|
||||||
|
@ -13,6 +13,7 @@ public class TestSparkJavaApplication {
|
||||||
(req, res) -> {
|
(req, res) -> {
|
||||||
throw new RuntimeException(req.params("param"));
|
throw new RuntimeException(req.params("param"));
|
||||||
});
|
});
|
||||||
|
|
||||||
Spark.awaitInitialization();
|
Spark.awaitInitialization();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@ import spock.lang.Unroll
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
@Timeout(1)
|
@Timeout(10)
|
||||||
class ScopeManagerTest extends Specification {
|
class ScopeManagerTest extends Specification {
|
||||||
def writer = new ListWriter()
|
def writer = new ListWriter()
|
||||||
def tracer = new DDTracer(writer)
|
def tracer = new DDTracer(writer)
|
||||||
|
|
|
@ -35,7 +35,7 @@ include ':dd-java-agent:instrumentation:play-2.4:play-2.6-testing'
|
||||||
include ':dd-java-agent:instrumentation:ratpack-1.4'
|
include ':dd-java-agent:instrumentation:ratpack-1.4'
|
||||||
include ':dd-java-agent:instrumentation:servlet-2'
|
include ':dd-java-agent:instrumentation:servlet-2'
|
||||||
include ':dd-java-agent:instrumentation:servlet-3'
|
include ':dd-java-agent:instrumentation:servlet-3'
|
||||||
include ':dd-java-agent:instrumentation:sparkjava-2.3'
|
include ':dd-java-agent:instrumentation:sparkjava-2.4'
|
||||||
include ':dd-java-agent:instrumentation:spring-web'
|
include ':dd-java-agent:instrumentation:spring-web'
|
||||||
include ':dd-java-agent:instrumentation:trace-annotation'
|
include ':dd-java-agent:instrumentation:trace-annotation'
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue