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
|
ctx.fireChannelRead(msg); // superclass does not throw
|
||||||
} else {
|
} else {
|
||||||
try (final Scope scope = tracer.scopeManager().activate(span, false)) {
|
try (final Scope scope = tracer.scopeManager().activate(span, false)) {
|
||||||
|
if (scope instanceof TraceScope) {
|
||||||
|
((TraceScope) scope).setAsyncPropagation(true);
|
||||||
|
}
|
||||||
ctx.fireChannelRead(msg); // superclass does not throw
|
ctx.fireChannelRead(msg); // superclass does not throw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,6 +103,7 @@ class Netty40ServerTest extends HttpServerTest<EventLoopGroup, NettyHttpServerDe
|
||||||
NettyHttpServerDecorator.DECORATE
|
NettyHttpServerDecorator.DECORATE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
String expectedOperationName() {
|
String expectedOperationName() {
|
||||||
"netty.request"
|
"netty.request"
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,9 @@ public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapte
|
||||||
ctx.fireChannelRead(msg); // superclass does not throw
|
ctx.fireChannelRead(msg); // superclass does not throw
|
||||||
} else {
|
} else {
|
||||||
try (final Scope scope = tracer.scopeManager().activate(span, false)) {
|
try (final Scope scope = tracer.scopeManager().activate(span, false)) {
|
||||||
|
if (scope instanceof TraceScope) {
|
||||||
|
((TraceScope) scope).setAsyncPropagation(true);
|
||||||
|
}
|
||||||
ctx.fireChannelRead(msg); // superclass does not throw
|
ctx.fireChannelRead(msg); // superclass does not throw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,7 @@ class Netty41ServerTest extends HttpServerTest<EventLoopGroup, NettyHttpServerDe
|
||||||
NettyHttpServerDecorator.DECORATE
|
NettyHttpServerDecorator.DECORATE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
String expectedOperationName() {
|
String expectedOperationName() {
|
||||||
"netty.request"
|
"netty.request"
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,9 @@ dependencies {
|
||||||
apply plugin: 'org.unbroken-dome.test-sets'
|
apply plugin: 'org.unbroken-dome.test-sets'
|
||||||
|
|
||||||
testSets {
|
testSets {
|
||||||
latestDepTest
|
latestDepTest {
|
||||||
|
dirName = 'test'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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;
|
package datadog.trace.instrumentation.ratpack;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
import static datadog.trace.instrumentation.ratpack.RatpackServerDecorator.DECORATE;
|
|
||||||
import static java.util.Collections.singletonMap;
|
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.isInterface;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
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 com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
import io.opentracing.Span;
|
|
||||||
import io.opentracing.util.GlobalTracer;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.bytebuddy.asm.Advice;
|
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
@ -27,8 +24,8 @@ public class ServerErrorHandlerInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return named("ratpack.exec.Execution")
|
return not(isInterface().or(isAbstract()))
|
||||||
.or(not(isInterface()).and(safeHasSuperType(named("ratpack.error.ServerErrorHandler"))));
|
.and(safeHasSuperType(named("ratpack.error.ServerErrorHandler")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -44,16 +41,9 @@ public class ServerErrorHandlerInstrumentation extends Instrumenter.Default {
|
||||||
@Override
|
@Override
|
||||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
return singletonMap(
|
return singletonMap(
|
||||||
named("error").and(takesArgument(1, Throwable.class)), ErrorHandlerAdvice.class.getName());
|
named("error")
|
||||||
}
|
.and(takesArgument(0, named("ratpack.handling.Context")))
|
||||||
|
.and(takesArgument(1, Throwable.class)),
|
||||||
public static class ErrorHandlerAdvice {
|
packageName + ".ErrorHandlerAdvice");
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
|
||||||
public static void captureThrowable(@Advice.Argument(1) final Throwable throwable) {
|
|
||||||
final Span span = GlobalTracer.get().activeSpan();
|
|
||||||
if (span != null) {
|
|
||||||
DECORATE.onError(span, throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,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;
|
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()
|
abstract String expectedOperationName()
|
||||||
|
|
||||||
|
boolean hasHandlerSpan() {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
boolean testNotFound() {
|
boolean testNotFound() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -148,9 +152,17 @@ abstract class HttpServerTest<SERVER, DECORATOR extends HttpServerDecorator> ext
|
||||||
and:
|
and:
|
||||||
cleanAndAssertTraces(count) {
|
cleanAndAssertTraces(count) {
|
||||||
(1..count).eachWithIndex { val, i ->
|
(1..count).eachWithIndex { val, i ->
|
||||||
trace(i, 2) {
|
if (hasHandlerSpan()) {
|
||||||
serverSpan(it, 0)
|
trace(i, 3) {
|
||||||
controllerSpan(it, 1, span(0))
|
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:
|
and:
|
||||||
cleanAndAssertTraces(1) {
|
cleanAndAssertTraces(1) {
|
||||||
trace(0, 2) {
|
if (hasHandlerSpan()) {
|
||||||
serverSpan(it, 0, traceId, parentId)
|
trace(0, 3) {
|
||||||
controllerSpan(it, 1, span(0))
|
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:
|
and:
|
||||||
cleanAndAssertTraces(1) {
|
cleanAndAssertTraces(1) {
|
||||||
trace(0, 2) {
|
if (hasHandlerSpan()) {
|
||||||
serverSpan(it, 0, null, null, method, REDIRECT)
|
trace(0, 3) {
|
||||||
controllerSpan(it, 1, span(0))
|
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:
|
and:
|
||||||
cleanAndAssertTraces(1) {
|
cleanAndAssertTraces(1) {
|
||||||
trace(0, 2) {
|
if (hasHandlerSpan()) {
|
||||||
serverSpan(it, 0, null, null, method, ERROR)
|
trace(0, 3) {
|
||||||
controllerSpan(it, 1, span(0))
|
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:
|
and:
|
||||||
cleanAndAssertTraces(1) {
|
cleanAndAssertTraces(1) {
|
||||||
trace(0, 2) {
|
if (hasHandlerSpan()) {
|
||||||
serverSpan(it, 0, null, null, method, EXCEPTION)
|
trace(0, 3) {
|
||||||
controllerSpan(it, 1, span(0), EXCEPTION.body)
|
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:
|
and:
|
||||||
cleanAndAssertTraces(1) {
|
cleanAndAssertTraces(1) {
|
||||||
trace(0, 1) {
|
if (hasHandlerSpan()) {
|
||||||
serverSpan(it, 0, null, null, method, NOT_FOUND)
|
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)
|
// 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) {
|
void serverSpan(TraceAssert trace, int index, String traceID = null, String parentID = null, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
|
||||||
trace.span(index) {
|
trace.span(index) {
|
||||||
|
|
Loading…
Reference in New Issue