Improve Ratpack context propagation and migrate tests
This commit is contained in:
parent
473aca7c08
commit
8c490a42a2
|
@ -26,6 +26,9 @@ public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapte
|
|||
ctx.fireChannelRead(msg); // superclass does not throw
|
||||
} else {
|
||||
try (final Scope scope = tracer.scopeManager().activate(span, false)) {
|
||||
if (scope instanceof TraceScope) {
|
||||
((TraceScope) scope).setAsyncPropagation(true);
|
||||
}
|
||||
ctx.fireChannelRead(msg); // superclass does not throw
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,6 +103,7 @@ class Netty40ServerTest extends HttpServerTest<EventLoopGroup, NettyHttpServerDe
|
|||
NettyHttpServerDecorator.DECORATE
|
||||
}
|
||||
|
||||
@Override
|
||||
String expectedOperationName() {
|
||||
"netty.request"
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapte
|
|||
ctx.fireChannelRead(msg); // superclass does not throw
|
||||
} else {
|
||||
try (final Scope scope = tracer.scopeManager().activate(span, false)) {
|
||||
if (scope instanceof TraceScope) {
|
||||
((TraceScope) scope).setAsyncPropagation(true);
|
||||
}
|
||||
ctx.fireChannelRead(msg); // superclass does not throw
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,6 +102,7 @@ class Netty41ServerTest extends HttpServerTest<EventLoopGroup, NettyHttpServerDe
|
|||
NettyHttpServerDecorator.DECORATE
|
||||
}
|
||||
|
||||
@Override
|
||||
String expectedOperationName() {
|
||||
"netty.request"
|
||||
}
|
||||
|
|
|
@ -45,7 +45,9 @@ dependencies {
|
|||
apply plugin: 'org.unbroken-dome.test-sets'
|
||||
|
||||
testSets {
|
||||
latestDepTest
|
||||
latestDepTest {
|
||||
dirName = 'test'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
@ -1,695 +0,0 @@
|
|||
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 bindings for #path"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
prefix("a") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("b/::\\d+") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("c/:val?") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("d/:val") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("e/:val?:\\d+") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("f/:val:\\d+") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(HttpUrl.get(app.address).newBuilder().addPathSegments(path).build())
|
||||
.get()
|
||||
.build()
|
||||
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
|
||||
then:
|
||||
resp.code() == 200
|
||||
resp.body.string() == route
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /$route"
|
||||
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.resolve(path)}"
|
||||
"$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 /$route"
|
||||
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" "${app.address.resolve(path)}"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
path | route
|
||||
"a" | "a"
|
||||
"b/123" | "b/::\\d+"
|
||||
"c" | "c/:val?"
|
||||
"c/123" | "c/:val?"
|
||||
"c/foo" | "c/:val?"
|
||||
"d/123" | "d/:val"
|
||||
"d/foo" | "d/:val"
|
||||
"e" | "e/:val?:\\d+"
|
||||
"e/123" | "e/:val?:\\d+"
|
||||
"e/foo" | "e/:val?:\\d+"
|
||||
"f/123" | "f/:val:\\d+"
|
||||
}
|
||||
|
||||
def "test handler error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
prefix("handler-error") {
|
||||
all {
|
||||
0 / 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.resolve("/handler-error?query=param").toURL())
|
||||
.get()
|
||||
.build()
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
then:
|
||||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /handler-error"
|
||||
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.resolve('handler-error')}"
|
||||
"$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 /handler-error"
|
||||
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" "${app.address.resolve('handler-error')}"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
errorTags(HandlerException, Pattern.compile("java.lang.ArithmeticException: Division( is)? undefined"))
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test promise error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get("promise-error") {
|
||||
Promise.async {
|
||||
0 / 0
|
||||
}.then {
|
||||
context.render(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.resolve("promise-error?query=param").toURL())
|
||||
.get()
|
||||
.build()
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
then:
|
||||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /promise-error"
|
||||
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.resolve('promise-error')}"
|
||||
"$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 /promise-error"
|
||||
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" "${app.address.resolve('promise-error')}"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
"$Tags.ERROR.key" true
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test render error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
all {
|
||||
context.render(Promise.sync {
|
||||
return "fail " + 0 / 0
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.resolve("?query=param").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" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
"$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" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
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(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}/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" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
"$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" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
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,97 @@
|
|||
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.named;
|
||||
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.Span;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
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.func.Block;
|
||||
import ratpack.path.PathBinding;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class ContinuationInstrumentation extends Instrumenter.Default {
|
||||
|
||||
public ContinuationInstrumentation() {
|
||||
super("ratpack");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return safeHasSuperType(named("ratpack.exec.internal.Continuation"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
getClass().getName() + "$ResumeAdvice$BlockWrapper",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
named("resume").and(takesArgument(0, named("ratpack.func.Block"))),
|
||||
ResumeAdvice.class.getName());
|
||||
}
|
||||
|
||||
public static class ResumeAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void wrap(@Advice.Argument(value = 0, readOnly = false) Block block) {
|
||||
final Span span = GlobalTracer.get().activeSpan();
|
||||
if (span != null) {
|
||||
block = BlockWrapper.wrapIfNeeded(block, span);
|
||||
}
|
||||
}
|
||||
|
||||
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 BlockWrapper<T> implements Block {
|
||||
private final Block delegate;
|
||||
private final Span span;
|
||||
|
||||
private BlockWrapper(final Block delegate, final Span span) {
|
||||
this.delegate = delegate;
|
||||
this.span = span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
if (span != null) {
|
||||
try (final Scope scope = GlobalTracer.get().scopeManager().activate(span, false)) {
|
||||
if (scope instanceof TraceScope) {
|
||||
((TraceScope) scope).setAsyncPropagation(true);
|
||||
}
|
||||
delegate.execute();
|
||||
}
|
||||
} else {
|
||||
delegate.execute();
|
||||
}
|
||||
}
|
||||
|
||||
public static Block wrapIfNeeded(final Block delegate, final Span span) {
|
||||
if (delegate instanceof BlockWrapper || span == null) {
|
||||
return delegate;
|
||||
}
|
||||
log.debug("Wrapping action task {}", delegate);
|
||||
return new BlockWrapper(delegate, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package datadog.trace.instrumentation.ratpack;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import datadog.trace.context.TraceScope;
|
||||
import io.opentracing.Scope;
|
||||
import io.opentracing.Span;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
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 DefaultExecutionInstrumentation extends Instrumenter.Default {
|
||||
|
||||
public DefaultExecutionInstrumentation() {
|
||||
super("ratpack");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return named("ratpack.exec.internal.DefaultExecution");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
getClass().getName() + "$DelimitAdvice$ActionWrapper",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
nameStartsWith("delimit") // include delimitStream
|
||||
.and(takesArgument(0, named("ratpack.func.Action")))
|
||||
.and(takesArgument(1, named("ratpack.func.Action"))),
|
||||
DelimitAdvice.class.getName());
|
||||
}
|
||||
|
||||
public static class DelimitAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void wrap(
|
||||
@Advice.Argument(value = 0, readOnly = false) Action<? super Throwable> onError,
|
||||
@Advice.Argument(value = 1, readOnly = false) Action<? super Continuation> segment) {
|
||||
final Span span = GlobalTracer.get().activeSpan();
|
||||
if (span != null) {
|
||||
/**
|
||||
* Here we pass along the span instead of a continuation because we aren't sure it won't be
|
||||
* used for both callbacks.
|
||||
*/
|
||||
onError = ActionWrapper.wrapIfNeeded(onError, span);
|
||||
segment = ActionWrapper.wrapIfNeeded(segment, span);
|
||||
}
|
||||
}
|
||||
|
||||
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 Span span;
|
||||
|
||||
private ActionWrapper(final Action<T> delegate, final Span span) {
|
||||
this.delegate = delegate;
|
||||
this.span = span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final T t) throws Exception {
|
||||
if (span != null) {
|
||||
try (final Scope scope = GlobalTracer.get().scopeManager().activate(span, false)) {
|
||||
if (scope instanceof TraceScope) {
|
||||
((TraceScope) scope).setAsyncPropagation(true);
|
||||
}
|
||||
delegate.execute(t);
|
||||
}
|
||||
} else {
|
||||
delegate.execute(t);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Action<T> wrapIfNeeded(final Action<T> delegate, final Span span) {
|
||||
if (delegate instanceof ActionWrapper || span == null) {
|
||||
return delegate;
|
||||
}
|
||||
log.debug("Wrapping action task {}", delegate);
|
||||
return new ActionWrapper(delegate, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,108 +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.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,8 +1,8 @@
|
|||
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.isAbstract;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
|
@ -10,10 +10,7 @@ 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;
|
||||
|
@ -27,8 +24,8 @@ public class ServerErrorHandlerInstrumentation extends Instrumenter.Default {
|
|||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return named("ratpack.exec.Execution")
|
||||
.or(not(isInterface()).and(safeHasSuperType(named("ratpack.error.ServerErrorHandler"))));
|
||||
return not(isInterface().or(isAbstract()))
|
||||
.and(safeHasSuperType(named("ratpack.error.ServerErrorHandler")));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -44,16 +41,9 @@ public class ServerErrorHandlerInstrumentation extends Instrumenter.Default {
|
|||
@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);
|
||||
}
|
||||
}
|
||||
named("error")
|
||||
.and(takesArgument(0, named("ratpack.handling.Context")))
|
||||
.and(takesArgument(1, Throwable.class)),
|
||||
packageName + ".ErrorHandlerAdvice");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package datadog.trace.instrumentation.ratpack;
|
||||
|
||||
import static datadog.trace.instrumentation.ratpack.RatpackServerDecorator.DECORATE;
|
||||
|
||||
import io.opentracing.Span;
|
||||
import java.util.Optional;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import ratpack.handling.Context;
|
||||
|
||||
public class ErrorHandlerAdvice {
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void captureThrowable(
|
||||
@Advice.Argument(0) final Context ctx, @Advice.Argument(1) final Throwable throwable) {
|
||||
final Optional<Span> span = ctx.maybeGet(Span.class);
|
||||
if (span.isPresent()) {
|
||||
DECORATE.onError(span.get(), throwable);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -79,4 +79,13 @@ public class RatpackServerDecorator extends HttpServerDecorator<Request, Request
|
|||
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span onError(final Span span, final Throwable throwable) {
|
||||
// Attempt to unwrap ratpack.handling.internal.HandlerException without direct reference.
|
||||
if (throwable instanceof Error && throwable.getCause() != null) {
|
||||
return super.onError(span, throwable.getCause());
|
||||
}
|
||||
return super.onError(span, throwable);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
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.opentracing.Scope
|
||||
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.path.PathBinding
|
||||
import ratpack.test.exec.ExecHarness
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
class RatpackOtherTest extends AgentTestRunner {
|
||||
|
||||
OkHttpClient client = OkHttpUtils.client()
|
||||
|
||||
def "test bindings for #path"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
prefix("a") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("b/::\\d+") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("c/:val?") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("d/:val") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("e/:val?:\\d+") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("f/:val:\\d+") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(HttpUrl.get(app.address).newBuilder().addPathSegments(path).build())
|
||||
.get()
|
||||
.build()
|
||||
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
|
||||
then:
|
||||
resp.code() == 200
|
||||
resp.body.string() == route
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /$route"
|
||||
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.resolve(path)}"
|
||||
"$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 /$route"
|
||||
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" "${app.address.resolve(path)}"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
path | route
|
||||
"a" | "a"
|
||||
"b/123" | "b/::\\d+"
|
||||
"c" | "c/:val?"
|
||||
"c/123" | "c/:val?"
|
||||
"c/foo" | "c/:val?"
|
||||
"d/123" | "d/:val"
|
||||
"d/foo" | "d/:val"
|
||||
"e" | "e/:val?:\\d+"
|
||||
"e/123" | "e/:val?:\\d+"
|
||||
"e/foo" | "e/:val?:\\d+"
|
||||
"f/123" | "f/:val:\\d+"
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,694 +0,0 @@
|
|||
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.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 bindings for #path"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
prefix("a") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("b/::\\d+") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("c/:val?") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("d/:val") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("e/:val?:\\d+") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
prefix("f/:val:\\d+") {
|
||||
all {
|
||||
context.render(context.get(PathBinding).description)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(HttpUrl.get(app.address).newBuilder().addPathSegments(path).build())
|
||||
.get()
|
||||
.build()
|
||||
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
|
||||
then:
|
||||
resp.code() == 200
|
||||
resp.body.string() == route
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /$route"
|
||||
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.resolve(path)}"
|
||||
"$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 /$route"
|
||||
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" "${app.address.resolve(path)}"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
path | route
|
||||
"a" | "a"
|
||||
"b/123" | "b/::\\d+"
|
||||
"c" | "c/:val?"
|
||||
"c/123" | "c/:val?"
|
||||
"c/foo" | "c/:val?"
|
||||
"d/123" | "d/:val"
|
||||
"d/foo" | "d/:val"
|
||||
"e" | "e/:val?:\\d+"
|
||||
"e/123" | "e/:val?:\\d+"
|
||||
"e/foo" | "e/:val?:\\d+"
|
||||
"f/123" | "f/:val:\\d+"
|
||||
}
|
||||
|
||||
def "test handler error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
prefix("handler-error") {
|
||||
all {
|
||||
0 / 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.resolve("/handler-error?query=param").toURL())
|
||||
.get()
|
||||
.build()
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
then:
|
||||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /handler-error"
|
||||
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.resolve('handler-error')}"
|
||||
"$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 /handler-error"
|
||||
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" "${app.address.resolve('handler-error')}"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
errorTags(HandlerException, Pattern.compile("java.lang.ArithmeticException: Division( is)? undefined"))
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test promise error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
get("promise-error") {
|
||||
Promise.async {
|
||||
0 / 0
|
||||
}.then {
|
||||
context.render(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.resolve("promise-error?query=param").toURL())
|
||||
.get()
|
||||
.build()
|
||||
when:
|
||||
def resp = client.newCall(request).execute()
|
||||
then:
|
||||
resp.code() == 500
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
resourceName "GET /promise-error"
|
||||
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.resolve('promise-error')}"
|
||||
"$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 /promise-error"
|
||||
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" "${app.address.resolve('promise-error')}"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
"$Tags.ERROR.key" true
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test render error response"() {
|
||||
setup:
|
||||
def app = GroovyEmbeddedApp.ratpack {
|
||||
handlers {
|
||||
all {
|
||||
context.render(Promise.sync {
|
||||
return "fail " + 0 / 0
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
def request = new Request.Builder()
|
||||
.url(app.address.resolve("?query=param").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" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
"$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" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
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(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}/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" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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" "$app.address"
|
||||
"$Tags.PEER_HOSTNAME.key" "$app.address.host"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
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,26 @@
|
|||
package client
|
||||
|
||||
|
||||
import ratpack.exec.ExecResult
|
||||
|
||||
class RatpackForkedHttpClientTest extends RatpackHttpClientTest {
|
||||
|
||||
@Override
|
||||
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
|
||||
ExecResult<Integer> result = exec.yield {
|
||||
def resp = client.request(uri) { spec ->
|
||||
spec.method(method)
|
||||
spec.headers { headersSpec ->
|
||||
headers.entrySet().each {
|
||||
headersSpec.add(it.key, it.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return resp.fork().map {
|
||||
callback?.call()
|
||||
it.status.code
|
||||
}
|
||||
}
|
||||
return result.value
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package client
|
||||
|
||||
import datadog.trace.agent.test.base.HttpClientTest
|
||||
import datadog.trace.instrumentation.netty41.client.NettyHttpClientDecorator
|
||||
import ratpack.exec.ExecResult
|
||||
import ratpack.http.client.HttpClient
|
||||
import ratpack.test.exec.ExecHarness
|
||||
import spock.lang.AutoCleanup
|
||||
import spock.lang.Shared
|
||||
|
||||
class RatpackHttpClientTest extends HttpClientTest<NettyHttpClientDecorator> {
|
||||
|
||||
@AutoCleanup
|
||||
@Shared
|
||||
ExecHarness exec = ExecHarness.harness()
|
||||
|
||||
@Shared
|
||||
def client = HttpClient.of {}
|
||||
|
||||
@Override
|
||||
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
|
||||
ExecResult<Integer> result = exec.yield {
|
||||
def resp = client.request(uri) { spec ->
|
||||
spec.method(method)
|
||||
spec.headers { headersSpec ->
|
||||
headers.entrySet().each {
|
||||
headersSpec.add(it.key, it.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return resp.map {
|
||||
callback?.call()
|
||||
it.status.code
|
||||
}
|
||||
}
|
||||
return result.value
|
||||
}
|
||||
|
||||
@Override
|
||||
NettyHttpClientDecorator decorator() {
|
||||
return NettyHttpClientDecorator.DECORATE
|
||||
}
|
||||
|
||||
@Override
|
||||
String expectedOperationName() {
|
||||
return "netty.client.request"
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean testRedirects() {
|
||||
false
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean testConnectionFailure() {
|
||||
false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
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("io.netty.handler.codec.ByteToMessageDecoder"))
|
||||
.transform(
|
||||
new AgentBuilder.Transformer.ForAdvice()
|
||||
.advice(
|
||||
named("channelRead"), HttpServerTestAdvice.ServerEntryAdvice.class.getName()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package server
|
||||
|
||||
import ratpack.exec.Promise
|
||||
import ratpack.groovy.test.embed.GroovyEmbeddedApp
|
||||
import ratpack.test.embed.EmbeddedApp
|
||||
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.ERROR
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||
|
||||
class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
|
||||
|
||||
@Override
|
||||
EmbeddedApp startServer(int bindPort) {
|
||||
def ratpack = GroovyEmbeddedApp.ratpack {
|
||||
serverConfig {
|
||||
port bindPort
|
||||
}
|
||||
bindings {
|
||||
bind TestErrorHandler
|
||||
}
|
||||
handlers {
|
||||
prefix(SUCCESS.rawPath()) {
|
||||
all {
|
||||
Promise.sync {
|
||||
SUCCESS
|
||||
} then { ServerEndpoint endpoint ->
|
||||
controller(endpoint) {
|
||||
context.response.status(endpoint.status).send(endpoint.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prefix(REDIRECT.rawPath()) {
|
||||
all {
|
||||
Promise.sync {
|
||||
REDIRECT
|
||||
} then { ServerEndpoint endpoint ->
|
||||
controller(endpoint) {
|
||||
context.redirect(endpoint.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prefix(ERROR.rawPath()) {
|
||||
all {
|
||||
Promise.sync {
|
||||
ERROR
|
||||
} then { ServerEndpoint endpoint ->
|
||||
controller(endpoint) {
|
||||
context.response.status(endpoint.status).send(endpoint.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prefix(EXCEPTION.rawPath()) {
|
||||
all {
|
||||
Promise.sync {
|
||||
EXCEPTION
|
||||
} then { ServerEndpoint endpoint ->
|
||||
controller(endpoint) {
|
||||
throw new Exception(endpoint.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ratpack.server.start()
|
||||
|
||||
assert ratpack.address.port == bindPort
|
||||
return ratpack
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package server
|
||||
|
||||
import ratpack.exec.Promise
|
||||
import ratpack.groovy.test.embed.GroovyEmbeddedApp
|
||||
import ratpack.test.embed.EmbeddedApp
|
||||
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.ERROR
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||
|
||||
class RatpackForkedHttpServerTest extends RatpackHttpServerTest {
|
||||
|
||||
@Override
|
||||
EmbeddedApp startServer(int bindPort) {
|
||||
def ratpack = GroovyEmbeddedApp.ratpack {
|
||||
serverConfig {
|
||||
port bindPort
|
||||
}
|
||||
bindings {
|
||||
bind TestErrorHandler
|
||||
}
|
||||
handlers {
|
||||
prefix(SUCCESS.rawPath()) {
|
||||
all {
|
||||
Promise.sync {
|
||||
SUCCESS
|
||||
}.fork().then { ServerEndpoint endpoint ->
|
||||
controller(endpoint) {
|
||||
context.response.status(endpoint.status).send(endpoint.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prefix(REDIRECT.rawPath()) {
|
||||
all {
|
||||
Promise.sync {
|
||||
REDIRECT
|
||||
}.fork().then { ServerEndpoint endpoint ->
|
||||
controller(endpoint) {
|
||||
context.redirect(endpoint.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prefix(ERROR.rawPath()) {
|
||||
all {
|
||||
Promise.sync {
|
||||
ERROR
|
||||
}.fork().then { ServerEndpoint endpoint ->
|
||||
controller(endpoint) {
|
||||
context.response.status(endpoint.status).send(endpoint.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prefix(EXCEPTION.rawPath()) {
|
||||
all {
|
||||
Promise.sync {
|
||||
EXCEPTION
|
||||
}.fork().then { ServerEndpoint endpoint ->
|
||||
controller(endpoint) {
|
||||
throw new Exception(endpoint.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ratpack.server.start()
|
||||
|
||||
assert ratpack.address.port == bindPort
|
||||
return ratpack
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
package server
|
||||
|
||||
import datadog.opentracing.DDSpan
|
||||
import datadog.trace.agent.test.asserts.ListWriterAssert
|
||||
import datadog.trace.agent.test.asserts.TraceAssert
|
||||
import datadog.trace.agent.test.base.HttpServerTest
|
||||
import datadog.trace.api.DDSpanTypes
|
||||
import datadog.trace.instrumentation.netty41.server.NettyHttpServerDecorator
|
||||
import datadog.trace.instrumentation.ratpack.RatpackServerDecorator
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.SimpleType
|
||||
import io.opentracing.tag.Tags
|
||||
import ratpack.error.ServerErrorHandler
|
||||
import ratpack.groovy.test.embed.GroovyEmbeddedApp
|
||||
import ratpack.handling.Context
|
||||
import ratpack.test.embed.EmbeddedApp
|
||||
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.ERROR
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||
|
||||
class RatpackHttpServerTest extends HttpServerTest<EmbeddedApp, NettyHttpServerDecorator> {
|
||||
|
||||
@Override
|
||||
EmbeddedApp startServer(int bindPort) {
|
||||
def ratpack = GroovyEmbeddedApp.ratpack {
|
||||
serverConfig {
|
||||
port bindPort
|
||||
}
|
||||
bindings {
|
||||
bind TestErrorHandler
|
||||
}
|
||||
handlers {
|
||||
prefix(SUCCESS.rawPath()) {
|
||||
all {
|
||||
controller(SUCCESS) {
|
||||
context.response.status(SUCCESS.status).send(SUCCESS.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
prefix(REDIRECT.rawPath()) {
|
||||
all {
|
||||
controller(REDIRECT) {
|
||||
context.redirect(REDIRECT.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
prefix(ERROR.rawPath()) {
|
||||
all {
|
||||
controller(ERROR) {
|
||||
context.response.status(ERROR.status).send(ERROR.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
prefix(EXCEPTION.rawPath()) {
|
||||
all {
|
||||
controller(EXCEPTION) {
|
||||
throw new Exception(EXCEPTION.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ratpack.server.start()
|
||||
|
||||
assert ratpack.address.port == bindPort
|
||||
return ratpack
|
||||
}
|
||||
|
||||
static class TestErrorHandler implements ServerErrorHandler {
|
||||
@Override
|
||||
void error(Context context, Throwable throwable) throws Exception {
|
||||
context.response.status(500).send(throwable.message)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void stopServer(EmbeddedApp server) {
|
||||
server.close()
|
||||
}
|
||||
|
||||
@Override
|
||||
NettyHttpServerDecorator decorator() {
|
||||
return NettyHttpServerDecorator.DECORATE
|
||||
}
|
||||
|
||||
@Override
|
||||
String expectedOperationName() {
|
||||
"netty.request"
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasHandlerSpan() {
|
||||
true
|
||||
}
|
||||
|
||||
void cleanAndAssertTraces(
|
||||
final int size,
|
||||
@ClosureParams(value = SimpleType, options = "datadog.trace.agent.test.asserts.ListWriterAssert")
|
||||
@DelegatesTo(value = ListWriterAssert, strategy = Closure.DELEGATE_FIRST)
|
||||
final Closure spec) {
|
||||
// If this is failing, make sure HttpServerTestAdvice is applied correctly.
|
||||
TEST_WRITER.waitForTraces(size * 2)
|
||||
|
||||
// Ratpack closes the handler span before the controller returns, so we need to manually reorder it.
|
||||
TEST_WRITER.each {
|
||||
def controllerSpan = it.find {
|
||||
it.operationName == "controller"
|
||||
}
|
||||
if (controllerSpan) {
|
||||
it.remove(controllerSpan)
|
||||
it.add(controllerSpan)
|
||||
}
|
||||
}
|
||||
super.cleanAndAssertTraces(size, spec)
|
||||
}
|
||||
|
||||
@Override
|
||||
void handlerSpan(TraceAssert trace, int index, Object parent, ServerEndpoint endpoint = SUCCESS) {
|
||||
trace.span(index) {
|
||||
serviceName expectedServiceName()
|
||||
operationName "ratpack.handler"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
errored endpoint == ERROR || endpoint == EXCEPTION
|
||||
childOf(parent as DDSpan)
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" RatpackServerDecorator.DECORATE.component()
|
||||
"$Tags.HTTP_STATUS.key" Integer
|
||||
"$Tags.HTTP_URL.key" String
|
||||
"$Tags.PEER_HOSTNAME.key" "localhost"
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
"$Tags.PEER_HOST_IPV4.key" { it == null || it == "127.0.0.1" } // Optional
|
||||
"$Tags.HTTP_METHOD.key" String
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
defaultTags()
|
||||
if (endpoint == ERROR) {
|
||||
"$Tags.ERROR.key" true
|
||||
} else if (endpoint == EXCEPTION) {
|
||||
errorTags(Exception, EXCEPTION.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -72,6 +72,10 @@ abstract class HttpServerTest<SERVER, DECORATOR extends HttpServerDecorator> ext
|
|||
|
||||
abstract String expectedOperationName()
|
||||
|
||||
boolean hasHandlerSpan() {
|
||||
false
|
||||
}
|
||||
|
||||
boolean testNotFound() {
|
||||
true
|
||||
}
|
||||
|
@ -148,9 +152,17 @@ abstract class HttpServerTest<SERVER, DECORATOR extends HttpServerDecorator> ext
|
|||
and:
|
||||
cleanAndAssertTraces(count) {
|
||||
(1..count).eachWithIndex { val, i ->
|
||||
trace(i, 2) {
|
||||
serverSpan(it, 0)
|
||||
controllerSpan(it, 1, span(0))
|
||||
if (hasHandlerSpan()) {
|
||||
trace(i, 3) {
|
||||
serverSpan(it, 0)
|
||||
handlerSpan(it, 1, span(0))
|
||||
controllerSpan(it, 2, span(1))
|
||||
}
|
||||
} else {
|
||||
trace(i, 2) {
|
||||
serverSpan(it, 0)
|
||||
controllerSpan(it, 1, span(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,9 +189,17 @@ abstract class HttpServerTest<SERVER, DECORATOR extends HttpServerDecorator> ext
|
|||
|
||||
and:
|
||||
cleanAndAssertTraces(1) {
|
||||
trace(0, 2) {
|
||||
serverSpan(it, 0, traceId, parentId)
|
||||
controllerSpan(it, 1, span(0))
|
||||
if (hasHandlerSpan()) {
|
||||
trace(0, 3) {
|
||||
serverSpan(it, 0, traceId, parentId)
|
||||
handlerSpan(it, 1, span(0))
|
||||
controllerSpan(it, 2, span(1))
|
||||
}
|
||||
} else {
|
||||
trace(0, 2) {
|
||||
serverSpan(it, 0, traceId, parentId)
|
||||
controllerSpan(it, 1, span(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,9 +221,17 @@ abstract class HttpServerTest<SERVER, DECORATOR extends HttpServerDecorator> ext
|
|||
|
||||
and:
|
||||
cleanAndAssertTraces(1) {
|
||||
trace(0, 2) {
|
||||
serverSpan(it, 0, null, null, method, REDIRECT)
|
||||
controllerSpan(it, 1, span(0))
|
||||
if (hasHandlerSpan()) {
|
||||
trace(0, 3) {
|
||||
serverSpan(it, 0, null, null, method, REDIRECT)
|
||||
handlerSpan(it, 1, span(0))
|
||||
controllerSpan(it, 2, span(1))
|
||||
}
|
||||
} else {
|
||||
trace(0, 2) {
|
||||
serverSpan(it, 0, null, null, method, REDIRECT)
|
||||
controllerSpan(it, 1, span(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,9 +251,17 @@ abstract class HttpServerTest<SERVER, DECORATOR extends HttpServerDecorator> ext
|
|||
|
||||
and:
|
||||
cleanAndAssertTraces(1) {
|
||||
trace(0, 2) {
|
||||
serverSpan(it, 0, null, null, method, ERROR)
|
||||
controllerSpan(it, 1, span(0))
|
||||
if (hasHandlerSpan()) {
|
||||
trace(0, 3) {
|
||||
serverSpan(it, 0, null, null, method, ERROR)
|
||||
handlerSpan(it, 1, span(0), ERROR)
|
||||
controllerSpan(it, 2, span(1))
|
||||
}
|
||||
} else {
|
||||
trace(0, 2) {
|
||||
serverSpan(it, 0, null, null, method, ERROR)
|
||||
controllerSpan(it, 1, span(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,9 +283,17 @@ abstract class HttpServerTest<SERVER, DECORATOR extends HttpServerDecorator> ext
|
|||
|
||||
and:
|
||||
cleanAndAssertTraces(1) {
|
||||
trace(0, 2) {
|
||||
serverSpan(it, 0, null, null, method, EXCEPTION)
|
||||
controllerSpan(it, 1, span(0), EXCEPTION.body)
|
||||
if (hasHandlerSpan()) {
|
||||
trace(0, 3) {
|
||||
serverSpan(it, 0, null, null, method, EXCEPTION)
|
||||
handlerSpan(it, 1, span(0), EXCEPTION)
|
||||
controllerSpan(it, 2, span(1), EXCEPTION.body)
|
||||
}
|
||||
} else {
|
||||
trace(0, 2) {
|
||||
serverSpan(it, 0, null, null, method, EXCEPTION)
|
||||
controllerSpan(it, 1, span(0), EXCEPTION.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,8 +313,15 @@ abstract class HttpServerTest<SERVER, DECORATOR extends HttpServerDecorator> ext
|
|||
|
||||
and:
|
||||
cleanAndAssertTraces(1) {
|
||||
trace(0, 1) {
|
||||
serverSpan(it, 0, null, null, method, NOT_FOUND)
|
||||
if (hasHandlerSpan()) {
|
||||
trace(0, 2) {
|
||||
serverSpan(it, 0, null, null, method, NOT_FOUND)
|
||||
handlerSpan(it, 1, span(0), NOT_FOUND)
|
||||
}
|
||||
} else {
|
||||
trace(0, 1) {
|
||||
serverSpan(it, 0, null, null, method, NOT_FOUND)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,6 +371,10 @@ abstract class HttpServerTest<SERVER, DECORATOR extends HttpServerDecorator> ext
|
|||
}
|
||||
}
|
||||
|
||||
void handlerSpan(TraceAssert trace, int index, Object parent, ServerEndpoint endpoint = SUCCESS) {
|
||||
throw new UnsupportedOperationException("handlerSpan not implemented in " + getClass().name)
|
||||
}
|
||||
|
||||
// parent span must be cast otherwise it breaks debugging classloading (junit loads it early)
|
||||
void serverSpan(TraceAssert trace, int index, String traceID = null, String parentID = null, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
|
||||
trace.span(index) {
|
||||
|
|
Loading…
Reference in New Issue