Merge pull request #1325 from DataDog/devinsba/play-2.3
Play 2.3 support
This commit is contained in:
commit
40cbd19f8e
|
@ -24,6 +24,7 @@ import org.gradle.api.model.ObjectFactory
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.security.SecureClassLoader
|
import java.security.SecureClassLoader
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* muzzle task plugin which runs muzzle validation against a range of dependencies.
|
* muzzle task plugin which runs muzzle validation against a range of dependencies.
|
||||||
|
@ -36,7 +37,8 @@ class MuzzlePlugin implements Plugin<Project> {
|
||||||
private static final AtomicReference<ClassLoader> TOOLING_LOADER = new AtomicReference<>()
|
private static final AtomicReference<ClassLoader> TOOLING_LOADER = new AtomicReference<>()
|
||||||
static {
|
static {
|
||||||
RemoteRepository central = new RemoteRepository.Builder("central", "default", "https://repo1.maven.org/maven2/").build()
|
RemoteRepository central = new RemoteRepository.Builder("central", "default", "https://repo1.maven.org/maven2/").build()
|
||||||
MUZZLE_REPOS = new ArrayList<RemoteRepository>(Arrays.asList(central))
|
RemoteRepository typesafe = new RemoteRepository.Builder("typesafe", "default", "https://repo.typesafe.com/typesafe/releases").build()
|
||||||
|
MUZZLE_REPOS = new ArrayList<RemoteRepository>(Arrays.asList(central, typesafe))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -343,6 +345,8 @@ class MuzzlePlugin implements Plugin<Project> {
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Pattern GIT_SHA_PATTERN = Pattern.compile('^.*-[0-9a-f]{7,}$')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter out snapshot-type builds from versions list.
|
* Filter out snapshot-type builds from versions list.
|
||||||
*/
|
*/
|
||||||
|
@ -357,7 +361,8 @@ class MuzzlePlugin implements Plugin<Project> {
|
||||||
version.contains(".m") ||
|
version.contains(".m") ||
|
||||||
version.contains("-m") ||
|
version.contains("-m") ||
|
||||||
version.contains("-dev") ||
|
version.contains("-dev") ||
|
||||||
version.contains("public_draft")
|
version.contains("public_draft") ||
|
||||||
|
version.matches(GIT_SHA_PATTERN)
|
||||||
}
|
}
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
logs/
|
|
@ -0,0 +1,58 @@
|
||||||
|
ext {
|
||||||
|
minJavaVersionForTests = JavaVersion.VERSION_1_8
|
||||||
|
// Play doesn't work with Java 9+ until 2.6.12
|
||||||
|
maxJavaVersionForTests = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group = 'com.typesafe.play'
|
||||||
|
module = 'play_2.11'
|
||||||
|
versions = '[2.3.0,2.4)'
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
fail {
|
||||||
|
group = 'com.typesafe.play'
|
||||||
|
module = 'play_2.12'
|
||||||
|
versions = '[,]'
|
||||||
|
}
|
||||||
|
fail {
|
||||||
|
group = 'com.typesafe.play'
|
||||||
|
module = 'play_2.13'
|
||||||
|
versions = '[,]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "${rootDir}/gradle/java.gradle"
|
||||||
|
apply from: "${rootDir}/gradle/test-with-scala.gradle"
|
||||||
|
|
||||||
|
apply plugin: 'org.unbroken-dome.test-sets'
|
||||||
|
|
||||||
|
testSets {
|
||||||
|
latestDepTest {
|
||||||
|
dirName = 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
main_java8Compile group: 'com.typesafe.play', name: 'play_2.11', version: '2.3.0'
|
||||||
|
|
||||||
|
testCompile project(':dd-java-agent:instrumentation:netty-3.8')
|
||||||
|
|
||||||
|
testCompile group: 'com.typesafe.play', name: 'play-java_2.11', version: '2.3.0'
|
||||||
|
testCompile group: 'com.typesafe.play', name: 'play-java-ws_2.11', version: '2.3.0'
|
||||||
|
testCompile(group: 'com.typesafe.play', name: 'play-test_2.11', version: '2.3.0') {
|
||||||
|
exclude group: 'org.eclipse.jetty', module: 'jetty-websocket'
|
||||||
|
}
|
||||||
|
|
||||||
|
latestDepTestCompile group: 'com.typesafe.play', name: 'play-java_2.11', version: '2.3.+'
|
||||||
|
latestDepTestCompile group: 'com.typesafe.play', name: 'play-java-ws_2.11', version: '2.3.+'
|
||||||
|
latestDepTestCompile(group: 'com.typesafe.play', name: 'play-test_2.11', version: '2.3.+') {
|
||||||
|
exclude group: 'org.eclipse.jetty', module: 'jetty-websocket'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileLatestDepTestGroovy {
|
||||||
|
classpath = classpath.plus(files(compileLatestDepTestScala.destinationDir))
|
||||||
|
dependsOn compileLatestDepTestScala
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package datadog.trace.instrumentation.play23;
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.ClassLoaderMatcher.hasClassesNamed;
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
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.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 final class PlayInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public PlayInstrumentation() {
|
||||||
|
super("play", "play-action");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<ClassLoader> classLoaderMatcher() {
|
||||||
|
// Optimization for expensive typeMatcher.
|
||||||
|
return hasClassesNamed("play.api.mvc.Action");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return implementsInterface(named("play.api.mvc.Action"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {
|
||||||
|
packageName + ".PlayHttpServerDecorator",
|
||||||
|
packageName + ".RequestCompleteCallback",
|
||||||
|
packageName + ".PlayHeaders",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
named("apply")
|
||||||
|
.and(takesArgument(0, named("play.api.mvc.Request")))
|
||||||
|
.and(returns(named("scala.concurrent.Future"))),
|
||||||
|
packageName + ".PlayAdvice");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package datadog.trace.instrumentation.play23;
|
||||||
|
|
||||||
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
|
||||||
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
|
||||||
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
|
||||||
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
|
import static datadog.trace.instrumentation.play23.PlayHeaders.GETTER;
|
||||||
|
import static datadog.trace.instrumentation.play23.PlayHttpServerDecorator.DECORATE;
|
||||||
|
|
||||||
|
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
|
||||||
|
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
|
||||||
|
import datadog.trace.bootstrap.instrumentation.api.AgentSpan.Context;
|
||||||
|
import datadog.trace.bootstrap.instrumentation.api.Tags;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import play.api.mvc.Action;
|
||||||
|
import play.api.mvc.Request;
|
||||||
|
import play.api.mvc.Result;
|
||||||
|
import scala.concurrent.Future;
|
||||||
|
|
||||||
|
public class PlayAdvice {
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static AgentScope onEnter(@Advice.Argument(0) final Request req) {
|
||||||
|
final AgentSpan span;
|
||||||
|
if (activeSpan() == null) {
|
||||||
|
final Context extractedContext = propagate().extract(req.headers(), GETTER);
|
||||||
|
span = startSpan("play.request", extractedContext);
|
||||||
|
} else {
|
||||||
|
// An upstream framework (e.g. akka-http, netty) has already started the span.
|
||||||
|
// Do not extract the context.
|
||||||
|
span = startSpan("play.request");
|
||||||
|
}
|
||||||
|
DECORATE.afterStart(span);
|
||||||
|
DECORATE.onConnection(span, req);
|
||||||
|
|
||||||
|
final AgentScope scope = activateSpan(span, false);
|
||||||
|
scope.setAsyncPropagation(true);
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void stopTraceOnResponse(
|
||||||
|
@Advice.Enter final AgentScope playControllerScope,
|
||||||
|
@Advice.This final Object thisAction,
|
||||||
|
@Advice.Thrown final Throwable throwable,
|
||||||
|
@Advice.Argument(0) final Request req,
|
||||||
|
@Advice.Return(readOnly = false) final Future<Result> responseFuture) {
|
||||||
|
final AgentSpan playControllerSpan = playControllerScope.span();
|
||||||
|
|
||||||
|
// Call onRequest on return after tags are populated.
|
||||||
|
DECORATE.onRequest(playControllerSpan, req);
|
||||||
|
|
||||||
|
if (throwable == null) {
|
||||||
|
responseFuture.onComplete(
|
||||||
|
new RequestCompleteCallback(playControllerSpan),
|
||||||
|
((Action) thisAction).executionContext());
|
||||||
|
} else {
|
||||||
|
DECORATE.onError(playControllerSpan, throwable);
|
||||||
|
playControllerSpan.setTag(Tags.HTTP_STATUS, 500);
|
||||||
|
DECORATE.beforeFinish(playControllerSpan);
|
||||||
|
playControllerSpan.finish();
|
||||||
|
}
|
||||||
|
playControllerScope.close();
|
||||||
|
|
||||||
|
final AgentSpan rootSpan = activeSpan();
|
||||||
|
// set the resource name on the upstream akka/netty span
|
||||||
|
DECORATE.onRequest(rootSpan, req);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package datadog.trace.instrumentation.play23;
|
||||||
|
|
||||||
|
import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
|
||||||
|
import play.api.mvc.Headers;
|
||||||
|
import scala.Option;
|
||||||
|
import scala.collection.JavaConversions;
|
||||||
|
|
||||||
|
public class PlayHeaders implements AgentPropagation.Getter<Headers> {
|
||||||
|
|
||||||
|
public static final PlayHeaders GETTER = new PlayHeaders();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<String> keys(final Headers headers) {
|
||||||
|
return JavaConversions.asJavaIterable(headers.keys());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(final Headers headers, final String key) {
|
||||||
|
final Option<String> option = headers.get(key);
|
||||||
|
if (option.isDefined()) {
|
||||||
|
return option.get();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package datadog.trace.instrumentation.play23;
|
||||||
|
|
||||||
|
import datadog.trace.api.DDTags;
|
||||||
|
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
|
||||||
|
import datadog.trace.bootstrap.instrumentation.api.Tags;
|
||||||
|
import datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import play.api.mvc.Request;
|
||||||
|
import play.api.mvc.Result;
|
||||||
|
import scala.Option;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class PlayHttpServerDecorator extends HttpServerDecorator<Request, Request, Result> {
|
||||||
|
public static final PlayHttpServerDecorator DECORATE = new PlayHttpServerDecorator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String[] instrumentationNames() {
|
||||||
|
return new String[] {"play"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String component() {
|
||||||
|
return "play-action";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String method(final Request httpRequest) {
|
||||||
|
return httpRequest.method();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected URI url(final Request request) throws URISyntaxException {
|
||||||
|
return new URI((request.secure() ? "https://" : "http://") + request.host() + request.uri());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String peerHostIP(final Request request) {
|
||||||
|
return request.remoteAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer peerPort(final Request request) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer status(final Result httpResponse) {
|
||||||
|
return httpResponse.header().status();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AgentSpan onRequest(final AgentSpan span, final Request request) {
|
||||||
|
super.onRequest(span, request);
|
||||||
|
if (request != null) {
|
||||||
|
// more about routes here:
|
||||||
|
// https://github.com/playframework/playframework/blob/master/documentation/manual/releases/release26/migration26/Migration26.md#router-tags-are-now-attributes
|
||||||
|
final Option pathOption = request.tags().get("ROUTE_PATTERN");
|
||||||
|
if (!pathOption.isEmpty()) {
|
||||||
|
final String path = (String) pathOption.get();
|
||||||
|
span.setTag(DDTags.RESOURCE_NAME, request.method() + " " + path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return span;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AgentSpan onError(final AgentSpan span, Throwable throwable) {
|
||||||
|
span.setTag(Tags.HTTP_STATUS, 500);
|
||||||
|
if (throwable != null
|
||||||
|
// This can be moved to instanceof check when using Java 8.
|
||||||
|
&& throwable.getClass().getName().equals("java.util.concurrent.CompletionException")
|
||||||
|
&& throwable.getCause() != null) {
|
||||||
|
throwable = throwable.getCause();
|
||||||
|
}
|
||||||
|
while ((throwable instanceof InvocationTargetException
|
||||||
|
|| throwable instanceof UndeclaredThrowableException)
|
||||||
|
&& throwable.getCause() != null) {
|
||||||
|
throwable = throwable.getCause();
|
||||||
|
}
|
||||||
|
return super.onError(span, throwable);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package datadog.trace.instrumentation.play23;
|
||||||
|
|
||||||
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope;
|
||||||
|
import static datadog.trace.instrumentation.play23.PlayHttpServerDecorator.DECORATE;
|
||||||
|
|
||||||
|
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
|
||||||
|
import datadog.trace.context.TraceScope;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import play.api.mvc.Result;
|
||||||
|
import scala.util.Try;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class RequestCompleteCallback extends scala.runtime.AbstractFunction1<Try<Result>, Object> {
|
||||||
|
private final AgentSpan span;
|
||||||
|
|
||||||
|
public RequestCompleteCallback(final AgentSpan span) {
|
||||||
|
this.span = span;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object apply(final Try<Result> result) {
|
||||||
|
try {
|
||||||
|
if (result.isFailure()) {
|
||||||
|
DECORATE.onError(span, result.failed().get());
|
||||||
|
} else {
|
||||||
|
DECORATE.onResponse(span, result.get());
|
||||||
|
}
|
||||||
|
DECORATE.beforeFinish(span);
|
||||||
|
final TraceScope scope = activeScope();
|
||||||
|
if (scope != null) {
|
||||||
|
scope.setAsyncPropagation(false);
|
||||||
|
}
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
log.debug("error in play instrumentation", t);
|
||||||
|
} finally {
|
||||||
|
span.finish();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import datadog.trace.agent.test.base.HttpClientTest
|
||||||
|
import datadog.trace.instrumentation.netty38.client.NettyHttpClientDecorator
|
||||||
|
import play.GlobalSettings
|
||||||
|
import play.libs.ws.WS
|
||||||
|
import play.test.FakeApplication
|
||||||
|
import play.test.Helpers
|
||||||
|
import spock.lang.Shared
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class PlayWSClientTest extends HttpClientTest {
|
||||||
|
@Shared
|
||||||
|
def application = new FakeApplication(
|
||||||
|
new File("."),
|
||||||
|
FakeApplication.getClassLoader(),
|
||||||
|
[
|
||||||
|
"ws.timeout.connection": CONNECT_TIMEOUT_MS,
|
||||||
|
"ws.timeout.request" : READ_TIMEOUT_MS
|
||||||
|
],
|
||||||
|
Collections.emptyList(),
|
||||||
|
new GlobalSettings()
|
||||||
|
)
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
def client
|
||||||
|
|
||||||
|
def setupSpec() {
|
||||||
|
Helpers.start(application)
|
||||||
|
client = WS.client()
|
||||||
|
}
|
||||||
|
|
||||||
|
def cleanupSpec() {
|
||||||
|
Helpers.stop(application)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
|
||||||
|
def request = client.url(uri.toString())
|
||||||
|
headers.entrySet().each {
|
||||||
|
request.setHeader(it.key, it.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
def status = request.execute(method).map({
|
||||||
|
callback?.call()
|
||||||
|
it
|
||||||
|
}).map({
|
||||||
|
it.status
|
||||||
|
})
|
||||||
|
return status.get(1, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String component() {
|
||||||
|
return NettyHttpClientDecorator.DECORATE.component()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String expectedOperationName() {
|
||||||
|
return "netty.client.request"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean testRedirects() {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean testConnectionFailure() {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean testRemoteConnection() {
|
||||||
|
// On connection failures the operation and resource names end up different from expected.
|
||||||
|
// This would require a lot of changes to the base client test class to support
|
||||||
|
// span.operationName = "netty.connect"
|
||||||
|
// span.resourceName = "netty.connect"
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package server;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import datadog.trace.agent.test.base.HttpServerTestAdvice;
|
||||||
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class NettyServerTestInstrumentation implements Instrumenter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
|
||||||
|
return agentBuilder
|
||||||
|
.type(named("org.jboss.netty.handler.codec.http.HttpRequestDecoder"))
|
||||||
|
.transform(
|
||||||
|
new AgentBuilder.Transformer.ForAdvice()
|
||||||
|
.advice(
|
||||||
|
named("createMessage"),
|
||||||
|
HttpServerTestAdvice.ServerEntryAdvice.class.getName()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import play.api.test.TestServer
|
||||||
|
|
||||||
|
class PlayAsyncServerTest extends PlayServerTest {
|
||||||
|
@Override
|
||||||
|
TestServer startServer(int port) {
|
||||||
|
def server = AsyncServer.server(port)
|
||||||
|
server.start()
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import datadog.opentracing.DDSpan
|
||||||
|
import datadog.trace.agent.test.asserts.TraceAssert
|
||||||
|
import datadog.trace.agent.test.base.HttpServerTest
|
||||||
|
import datadog.trace.api.DDSpanTypes
|
||||||
|
import datadog.trace.api.DDTags
|
||||||
|
import datadog.trace.bootstrap.instrumentation.api.Tags
|
||||||
|
import datadog.trace.instrumentation.netty38.server.NettyHttpServerDecorator
|
||||||
|
import datadog.trace.instrumentation.play23.PlayHttpServerDecorator
|
||||||
|
import play.api.test.TestServer
|
||||||
|
|
||||||
|
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.*
|
||||||
|
|
||||||
|
class PlayServerTest extends HttpServerTest<TestServer> {
|
||||||
|
@Override
|
||||||
|
TestServer startServer(int port) {
|
||||||
|
def server = SyncServer.server(port)
|
||||||
|
server.start()
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void stopServer(TestServer server) {
|
||||||
|
server.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String component() {
|
||||||
|
return NettyHttpServerDecorator.DECORATE.component()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String expectedOperationName() {
|
||||||
|
return "netty.request"
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't have instrumentation for this version of netty yet
|
||||||
|
@Override
|
||||||
|
boolean hasHandlerSpan() {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
// Return the handler span's name
|
||||||
|
String reorderHandlerSpan() {
|
||||||
|
"play.request"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void handlerSpan(TraceAssert trace, int index, Object parent, ServerEndpoint endpoint = SUCCESS) {
|
||||||
|
trace.span(index) {
|
||||||
|
serviceName expectedServiceName()
|
||||||
|
operationName "play.request"
|
||||||
|
spanType DDSpanTypes.HTTP_SERVER
|
||||||
|
errored endpoint == ERROR || endpoint == EXCEPTION
|
||||||
|
childOf(parent as DDSpan)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT" PlayHttpServerDecorator.DECORATE.component()
|
||||||
|
"$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER
|
||||||
|
"$Tags.PEER_HOST_IPV4" { it == null || it == "127.0.0.1" } // Optional
|
||||||
|
"$Tags.HTTP_URL" String
|
||||||
|
"$Tags.HTTP_METHOD" String
|
||||||
|
"$Tags.HTTP_STATUS" Integer
|
||||||
|
if (endpoint == ERROR) {
|
||||||
|
"$Tags.ERROR" true
|
||||||
|
} else if (endpoint == EXCEPTION) {
|
||||||
|
errorTags(Exception, EXCEPTION.body)
|
||||||
|
}
|
||||||
|
if (endpoint.query) {
|
||||||
|
"$DDTags.HTTP_QUERY" endpoint.query
|
||||||
|
}
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import datadog.trace.agent.test.base.HttpServerTest
|
||||||
|
import datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint._
|
||||||
|
import play.api.mvc.{Action, Handler, Results}
|
||||||
|
import play.api.test.{FakeApplication, TestServer}
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
object AsyncServer {
|
||||||
|
val routes: PartialFunction[(String, String), Handler] = {
|
||||||
|
case ("GET", "/success") => Action.async { request => HttpServerTest.controller(SUCCESS, new AsyncControllerClosureAdapter(Future.successful(Results.Status(SUCCESS.getStatus).apply(SUCCESS.getBody)))) }
|
||||||
|
case ("GET", "/redirect") => Action.async { request => HttpServerTest.controller(REDIRECT, new AsyncControllerClosureAdapter(Future.successful(Results.Redirect(REDIRECT.getBody, REDIRECT.getStatus)))) }
|
||||||
|
case ("GET", "/query") => Action.async { result => HttpServerTest.controller(QUERY_PARAM, new AsyncControllerClosureAdapter(Future.successful(Results.Status(QUERY_PARAM.getStatus).apply(QUERY_PARAM.getBody)))) }
|
||||||
|
case ("GET", "/error-status") => Action.async { result => HttpServerTest.controller(ERROR, new AsyncControllerClosureAdapter(Future.successful(Results.Status(ERROR.getStatus).apply(ERROR.getBody)))) }
|
||||||
|
case ("GET", "/exception") => Action.async { result =>
|
||||||
|
HttpServerTest.controller(EXCEPTION, new AsyncBlockClosureAdapter(() => {
|
||||||
|
throw new Exception(EXCEPTION.getBody)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def server(port: Int): TestServer = {
|
||||||
|
TestServer(port, FakeApplication(withGlobal = Some(new Settings()), withRoutes = routes))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import groovy.lang.Closure
|
||||||
|
import play.api.mvc.Result
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
class ControllerClosureAdapter(response: Result) extends Closure[Result] {
|
||||||
|
override def call(): Result = response
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlockClosureAdapter(block: () => Result) extends Closure[Result] {
|
||||||
|
override def call(): Result = block()
|
||||||
|
}
|
||||||
|
|
||||||
|
class AsyncControllerClosureAdapter(response: Future[Result]) extends Closure[Future[Result]] {
|
||||||
|
override def call(): Future[Result] = response
|
||||||
|
}
|
||||||
|
|
||||||
|
class AsyncBlockClosureAdapter(block: () => Future[Result]) extends Closure[Future[Result]] {
|
||||||
|
override def call(): Future[Result] = block()
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import play.api.GlobalSettings
|
||||||
|
import play.api.mvc.{RequestHeader, Result, Results}
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
class Settings extends GlobalSettings {
|
||||||
|
override def onError(request: RequestHeader, ex: Throwable): Future[Result] = {
|
||||||
|
Future.successful(Results.InternalServerError(ex.getCause.getMessage))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import datadog.trace.agent.test.base.HttpServerTest
|
||||||
|
import datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint._
|
||||||
|
import play.api.mvc.{Action, Handler, Results}
|
||||||
|
import play.api.test.{FakeApplication, TestServer}
|
||||||
|
|
||||||
|
object SyncServer {
|
||||||
|
val routes: PartialFunction[(String, String), Handler] = {
|
||||||
|
case ("GET", "/success") => Action { request =>
|
||||||
|
HttpServerTest.controller(SUCCESS, new ControllerClosureAdapter(Results.Status(SUCCESS.getStatus).apply(SUCCESS.getBody)))
|
||||||
|
}
|
||||||
|
case ("GET", "/redirect") => Action { request =>
|
||||||
|
HttpServerTest.controller(REDIRECT, new ControllerClosureAdapter(Results.Redirect(REDIRECT.getBody, REDIRECT.getStatus)))
|
||||||
|
}
|
||||||
|
case ("GET", "/query") => Action { request =>
|
||||||
|
HttpServerTest.controller(QUERY_PARAM, new ControllerClosureAdapter(Results.Status(QUERY_PARAM.getStatus).apply(QUERY_PARAM.getBody)))
|
||||||
|
}
|
||||||
|
case ("GET", "/error-status") => Action { request =>
|
||||||
|
HttpServerTest.controller(ERROR, new ControllerClosureAdapter(Results.Status(ERROR.getStatus).apply(ERROR.getBody)))
|
||||||
|
}
|
||||||
|
case ("GET", "/exception") => Action { request =>
|
||||||
|
HttpServerTest.controller(EXCEPTION, new BlockClosureAdapter(() => {
|
||||||
|
throw new Exception(EXCEPTION.getBody)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def server(port: Int): TestServer = {
|
||||||
|
TestServer(port, FakeApplication(withGlobal = Some(new Settings()), withRoutes = routes))
|
||||||
|
}
|
||||||
|
}
|
|
@ -131,6 +131,9 @@ repositories {
|
||||||
maven {
|
maven {
|
||||||
url "https://adoptopenjdk.jfrog.io/adoptopenjdk/jmc-libs-snapshots"
|
url "https://adoptopenjdk.jfrog.io/adoptopenjdk/jmc-libs-snapshots"
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
url "https://repo.typesafe.com/typesafe/releases"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
|
@ -116,6 +116,7 @@ include ':dd-java-agent:instrumentation:netty-3.8'
|
||||||
include ':dd-java-agent:instrumentation:netty-4.0'
|
include ':dd-java-agent:instrumentation:netty-4.0'
|
||||||
include ':dd-java-agent:instrumentation:netty-4.1'
|
include ':dd-java-agent:instrumentation:netty-4.1'
|
||||||
include ':dd-java-agent:instrumentation:okhttp-3'
|
include ':dd-java-agent:instrumentation:okhttp-3'
|
||||||
|
include ':dd-java-agent:instrumentation:play-2.3'
|
||||||
include ':dd-java-agent:instrumentation:play-2.4'
|
include ':dd-java-agent:instrumentation:play-2.4'
|
||||||
include ':dd-java-agent:instrumentation:play-2.6'
|
include ':dd-java-agent:instrumentation:play-2.6'
|
||||||
include ':dd-java-agent:instrumentation:play-ws'
|
include ':dd-java-agent:instrumentation:play-ws'
|
||||||
|
|
Loading…
Reference in New Issue