Add library instrumentation for Ratpack server (#3749)
* Add Ratpack server library instrumentation * Finish * Back to 1.4 * Drift * Cocaine * Update instrumentation/ratpack-1.4/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackTracingBuilder.java Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com> * Cleanup Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com>
This commit is contained in:
parent
32351d0bab
commit
e92ecc02bc
|
@ -15,6 +15,8 @@ dependencies {
|
||||||
|
|
||||||
implementation(project(":instrumentation:netty:netty-4.1:javaagent"))
|
implementation(project(":instrumentation:netty:netty-4.1:javaagent"))
|
||||||
|
|
||||||
|
testImplementation(project(":instrumentation:ratpack-1.4:testing"))
|
||||||
|
|
||||||
testLibrary("io.ratpack:ratpack-test:1.4.0")
|
testLibrary("io.ratpack:ratpack-test:1.4.0")
|
||||||
|
|
||||||
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_11)) {
|
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_11)) {
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.SERVER
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
|
|
||||||
import io.opentelemetry.instrumentation.test.utils.PortUtils
|
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
|
||||||
import io.opentelemetry.testing.internal.armeria.client.WebClient
|
|
||||||
import ratpack.path.PathBinding
|
|
||||||
import ratpack.server.RatpackServer
|
|
||||||
import spock.lang.Shared
|
|
||||||
|
|
||||||
class RatpackOtherTest extends AgentInstrumentationSpecification {
|
|
||||||
|
|
||||||
@Shared
|
|
||||||
RatpackServer app = RatpackServer.start {
|
|
||||||
it.serverConfig {
|
|
||||||
it.port(PortUtils.findOpenPort())
|
|
||||||
it.address(InetAddress.getByName("localhost"))
|
|
||||||
}
|
|
||||||
it.handlers {
|
|
||||||
it.prefix("a") {
|
|
||||||
it.all {context ->
|
|
||||||
context.render(context.get(PathBinding).description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix("b/::\\d+") {
|
|
||||||
it.all {context ->
|
|
||||||
context.render(context.get(PathBinding).description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix("c/:val?") {
|
|
||||||
it.all {context ->
|
|
||||||
context.render(context.get(PathBinding).description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix("d/:val") {
|
|
||||||
it.all {context ->
|
|
||||||
context.render(context.get(PathBinding).description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix("e/:val?:\\d+") {
|
|
||||||
it.all {context ->
|
|
||||||
context.render(context.get(PathBinding).description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix("f/:val:\\d+") {
|
|
||||||
it.all {context ->
|
|
||||||
context.render(context.get(PathBinding).description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force HTTP/1 with h1c to prevent tracing of upgrade request.
|
|
||||||
@Shared
|
|
||||||
WebClient client = WebClient.of("h1c://localhost:${app.bindPort}")
|
|
||||||
|
|
||||||
def cleanupSpec() {
|
|
||||||
app.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
def "test bindings for #path"() {
|
|
||||||
when:
|
|
||||||
def resp = client.get(path).aggregate().join()
|
|
||||||
|
|
||||||
then:
|
|
||||||
resp.status().code() == 200
|
|
||||||
resp.contentUtf8() == route
|
|
||||||
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 2) {
|
|
||||||
span(0) {
|
|
||||||
name "/$route"
|
|
||||||
kind SERVER
|
|
||||||
hasNoParent()
|
|
||||||
attributes {
|
|
||||||
"${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1"
|
|
||||||
"${SemanticAttributes.NET_PEER_PORT.key}" Long
|
|
||||||
"${SemanticAttributes.HTTP_URL.key}" "http://localhost:${app.bindPort}/${path}"
|
|
||||||
"${SemanticAttributes.HTTP_METHOD.key}" "GET"
|
|
||||||
"${SemanticAttributes.HTTP_STATUS_CODE.key}" 200
|
|
||||||
"${SemanticAttributes.HTTP_FLAVOR.key}" "1.1"
|
|
||||||
"${SemanticAttributes.HTTP_USER_AGENT.key}" String
|
|
||||||
"${SemanticAttributes.HTTP_CLIENT_IP.key}" "127.0.0.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
span(1) {
|
|
||||||
name "/$route"
|
|
||||||
kind INTERNAL
|
|
||||||
childOf span(0)
|
|
||||||
attributes {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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+"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,114 +5,12 @@
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR
|
import io.opentelemetry.instrumentation.ratpack.server.AbstractRatpackAsyncHttpServerTest
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.INDEXED_CHILD
|
import ratpack.server.RatpackServerSpec
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
|
||||||
|
|
||||||
import ratpack.error.ServerErrorHandler
|
|
||||||
import ratpack.exec.Promise
|
|
||||||
import ratpack.server.RatpackServer
|
|
||||||
|
|
||||||
class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
|
|
||||||
|
|
||||||
|
class RatpackAsyncHttpServerTest extends AbstractRatpackAsyncHttpServerTest implements AgentTestTrait {
|
||||||
@Override
|
@Override
|
||||||
RatpackServer startServer(int bindPort) {
|
void configure(RatpackServerSpec serverSpec) {
|
||||||
def ratpack = RatpackServer.start {
|
|
||||||
it.serverConfig {
|
|
||||||
it.port(bindPort)
|
|
||||||
it.address(InetAddress.getByName("localhost"))
|
|
||||||
}
|
|
||||||
it.handlers {
|
|
||||||
it.register {
|
|
||||||
it.add(ServerErrorHandler, new TestErrorHandler())
|
|
||||||
}
|
|
||||||
it.prefix(SUCCESS.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
Promise.sync {
|
|
||||||
SUCCESS
|
|
||||||
} then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
context.response.status(endpoint.status).send(endpoint.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(INDEXED_CHILD.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
Promise.sync {
|
|
||||||
INDEXED_CHILD
|
|
||||||
} then {
|
|
||||||
controller(INDEXED_CHILD) {
|
|
||||||
INDEXED_CHILD.collectSpanAttributes { context.request.queryParams.get(it) }
|
|
||||||
context.response.status(INDEXED_CHILD.status).send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(QUERY_PARAM.rawPath()) {
|
|
||||||
it.all { context ->
|
|
||||||
Promise.sync {
|
|
||||||
QUERY_PARAM
|
|
||||||
} then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
context.response.status(endpoint.status).send(context.request.query)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(REDIRECT.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
Promise.sync {
|
|
||||||
REDIRECT
|
|
||||||
} then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
context.redirect(endpoint.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(ERROR.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
Promise.sync {
|
|
||||||
ERROR
|
|
||||||
} then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
context.response.status(endpoint.status).send(endpoint.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(EXCEPTION.rawPath()) {
|
|
||||||
it.all {
|
|
||||||
Promise.sync {
|
|
||||||
EXCEPTION
|
|
||||||
} then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
throw new Exception(endpoint.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix("path/:id/param") {
|
|
||||||
it.all {context ->
|
|
||||||
Promise.sync {
|
|
||||||
PATH_PARAM
|
|
||||||
} then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
context.response.status(endpoint.status).send(context.pathTokens.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert ratpack.bindPort == bindPort
|
|
||||||
assert ratpack.bindHost == 'localhost'
|
|
||||||
return ratpack
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,170 +5,12 @@
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR
|
import io.opentelemetry.instrumentation.ratpack.server.AbstractRatpackForkedHttpServerTest
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.INDEXED_CHILD
|
import ratpack.server.RatpackServerSpec
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
|
||||||
|
|
||||||
import io.opentelemetry.api.trace.SpanKind
|
|
||||||
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest
|
|
||||||
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse
|
|
||||||
import io.opentelemetry.testing.internal.armeria.common.HttpMethod
|
|
||||||
import ratpack.error.ServerErrorHandler
|
|
||||||
import ratpack.exec.Execution
|
|
||||||
import ratpack.exec.Promise
|
|
||||||
import ratpack.exec.Result
|
|
||||||
import ratpack.exec.util.ParallelBatch
|
|
||||||
import ratpack.server.RatpackServer
|
|
||||||
|
|
||||||
class RatpackForkedHttpServerTest extends RatpackHttpServerTest {
|
|
||||||
|
|
||||||
|
class RatpackForkedHttpServerTest extends AbstractRatpackForkedHttpServerTest implements AgentTestTrait {
|
||||||
@Override
|
@Override
|
||||||
RatpackServer startServer(int bindPort) {
|
void configure(RatpackServerSpec serverSpec) {
|
||||||
|
|
||||||
def ratpack = RatpackServer.start {
|
|
||||||
it.serverConfig {
|
|
||||||
it.port(bindPort)
|
|
||||||
it.address(InetAddress.getByName("localhost"))
|
|
||||||
}
|
|
||||||
it.handlers {
|
|
||||||
it.register {
|
|
||||||
it.add(ServerErrorHandler, new TestErrorHandler())
|
|
||||||
}
|
|
||||||
it.prefix(SUCCESS.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
Promise.sync {
|
|
||||||
SUCCESS
|
|
||||||
}.fork().then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
context.response.status(endpoint.status).send(endpoint.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(INDEXED_CHILD.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
Promise.sync {
|
|
||||||
INDEXED_CHILD
|
|
||||||
}.fork().then {
|
|
||||||
controller(INDEXED_CHILD) {
|
|
||||||
INDEXED_CHILD.collectSpanAttributes { context.request.queryParams.get(it) }
|
|
||||||
context.response.status(INDEXED_CHILD.status).send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(QUERY_PARAM.rawPath()) {
|
|
||||||
it.all { context ->
|
|
||||||
Promise.sync {
|
|
||||||
QUERY_PARAM
|
|
||||||
}.fork().then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
context.response.status(endpoint.status).send(context.request.query)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(REDIRECT.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
Promise.sync {
|
|
||||||
REDIRECT
|
|
||||||
}.fork().then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
context.redirect(endpoint.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(ERROR.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
Promise.sync {
|
|
||||||
ERROR
|
|
||||||
}.fork().then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
context.response.status(endpoint.status).send(endpoint.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(EXCEPTION.rawPath()) {
|
|
||||||
it.all {
|
|
||||||
Promise.sync {
|
|
||||||
EXCEPTION
|
|
||||||
}.fork().then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
throw new Exception(endpoint.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix("path/:id/param") {
|
|
||||||
it.all {context ->
|
|
||||||
Promise.sync {
|
|
||||||
PATH_PARAM
|
|
||||||
}.fork().then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
context.response.status(endpoint.status).send(context.pathTokens.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix("fork_and_yieldAll") {
|
|
||||||
it.all {context ->
|
|
||||||
def promise = Promise.async { upstream ->
|
|
||||||
Execution.fork().start({
|
|
||||||
upstream.accept(Result.success(SUCCESS))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ParallelBatch.of(promise).yieldAll().flatMap { list ->
|
|
||||||
Promise.sync { list.get(0).value }
|
|
||||||
} then { endpoint ->
|
|
||||||
controller(endpoint) {
|
|
||||||
context.response.status(endpoint.status).send(endpoint.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert ratpack.bindPort == bindPort
|
|
||||||
assert ratpack.bindHost == 'localhost'
|
|
||||||
return ratpack
|
|
||||||
}
|
|
||||||
|
|
||||||
def "test fork and yieldAll"() {
|
|
||||||
setup:
|
|
||||||
def url = address.resolve("fork_and_yieldAll").toString()
|
|
||||||
url = url.replace("http://", "h1c://")
|
|
||||||
def request = AggregatedHttpRequest.of(HttpMethod.GET, url)
|
|
||||||
AggregatedHttpResponse response = client.execute(request).aggregate().join()
|
|
||||||
|
|
||||||
expect:
|
|
||||||
response.status().code() == SUCCESS.status
|
|
||||||
response.contentUtf8() == SUCCESS.body
|
|
||||||
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 3) {
|
|
||||||
span(0) {
|
|
||||||
name "/fork_and_yieldAll"
|
|
||||||
kind SpanKind.SERVER
|
|
||||||
hasNoParent()
|
|
||||||
}
|
|
||||||
span(1) {
|
|
||||||
name "/fork_and_yieldAll"
|
|
||||||
kind SpanKind.INTERNAL
|
|
||||||
childOf span(0)
|
|
||||||
}
|
|
||||||
span(2) {
|
|
||||||
name "controller"
|
|
||||||
kind SpanKind.INTERNAL
|
|
||||||
childOf span(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,136 +5,13 @@
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.INDEXED_CHILD
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
|
||||||
|
|
||||||
import io.opentelemetry.api.trace.StatusCode
|
import io.opentelemetry.instrumentation.ratpack.server.AbstractRatpackHttpServerTest
|
||||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||||
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
|
import ratpack.server.RatpackServerSpec
|
||||||
import io.opentelemetry.instrumentation.test.base.HttpServerTest
|
|
||||||
import io.opentelemetry.sdk.trace.data.SpanData
|
|
||||||
import ratpack.error.ServerErrorHandler
|
|
||||||
import ratpack.handling.Context
|
|
||||||
import ratpack.server.RatpackServer
|
|
||||||
|
|
||||||
class RatpackHttpServerTest extends HttpServerTest<RatpackServer> implements AgentTestTrait {
|
|
||||||
|
|
||||||
|
class RatpackHttpServerTest extends AbstractRatpackHttpServerTest implements AgentTestTrait {
|
||||||
@Override
|
@Override
|
||||||
RatpackServer startServer(int bindPort) {
|
void configure(RatpackServerSpec serverSpec) {
|
||||||
def ratpack = RatpackServer.start {
|
|
||||||
it.serverConfig {
|
|
||||||
it.port(bindPort)
|
|
||||||
it.address(InetAddress.getByName("localhost"))
|
|
||||||
}
|
|
||||||
it.handlers {
|
|
||||||
it.register {
|
|
||||||
it.add(ServerErrorHandler, new TestErrorHandler())
|
|
||||||
}
|
|
||||||
it.prefix(SUCCESS.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
controller(SUCCESS) {
|
|
||||||
context.response.status(SUCCESS.status).send(SUCCESS.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(INDEXED_CHILD.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
controller(INDEXED_CHILD) {
|
|
||||||
INDEXED_CHILD.collectSpanAttributes { context.request.queryParams.get(it) }
|
|
||||||
context.response.status(INDEXED_CHILD.status).send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(QUERY_PARAM.rawPath()) {
|
|
||||||
it.all { context ->
|
|
||||||
controller(QUERY_PARAM) {
|
|
||||||
context.response.status(QUERY_PARAM.status).send(context.request.query)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(REDIRECT.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
controller(REDIRECT) {
|
|
||||||
context.redirect(REDIRECT.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(ERROR.rawPath()) {
|
|
||||||
it.all {context ->
|
|
||||||
controller(ERROR) {
|
|
||||||
context.response.status(ERROR.status).send(ERROR.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix(EXCEPTION.rawPath()) {
|
|
||||||
it.all {
|
|
||||||
controller(EXCEPTION) {
|
|
||||||
throw new Exception(EXCEPTION.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.prefix("path/:id/param") {
|
|
||||||
it.all {context ->
|
|
||||||
controller(PATH_PARAM) {
|
|
||||||
context.response.status(PATH_PARAM.status).send(context.pathTokens.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert ratpack.bindPort == 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(RatpackServer server) {
|
|
||||||
server.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean hasHandlerSpan(ServerEndpoint endpoint) {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean testPathParam() {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean testConcurrency() {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
|
|
||||||
trace.span(index) {
|
|
||||||
name endpoint.status == 404 ? "/" : endpoint == PATH_PARAM ? "/path/:id/param" : endpoint.path
|
|
||||||
kind INTERNAL
|
|
||||||
childOf((SpanData) parent)
|
|
||||||
if (endpoint == EXCEPTION) {
|
|
||||||
status StatusCode.ERROR
|
|
||||||
errorEvent(Exception, EXCEPTION.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String expectedServerSpanName(ServerEndpoint endpoint) {
|
|
||||||
return endpoint.status == 404 ? "/" : endpoint == PATH_PARAM ? "/path/:id/param" : endpoint.path
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package server
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.ratpack.server.AbstractRatpackRoutesTest
|
||||||
|
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||||
|
import ratpack.server.RatpackServerSpec
|
||||||
|
|
||||||
|
class RatpackRoutesTest extends AbstractRatpackRoutesTest implements AgentTestTrait {
|
||||||
|
@Override
|
||||||
|
void configure(RatpackServerSpec serverSpec) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean hasHandlerSpan() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
plugins {
|
||||||
|
id("otel.library-instrumentation")
|
||||||
|
id("otel.nullaway-conventions")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
library("io.ratpack:ratpack-core:1.4.0")
|
||||||
|
|
||||||
|
testImplementation(project(":instrumentation:ratpack-1.4:testing"))
|
||||||
|
|
||||||
|
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_11)) {
|
||||||
|
testImplementation("com.sun.activation:jakarta.activation:1.2.2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requires old Guava. Can't use enforcedPlatform since predates BOM
|
||||||
|
configurations.testRuntimeClasspath.resolutionStrategy.force("com.google.guava:guava:19.0")
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack;
|
||||||
|
|
||||||
|
import io.opentelemetry.context.Context;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import ratpack.exec.ExecInterceptor;
|
||||||
|
import ratpack.exec.Execution;
|
||||||
|
import ratpack.func.Block;
|
||||||
|
|
||||||
|
final class OpenTelemetryExecInterceptor implements ExecInterceptor {
|
||||||
|
|
||||||
|
static final ExecInterceptor INSTANCE = new OpenTelemetryExecInterceptor();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void intercept(Execution execution, ExecType type, Block continuation) throws Exception {
|
||||||
|
Context otelCtx = execution.maybeGet(Context.class).orElse(null);
|
||||||
|
if (otelCtx == null) {
|
||||||
|
// There is no OTel Context yet meaning this is the beginning of an Execution, before running
|
||||||
|
// the handler chain, which includes OpenTelemetryServerHandler. Run the chain.
|
||||||
|
executeHandlerChainAndThenCloseScope(execution, continuation);
|
||||||
|
} else {
|
||||||
|
// Execution already has a context, this is an asynchronous resumption and we need to make
|
||||||
|
// the context current.
|
||||||
|
executeContinuationWithContext(continuation, otelCtx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void executeHandlerChainAndThenCloseScope(Execution execution, Block continuation)
|
||||||
|
throws Exception {
|
||||||
|
try {
|
||||||
|
continuation.execute();
|
||||||
|
} finally {
|
||||||
|
// The handler chain, including OpenTelemetryServerHandler, has finished and we are about
|
||||||
|
// to unbind the Execution from its thread. As such, we need to make sure to close the
|
||||||
|
// thread-local Scope that was created by OpenTelemetryServerHandler. The Execution still
|
||||||
|
// has an OTel Context, so if it happens to resume because the user used an asynchronous
|
||||||
|
// flow, the interceptor will run again and instead make the context current by
|
||||||
|
// calling executeContinuationWithContext.
|
||||||
|
Scope scope = execution.maybeGet(Scope.class).orElse(null);
|
||||||
|
if (scope != null) {
|
||||||
|
scope.close();
|
||||||
|
execution.remove(Scope.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void executeContinuationWithContext(Block continuation, Context otelCtx)
|
||||||
|
throws Exception {
|
||||||
|
try (Scope ignored = otelCtx.makeCurrent()) {
|
||||||
|
continuation.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Includes work from:
|
||||||
|
/*
|
||||||
|
* Copyright 2014 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ratpack.error.ClientErrorHandler;
|
||||||
|
import ratpack.error.ServerErrorHandler;
|
||||||
|
import ratpack.handling.Context;
|
||||||
|
|
||||||
|
// Copied from
|
||||||
|
// https://github.com/ratpack/ratpack/blob/master/ratpack-core/src/main/java/ratpack/core/error/internal/DefaultProductionErrorHandler.java
|
||||||
|
// since it is internal and has had breaking changes.
|
||||||
|
final class OpenTelemetryFallbackErrorHandler implements ClientErrorHandler, ServerErrorHandler {
|
||||||
|
|
||||||
|
static final OpenTelemetryFallbackErrorHandler INSTANCE = new OpenTelemetryFallbackErrorHandler();
|
||||||
|
|
||||||
|
private static final Logger logger =
|
||||||
|
LoggerFactory.getLogger(OpenTelemetryFallbackErrorHandler.class);
|
||||||
|
|
||||||
|
OpenTelemetryFallbackErrorHandler() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(Context context, int statusCode) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
WarnOnce.execute();
|
||||||
|
logger.warn(getMsg(ClientErrorHandler.class, "client error", context));
|
||||||
|
}
|
||||||
|
context.getResponse().status(statusCode).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(Context context, Throwable throwable) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
WarnOnce.execute();
|
||||||
|
logger.warn(getMsg(ServerErrorHandler.class, "server error", context) + "\n", throwable);
|
||||||
|
}
|
||||||
|
context.getResponse().status(500).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getMsg(Class<?> handlerClass, String type, Context context) {
|
||||||
|
return "Default production error handler used to render "
|
||||||
|
+ type
|
||||||
|
+ ", please add a "
|
||||||
|
+ handlerClass.getName()
|
||||||
|
+ " instance to your application "
|
||||||
|
+ "(method: "
|
||||||
|
+ context.getRequest().getMethod()
|
||||||
|
+ ", uri: "
|
||||||
|
+ context.getRequest().getRawUri()
|
||||||
|
+ ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WarnOnce {
|
||||||
|
static {
|
||||||
|
logger.warn(
|
||||||
|
"Logging error using OpenTelemetryFallbackErrorHandler. This indicates "
|
||||||
|
+ "OpenTelemetry could not find a registered error handler which is not expected. "
|
||||||
|
+ "Log messages will only be outputed to console.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warned once in static initializer, this is just to trigger classload.
|
||||||
|
static void execute() {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack;
|
||||||
|
|
||||||
|
import ratpack.error.ServerErrorHandler;
|
||||||
|
import ratpack.handling.Context;
|
||||||
|
|
||||||
|
final class OpenTelemetryServerErrorHandler implements ServerErrorHandler {
|
||||||
|
|
||||||
|
static final ServerErrorHandler INSTANCE = new OpenTelemetryServerErrorHandler();
|
||||||
|
|
||||||
|
private OpenTelemetryServerErrorHandler() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(Context context, Throwable throwable) throws Exception {
|
||||||
|
context
|
||||||
|
.getExecution()
|
||||||
|
.add(
|
||||||
|
OpenTelemetryServerHandler.ErrorHolder.class,
|
||||||
|
new OpenTelemetryServerHandler.ErrorHolder(throwable));
|
||||||
|
|
||||||
|
ServerErrorHandler delegate = OpenTelemetryFallbackErrorHandler.INSTANCE;
|
||||||
|
for (ServerErrorHandler errorHandler : context.getAll(ServerErrorHandler.class)) {
|
||||||
|
if (errorHandler != INSTANCE) {
|
||||||
|
delegate = errorHandler;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate.error(context, throwable);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
|
import ratpack.error.ServerErrorHandler;
|
||||||
|
import ratpack.handling.Context;
|
||||||
|
import ratpack.handling.Handler;
|
||||||
|
import ratpack.http.Request;
|
||||||
|
import ratpack.http.Response;
|
||||||
|
|
||||||
|
final class OpenTelemetryServerHandler implements Handler {
|
||||||
|
|
||||||
|
private final Instrumenter<Request, Response> instrumenter;
|
||||||
|
|
||||||
|
OpenTelemetryServerHandler(Instrumenter<Request, Response> instrumenter) {
|
||||||
|
this.instrumenter = instrumenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(Context context) {
|
||||||
|
Request request = context.getRequest();
|
||||||
|
|
||||||
|
io.opentelemetry.context.Context parentOtelCtx = io.opentelemetry.context.Context.current();
|
||||||
|
if (!instrumenter.shouldStart(parentOtelCtx, request)) {
|
||||||
|
context.next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
io.opentelemetry.context.Context otelCtx = instrumenter.start(parentOtelCtx, request);
|
||||||
|
context.getExecution().add(io.opentelemetry.context.Context.class, otelCtx);
|
||||||
|
context.onClose(
|
||||||
|
outcome -> {
|
||||||
|
// Route not available in beginning of request so handle it manually here.
|
||||||
|
String route = '/' + context.getPathBinding().getDescription();
|
||||||
|
Span span = Span.fromContext(otelCtx);
|
||||||
|
span.updateName(route);
|
||||||
|
span.setAttribute(SemanticAttributes.HTTP_ROUTE, route);
|
||||||
|
|
||||||
|
Throwable error =
|
||||||
|
context.getExecution().maybeGet(ErrorHolder.class).map(ErrorHolder::get).orElse(null);
|
||||||
|
|
||||||
|
instrumenter.end(otelCtx, outcome.getRequest(), context.getResponse(), error);
|
||||||
|
});
|
||||||
|
|
||||||
|
// An execution continues to execute synchronously until it is unbound from a thread. We need
|
||||||
|
// to make the context current here to make it available to the next handler (possibly user
|
||||||
|
// code) but close the scope at the end of the ExecInterceptor, which corresponds to when the
|
||||||
|
// execution is about to be unbound from the thread.
|
||||||
|
Scope scope = otelCtx.makeCurrent();
|
||||||
|
context.getExecution().add(Scope.class, scope);
|
||||||
|
|
||||||
|
// A user may have defined their own ServerErrorHandler, so we add ours to the Execution which
|
||||||
|
// has higher precedence.
|
||||||
|
context.getExecution().add(ServerErrorHandler.class, OpenTelemetryServerErrorHandler.INSTANCE);
|
||||||
|
context.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class ErrorHolder {
|
||||||
|
private final Throwable throwable;
|
||||||
|
|
||||||
|
ErrorHolder(Throwable throwable) {
|
||||||
|
this.throwable = throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
Throwable get() {
|
||||||
|
return throwable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack;
|
||||||
|
|
||||||
|
import io.opentelemetry.context.propagation.TextMapGetter;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import ratpack.http.Request;
|
||||||
|
|
||||||
|
final class RatpackGetter implements TextMapGetter<Request> {
|
||||||
|
|
||||||
|
RatpackGetter() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<String> keys(Request request) {
|
||||||
|
return request.getHeaders().getNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String get(@Nullable Request request, String key) {
|
||||||
|
if (request == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return request.getHeaders().get(key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import ratpack.handling.Context;
|
||||||
|
import ratpack.http.Request;
|
||||||
|
import ratpack.http.Response;
|
||||||
|
import ratpack.server.PublicAddress;
|
||||||
|
|
||||||
|
final class RatpackHttpAttributesExtractor extends HttpAttributesExtractor<Request, Response> {
|
||||||
|
@Override
|
||||||
|
protected String method(Request request) {
|
||||||
|
return request.getMethod().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected String url(Request request) {
|
||||||
|
// TODO(anuraaga): We should probably just not fill this
|
||||||
|
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/3700
|
||||||
|
Context ratpackContext = request.get(Context.class);
|
||||||
|
if (ratpackContext == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PublicAddress publicAddress = ratpackContext.get(PublicAddress.class);
|
||||||
|
if (publicAddress == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return publicAddress
|
||||||
|
.builder()
|
||||||
|
.path(request.getPath())
|
||||||
|
.params(request.getQueryParams())
|
||||||
|
.build()
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String target(Request request) {
|
||||||
|
// Uri is the path + query string, not a full URL
|
||||||
|
return request.getUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected String host(Request request) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected String route(Request request) {
|
||||||
|
// Ratpack route not available at the beginning of request.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected String scheme(Request request) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected String userAgent(Request request) {
|
||||||
|
return request.getHeaders().get("user-agent");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected Long requestContentLength(Request request, @Nullable Response response) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected Long requestContentLengthUncompressed(Request request, @Nullable Response response) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected String flavor(Request request, @Nullable Response response) {
|
||||||
|
switch (request.getProtocol()) {
|
||||||
|
case "HTTP/1.0":
|
||||||
|
return SemanticAttributes.HttpFlavorValues.HTTP_1_0;
|
||||||
|
case "HTTP/1.1":
|
||||||
|
return SemanticAttributes.HttpFlavorValues.HTTP_1_1;
|
||||||
|
case "HTTP/2.0":
|
||||||
|
return SemanticAttributes.HttpFlavorValues.HTTP_2_0;
|
||||||
|
default:
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected String serverName(Request request, @Nullable Response response) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected String clientIp(Request request, @Nullable Response response) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer statusCode(Request request, Response response) {
|
||||||
|
return response.getStatus().getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected Long responseContentLength(Request request, Response response) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected Long responseContentLengthUncompressed(Request request, Response response) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.OpenTelemetry;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||||
|
import ratpack.handling.HandlerDecorator;
|
||||||
|
import ratpack.http.Request;
|
||||||
|
import ratpack.http.Response;
|
||||||
|
import ratpack.registry.RegistrySpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entrypoint for tracing Ratpack servers. To apply OpenTelemetry to a server, configure the {@link
|
||||||
|
* RegistrySpec} using {@link #configureServerRegistry(RegistrySpec)}.
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* RatpackTracing tracing = RatpackTracing.create(OpenTelemetrySdk.builder()
|
||||||
|
* ...
|
||||||
|
* .build());
|
||||||
|
* RatpackServer.start(server -> {
|
||||||
|
* server.registryOf(tracing::configureServerRegistry);
|
||||||
|
* server.handlers(chain -> ...);
|
||||||
|
* });
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
public final class RatpackTracing {
|
||||||
|
|
||||||
|
/** Returns a new {@link RatpackTracing} configured with the given {@link OpenTelemetry}. */
|
||||||
|
public static RatpackTracing create(OpenTelemetry openTelemetry) {
|
||||||
|
return newBuilder(openTelemetry).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link RatpackTracingBuilder} configured with the given {@link OpenTelemetry}.
|
||||||
|
*/
|
||||||
|
public static RatpackTracingBuilder newBuilder(OpenTelemetry openTelemetry) {
|
||||||
|
return new RatpackTracingBuilder(openTelemetry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final OpenTelemetryServerHandler serverHandler;
|
||||||
|
|
||||||
|
RatpackTracing(Instrumenter<Request, Response> serverInstrumenter) {
|
||||||
|
serverHandler = new OpenTelemetryServerHandler(serverInstrumenter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Configures the {@link RegistrySpec} with OpenTelemetry. */
|
||||||
|
public void configureServerRegistry(RegistrySpec registry) {
|
||||||
|
registry.add(HandlerDecorator.prepend(serverHandler));
|
||||||
|
registry.add(OpenTelemetryExecInterceptor.INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.OpenTelemetry;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
|
||||||
|
import io.opentelemetry.instrumentation.ratpack.internal.RatpackNetAttributesExtractor;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import ratpack.http.Request;
|
||||||
|
import ratpack.http.Response;
|
||||||
|
|
||||||
|
/** A builder for {@link RatpackTracing}. */
|
||||||
|
public final class RatpackTracingBuilder {
|
||||||
|
|
||||||
|
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.ratpack-1.4";
|
||||||
|
|
||||||
|
private final OpenTelemetry openTelemetry;
|
||||||
|
|
||||||
|
private final List<AttributesExtractor<? super Request, ? super Response>> additionalExtractors =
|
||||||
|
new ArrayList<>();
|
||||||
|
|
||||||
|
RatpackTracingBuilder(OpenTelemetry openTelemetry) {
|
||||||
|
this.openTelemetry = openTelemetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented
|
||||||
|
* items. The {@link AttributesExtractor} will be executed after all default extractors.
|
||||||
|
*/
|
||||||
|
public RatpackTracingBuilder addAttributeExtractor(
|
||||||
|
AttributesExtractor<? super Request, ? super Response> attributesExtractor) {
|
||||||
|
additionalExtractors.add(attributesExtractor);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a new {@link RatpackTracing} with the configuration of this builder. */
|
||||||
|
public RatpackTracing build() {
|
||||||
|
RatpackNetAttributesExtractor netAttributes = new RatpackNetAttributesExtractor();
|
||||||
|
RatpackHttpAttributesExtractor httpAttributes = new RatpackHttpAttributesExtractor();
|
||||||
|
|
||||||
|
InstrumenterBuilder<Request, Response> builder =
|
||||||
|
Instrumenter.newBuilder(
|
||||||
|
openTelemetry, INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributes));
|
||||||
|
|
||||||
|
builder.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributes));
|
||||||
|
builder.addAttributesExtractor(netAttributes);
|
||||||
|
builder.addAttributesExtractor(httpAttributes);
|
||||||
|
builder.addAttributesExtractors(additionalExtractors);
|
||||||
|
|
||||||
|
return new RatpackTracing(builder.newServerInstrumenter(new RatpackGetter()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack.internal;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor;
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import ratpack.http.Request;
|
||||||
|
import ratpack.http.Response;
|
||||||
|
|
||||||
|
public final class RatpackNetAttributesExtractor extends NetAttributesExtractor<Request, Response> {
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public String transport(Request request) {
|
||||||
|
return SemanticAttributes.NetTransportValues.IP_TCP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public String peerName(Request request, @Nullable Response response) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer peerPort(Request request, @Nullable Response response) {
|
||||||
|
return request.getRemoteAddress().getPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public String peerIp(Request request, @Nullable Response response) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack.server
|
||||||
|
|
||||||
|
import io.opentelemetry.api.common.AttributeKey
|
||||||
|
import io.opentelemetry.instrumentation.ratpack.RatpackTracing
|
||||||
|
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||||
|
import ratpack.server.RatpackServerSpec
|
||||||
|
|
||||||
|
class RatpackAsyncHttpServerTest extends AbstractRatpackAsyncHttpServerTest implements LibraryTestTrait {
|
||||||
|
@Override
|
||||||
|
void configure(RatpackServerSpec serverSpec) {
|
||||||
|
RatpackTracing tracing = RatpackTracing.create(openTelemetry)
|
||||||
|
serverSpec.registryOf {
|
||||||
|
tracing.configureServerRegistry(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
boolean hasHandlerSpan(ServerEndpoint endpoint) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<AttributeKey<?>> extraAttributes() {
|
||||||
|
return [
|
||||||
|
SemanticAttributes.HTTP_ROUTE,
|
||||||
|
SemanticAttributes.HTTP_TARGET,
|
||||||
|
SemanticAttributes.NET_TRANSPORT,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack.server
|
||||||
|
|
||||||
|
import io.opentelemetry.api.common.AttributeKey
|
||||||
|
import io.opentelemetry.instrumentation.ratpack.RatpackTracing
|
||||||
|
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||||
|
import ratpack.server.RatpackServerSpec
|
||||||
|
|
||||||
|
class RatpackForkedHttpServerTest extends AbstractRatpackForkedHttpServerTest implements LibraryTestTrait {
|
||||||
|
@Override
|
||||||
|
void configure(RatpackServerSpec serverSpec) {
|
||||||
|
RatpackTracing tracing = RatpackTracing.create(openTelemetry)
|
||||||
|
serverSpec.registryOf {
|
||||||
|
tracing.configureServerRegistry(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
boolean hasHandlerSpan(ServerEndpoint endpoint) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<AttributeKey<?>> extraAttributes() {
|
||||||
|
return [
|
||||||
|
SemanticAttributes.HTTP_ROUTE,
|
||||||
|
SemanticAttributes.HTTP_TARGET,
|
||||||
|
SemanticAttributes.NET_TRANSPORT,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack.server
|
||||||
|
|
||||||
|
import io.opentelemetry.api.common.AttributeKey
|
||||||
|
import io.opentelemetry.instrumentation.ratpack.RatpackTracing
|
||||||
|
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||||
|
import ratpack.server.RatpackServerSpec
|
||||||
|
|
||||||
|
class RatpackHttpServerTest extends AbstractRatpackHttpServerTest implements LibraryTestTrait {
|
||||||
|
@Override
|
||||||
|
void configure(RatpackServerSpec serverSpec) {
|
||||||
|
RatpackTracing tracing = RatpackTracing.create(openTelemetry)
|
||||||
|
serverSpec.registryOf {
|
||||||
|
tracing.configureServerRegistry(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean hasHandlerSpan(ServerEndpoint endpoint) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<AttributeKey<?>> extraAttributes() {
|
||||||
|
return [
|
||||||
|
SemanticAttributes.HTTP_ROUTE,
|
||||||
|
SemanticAttributes.HTTP_TARGET,
|
||||||
|
SemanticAttributes.NET_TRANSPORT,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack.server
|
||||||
|
|
||||||
|
import io.opentelemetry.api.common.AttributeKey
|
||||||
|
import io.opentelemetry.instrumentation.ratpack.RatpackTracing
|
||||||
|
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||||
|
import ratpack.server.RatpackServerSpec
|
||||||
|
|
||||||
|
class RatpackRoutesTest extends AbstractRatpackRoutesTest implements LibraryTestTrait {
|
||||||
|
@Override
|
||||||
|
void configure(RatpackServerSpec serverSpec) {
|
||||||
|
RatpackTracing tracing = RatpackTracing.create(openTelemetry)
|
||||||
|
serverSpec.registryOf {
|
||||||
|
tracing.configureServerRegistry(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean hasHandlerSpan() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<AttributeKey<?>> extraAttributes() {
|
||||||
|
return [
|
||||||
|
SemanticAttributes.HTTP_ROUTE,
|
||||||
|
SemanticAttributes.HTTP_TARGET,
|
||||||
|
SemanticAttributes.NET_TRANSPORT,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
plugins {
|
||||||
|
id("otel.java-conventions")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(project(":testing-common"))
|
||||||
|
|
||||||
|
api("io.ratpack:ratpack-core:1.4.0")
|
||||||
|
|
||||||
|
implementation("org.codehaus.groovy:groovy-all")
|
||||||
|
implementation("io.opentelemetry:opentelemetry-api")
|
||||||
|
implementation("org.spockframework:spock-core")
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack.server
|
||||||
|
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.INDEXED_CHILD
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||||
|
|
||||||
|
import ratpack.error.ServerErrorHandler
|
||||||
|
import ratpack.exec.Promise
|
||||||
|
import ratpack.server.RatpackServer
|
||||||
|
|
||||||
|
abstract class AbstractRatpackAsyncHttpServerTest extends AbstractRatpackHttpServerTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
RatpackServer startServer(int bindPort) {
|
||||||
|
def ratpack = RatpackServer.start {
|
||||||
|
it.serverConfig {
|
||||||
|
it.port(bindPort)
|
||||||
|
it.address(InetAddress.getByName("localhost"))
|
||||||
|
}
|
||||||
|
it.handlers {
|
||||||
|
it.register {
|
||||||
|
it.add(ServerErrorHandler, new TestErrorHandler())
|
||||||
|
}
|
||||||
|
it.prefix(SUCCESS.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
Promise.sync {
|
||||||
|
SUCCESS
|
||||||
|
} then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
context.response.status(endpoint.status).send(endpoint.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(INDEXED_CHILD.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
Promise.sync {
|
||||||
|
INDEXED_CHILD
|
||||||
|
} then {
|
||||||
|
controller(INDEXED_CHILD) {
|
||||||
|
INDEXED_CHILD.collectSpanAttributes { context.request.queryParams.get(it) }
|
||||||
|
context.response.status(INDEXED_CHILD.status).send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(QUERY_PARAM.rawPath()) {
|
||||||
|
it.all { context ->
|
||||||
|
Promise.sync {
|
||||||
|
QUERY_PARAM
|
||||||
|
} then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
context.response.status(endpoint.status).send(context.request.query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(REDIRECT.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
Promise.sync {
|
||||||
|
REDIRECT
|
||||||
|
} then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
context.redirect(endpoint.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(ERROR.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
Promise.sync {
|
||||||
|
ERROR
|
||||||
|
} then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
context.response.status(endpoint.status).send(endpoint.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(EXCEPTION.rawPath()) {
|
||||||
|
it.all {
|
||||||
|
Promise.sync {
|
||||||
|
EXCEPTION
|
||||||
|
} then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
throw new Exception(endpoint.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix("path/:id/param") {
|
||||||
|
it.all {context ->
|
||||||
|
Promise.sync {
|
||||||
|
PATH_PARAM
|
||||||
|
} then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
context.response.status(endpoint.status).send(context.pathTokens.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configure(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert ratpack.bindPort == bindPort
|
||||||
|
assert ratpack.bindHost == 'localhost'
|
||||||
|
return ratpack
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack.server
|
||||||
|
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.INDEXED_CHILD
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.SpanKind
|
||||||
|
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest
|
||||||
|
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse
|
||||||
|
import io.opentelemetry.testing.internal.armeria.common.HttpMethod
|
||||||
|
import ratpack.error.ServerErrorHandler
|
||||||
|
import ratpack.exec.Execution
|
||||||
|
import ratpack.exec.Promise
|
||||||
|
import ratpack.exec.Result
|
||||||
|
import ratpack.exec.util.ParallelBatch
|
||||||
|
import ratpack.server.RatpackServer
|
||||||
|
|
||||||
|
abstract class AbstractRatpackForkedHttpServerTest extends AbstractRatpackHttpServerTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
RatpackServer startServer(int bindPort) {
|
||||||
|
|
||||||
|
def ratpack = RatpackServer.start {
|
||||||
|
it.serverConfig {
|
||||||
|
it.port(bindPort)
|
||||||
|
it.address(InetAddress.getByName("localhost"))
|
||||||
|
}
|
||||||
|
it.handlers {
|
||||||
|
it.register {
|
||||||
|
it.add(ServerErrorHandler, new TestErrorHandler())
|
||||||
|
}
|
||||||
|
it.prefix(SUCCESS.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
Promise.sync {
|
||||||
|
SUCCESS
|
||||||
|
}.fork().then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
context.response.status(endpoint.status).send(endpoint.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(INDEXED_CHILD.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
Promise.sync {
|
||||||
|
INDEXED_CHILD
|
||||||
|
}.fork().then {
|
||||||
|
controller(INDEXED_CHILD) {
|
||||||
|
INDEXED_CHILD.collectSpanAttributes { context.request.queryParams.get(it) }
|
||||||
|
context.response.status(INDEXED_CHILD.status).send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(QUERY_PARAM.rawPath()) {
|
||||||
|
it.all { context ->
|
||||||
|
Promise.sync {
|
||||||
|
QUERY_PARAM
|
||||||
|
}.fork().then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
context.response.status(endpoint.status).send(context.request.query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(REDIRECT.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
Promise.sync {
|
||||||
|
REDIRECT
|
||||||
|
}.fork().then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
context.redirect(endpoint.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(ERROR.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
Promise.sync {
|
||||||
|
ERROR
|
||||||
|
}.fork().then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
context.response.status(endpoint.status).send(endpoint.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(EXCEPTION.rawPath()) {
|
||||||
|
it.all {
|
||||||
|
Promise.sync {
|
||||||
|
EXCEPTION
|
||||||
|
}.fork().then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
throw new Exception(endpoint.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix("path/:id/param") {
|
||||||
|
it.all {context ->
|
||||||
|
Promise.sync {
|
||||||
|
PATH_PARAM
|
||||||
|
}.fork().then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
context.response.status(endpoint.status).send(context.pathTokens.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix("fork_and_yieldAll") {
|
||||||
|
it.all {context ->
|
||||||
|
def promise = Promise.async { upstream ->
|
||||||
|
Execution.fork().start({
|
||||||
|
upstream.accept(Result.success(SUCCESS))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ParallelBatch.of(promise).yieldAll().flatMap { list ->
|
||||||
|
Promise.sync { list.get(0).value }
|
||||||
|
} then { endpoint ->
|
||||||
|
controller(endpoint) {
|
||||||
|
context.response.status(endpoint.status).send(endpoint.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configure(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert ratpack.bindPort == bindPort
|
||||||
|
assert ratpack.bindHost == 'localhost'
|
||||||
|
return ratpack
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test fork and yieldAll"() {
|
||||||
|
setup:
|
||||||
|
def url = address.resolve("fork_and_yieldAll").toString()
|
||||||
|
url = url.replace("http://", "h1c://")
|
||||||
|
def request = AggregatedHttpRequest.of(HttpMethod.GET, url)
|
||||||
|
AggregatedHttpResponse response = client.execute(request).aggregate().join()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
response.status().code() == SUCCESS.status
|
||||||
|
response.contentUtf8() == SUCCESS.body
|
||||||
|
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 2 + (hasHandlerSpan(SUCCESS) ? 1 : 0)) {
|
||||||
|
span(0) {
|
||||||
|
name "/fork_and_yieldAll"
|
||||||
|
kind SpanKind.SERVER
|
||||||
|
hasNoParent()
|
||||||
|
}
|
||||||
|
if (hasHandlerSpan(SUCCESS)) {
|
||||||
|
span(1) {
|
||||||
|
name "/fork_and_yieldAll"
|
||||||
|
kind SpanKind.INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "controller"
|
||||||
|
kind SpanKind.INTERNAL
|
||||||
|
childOf span(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
span(1) {
|
||||||
|
name "controller"
|
||||||
|
kind SpanKind.INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack.server
|
||||||
|
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.INDEXED_CHILD
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
||||||
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.StatusCode
|
||||||
|
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
|
||||||
|
import io.opentelemetry.instrumentation.test.base.HttpServerTest
|
||||||
|
import io.opentelemetry.sdk.trace.data.SpanData
|
||||||
|
import ratpack.error.ServerErrorHandler
|
||||||
|
import ratpack.handling.Context
|
||||||
|
import ratpack.server.RatpackServer
|
||||||
|
import ratpack.server.RatpackServerSpec
|
||||||
|
|
||||||
|
abstract class AbstractRatpackHttpServerTest extends HttpServerTest<RatpackServer> {
|
||||||
|
|
||||||
|
abstract void configure(RatpackServerSpec serverSpec)
|
||||||
|
|
||||||
|
@Override
|
||||||
|
RatpackServer startServer(int bindPort) {
|
||||||
|
def ratpack = RatpackServer.start {
|
||||||
|
it.serverConfig {
|
||||||
|
it.port(bindPort)
|
||||||
|
it.address(InetAddress.getByName("localhost"))
|
||||||
|
}
|
||||||
|
it.handlers {
|
||||||
|
it.register {
|
||||||
|
it.add(ServerErrorHandler, new TestErrorHandler())
|
||||||
|
}
|
||||||
|
it.prefix(SUCCESS.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
controller(SUCCESS) {
|
||||||
|
context.response.status(SUCCESS.status).send(SUCCESS.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(INDEXED_CHILD.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
controller(INDEXED_CHILD) {
|
||||||
|
INDEXED_CHILD.collectSpanAttributes { context.request.queryParams.get(it) }
|
||||||
|
context.response.status(INDEXED_CHILD.status).send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(QUERY_PARAM.rawPath()) {
|
||||||
|
it.all { context ->
|
||||||
|
controller(QUERY_PARAM) {
|
||||||
|
context.response.status(QUERY_PARAM.status).send(context.request.query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(REDIRECT.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
controller(REDIRECT) {
|
||||||
|
context.redirect(REDIRECT.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(ERROR.rawPath()) {
|
||||||
|
it.all {context ->
|
||||||
|
controller(ERROR) {
|
||||||
|
context.response.status(ERROR.status).send(ERROR.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix(EXCEPTION.rawPath()) {
|
||||||
|
it.all {
|
||||||
|
controller(EXCEPTION) {
|
||||||
|
throw new Exception(EXCEPTION.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix("path/:id/param") {
|
||||||
|
it.all {context ->
|
||||||
|
controller(PATH_PARAM) {
|
||||||
|
context.response.status(PATH_PARAM.status).send(context.pathTokens.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configure(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert ratpack.bindPort == bindPort
|
||||||
|
return ratpack
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(anuraaga): The default Ratpack error handler also returns a 500 which is all we test, so
|
||||||
|
// we don't actually have test coverage ensuring our instrumentation correctly delegates to this
|
||||||
|
// user registered handler.
|
||||||
|
static class TestErrorHandler implements ServerErrorHandler {
|
||||||
|
@Override
|
||||||
|
void error(Context context, Throwable throwable) throws Exception {
|
||||||
|
context.response.status(500).send(throwable.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void stopServer(RatpackServer server) {
|
||||||
|
server.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean hasHandlerSpan(ServerEndpoint endpoint) {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean testPathParam() {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean testConcurrency() {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
|
||||||
|
trace.span(index) {
|
||||||
|
name endpoint.status == 404 ? "/" : endpoint == PATH_PARAM ? "/path/:id/param" : endpoint.path
|
||||||
|
kind INTERNAL
|
||||||
|
childOf((SpanData) parent)
|
||||||
|
if (endpoint == EXCEPTION) {
|
||||||
|
status StatusCode.ERROR
|
||||||
|
errorEvent(Exception, EXCEPTION.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String expectedServerSpanName(ServerEndpoint endpoint) {
|
||||||
|
return endpoint.status == 404 ? "/" : endpoint == PATH_PARAM ? "/path/:id/param" : endpoint.path
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ratpack.server
|
||||||
|
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.SERVER
|
||||||
|
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP
|
||||||
|
|
||||||
|
import io.opentelemetry.api.common.AttributeKey
|
||||||
|
import io.opentelemetry.instrumentation.test.InstrumentationSpecification
|
||||||
|
import io.opentelemetry.instrumentation.test.utils.PortUtils
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||||
|
import io.opentelemetry.testing.internal.armeria.client.WebClient
|
||||||
|
import ratpack.path.PathBinding
|
||||||
|
import ratpack.server.RatpackServer
|
||||||
|
import ratpack.server.RatpackServerSpec
|
||||||
|
import spock.lang.Shared
|
||||||
|
import spock.lang.Unroll
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
abstract class AbstractRatpackRoutesTest extends InstrumentationSpecification {
|
||||||
|
|
||||||
|
abstract void configure(RatpackServerSpec serverSpec)
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
RatpackServer app
|
||||||
|
|
||||||
|
// Force HTTP/1 with h1c to prevent tracing of upgrade request.
|
||||||
|
@Shared
|
||||||
|
WebClient client
|
||||||
|
|
||||||
|
def setupSpec() {
|
||||||
|
app = RatpackServer.start {
|
||||||
|
it.serverConfig {
|
||||||
|
it.port(PortUtils.findOpenPort())
|
||||||
|
it.address(InetAddress.getByName("localhost"))
|
||||||
|
}
|
||||||
|
it.handlers {
|
||||||
|
it.prefix("a") {
|
||||||
|
it.all { context ->
|
||||||
|
context.render(context.get(PathBinding).description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix("b/::\\d+") {
|
||||||
|
it.all { context ->
|
||||||
|
context.render(context.get(PathBinding).description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix("c/:val?") {
|
||||||
|
it.all { context ->
|
||||||
|
context.render(context.get(PathBinding).description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix("d/:val") {
|
||||||
|
it.all { context ->
|
||||||
|
context.render(context.get(PathBinding).description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix("e/:val?:\\d+") {
|
||||||
|
it.all { context ->
|
||||||
|
context.render(context.get(PathBinding).description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.prefix("f/:val:\\d+") {
|
||||||
|
it.all { context ->
|
||||||
|
context.render(context.get(PathBinding).description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configure(it)
|
||||||
|
}
|
||||||
|
client = WebClient.of("h1c://localhost:${app.bindPort}")
|
||||||
|
}
|
||||||
|
|
||||||
|
def cleanupSpec() {
|
||||||
|
app.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract boolean hasHandlerSpan()
|
||||||
|
|
||||||
|
List<AttributeKey<?>> extraAttributes() {
|
||||||
|
[]
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test bindings for #path"() {
|
||||||
|
when:
|
||||||
|
def resp = client.get(path).aggregate().join()
|
||||||
|
|
||||||
|
then:
|
||||||
|
resp.status().code() == 200
|
||||||
|
resp.contentUtf8() == route
|
||||||
|
|
||||||
|
def extraAttributes = extraAttributes()
|
||||||
|
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1 + (hasHandlerSpan() ? 1 : 0)) {
|
||||||
|
span(0) {
|
||||||
|
name "/$route"
|
||||||
|
kind SERVER
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
"${SemanticAttributes.NET_PEER_IP.key}" { it == null || it == "127.0.0.1" }
|
||||||
|
"${SemanticAttributes.NET_PEER_PORT.key}" Long
|
||||||
|
"${SemanticAttributes.HTTP_URL.key}" "http://localhost:${app.bindPort}/${path}"
|
||||||
|
"${SemanticAttributes.HTTP_METHOD.key}" "GET"
|
||||||
|
"${SemanticAttributes.HTTP_STATUS_CODE.key}" 200
|
||||||
|
"${SemanticAttributes.HTTP_FLAVOR.key}" "1.1"
|
||||||
|
"${SemanticAttributes.HTTP_USER_AGENT.key}" String
|
||||||
|
"${SemanticAttributes.HTTP_CLIENT_IP.key}" { it == null || it == "127.0.0.1" }
|
||||||
|
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_HOST)) {
|
||||||
|
"${SemanticAttributes.HTTP_HOST}" "localhost:${app.bindPort}"
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH)) {
|
||||||
|
"${SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH}" Long
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH)) {
|
||||||
|
"${SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH}" Long
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_ROUTE)) {
|
||||||
|
// TODO(anuraaga): Revisit this when applying instrumenters to more libraries, Armeria
|
||||||
|
// currently reports '/*' which is a fallback route.
|
||||||
|
"${SemanticAttributes.HTTP_ROUTE}" String
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_SCHEME)) {
|
||||||
|
"${SemanticAttributes.HTTP_SCHEME}" "http"
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_SERVER_NAME)) {
|
||||||
|
"${SemanticAttributes.HTTP_SERVER_NAME}" String
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_TARGET)) {
|
||||||
|
"${SemanticAttributes.HTTP_TARGET}" "/$path"
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.NET_PEER_NAME)) {
|
||||||
|
"${SemanticAttributes.NET_PEER_NAME}" "localhost"
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.NET_TRANSPORT)) {
|
||||||
|
"${SemanticAttributes.NET_TRANSPORT}" IP_TCP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasHandlerSpan()) {
|
||||||
|
span(1) {
|
||||||
|
name "/$route"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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+"
|
||||||
|
}
|
||||||
|
}
|
|
@ -260,6 +260,8 @@ include(":instrumentation:play-ws:play-ws-common:javaagent")
|
||||||
include(":instrumentation:play-ws:play-ws-testing")
|
include(":instrumentation:play-ws:play-ws-testing")
|
||||||
include(":instrumentation:rabbitmq-2.7:javaagent")
|
include(":instrumentation:rabbitmq-2.7:javaagent")
|
||||||
include(":instrumentation:ratpack-1.4:javaagent")
|
include(":instrumentation:ratpack-1.4:javaagent")
|
||||||
|
include(":instrumentation:ratpack-1.4:library")
|
||||||
|
include(":instrumentation:ratpack-1.4:testing")
|
||||||
include(":instrumentation:reactor-3.1:javaagent")
|
include(":instrumentation:reactor-3.1:javaagent")
|
||||||
include(":instrumentation:reactor-3.1:library")
|
include(":instrumentation:reactor-3.1:library")
|
||||||
include(":instrumentation:reactor-3.1:testing")
|
include(":instrumentation:reactor-3.1:testing")
|
||||||
|
|
|
@ -608,6 +608,7 @@ abstract class HttpServerTest<SERVER> extends InstrumentationSpecification imple
|
||||||
}
|
}
|
||||||
|
|
||||||
void indexedServerSpan(TraceAssert trace, Object parent, int requestId) {
|
void indexedServerSpan(TraceAssert trace, Object parent, int requestId) {
|
||||||
|
def extraAttributes = extraAttributes()
|
||||||
ServerEndpoint endpoint = INDEXED_CHILD
|
ServerEndpoint endpoint = INDEXED_CHILD
|
||||||
trace.span(1) {
|
trace.span(1) {
|
||||||
name expectedServerSpanName(endpoint)
|
name expectedServerSpanName(endpoint)
|
||||||
|
@ -622,6 +623,36 @@ abstract class HttpServerTest<SERVER> extends InstrumentationSpecification imple
|
||||||
"${SemanticAttributes.HTTP_STATUS_CODE.key}" 200
|
"${SemanticAttributes.HTTP_STATUS_CODE.key}" 200
|
||||||
"${SemanticAttributes.HTTP_FLAVOR.key}" "1.1"
|
"${SemanticAttributes.HTTP_FLAVOR.key}" "1.1"
|
||||||
"${SemanticAttributes.HTTP_USER_AGENT.key}" TEST_USER_AGENT
|
"${SemanticAttributes.HTTP_USER_AGENT.key}" TEST_USER_AGENT
|
||||||
|
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_HOST)) {
|
||||||
|
"${SemanticAttributes.HTTP_HOST}" "localhost:${port}"
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH)) {
|
||||||
|
"${SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH}" Long
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH)) {
|
||||||
|
"${SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH}" Long
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_ROUTE)) {
|
||||||
|
// TODO(anuraaga): Revisit this when applying instrumenters to more libraries, Armeria
|
||||||
|
// currently reports '/*' which is a fallback route.
|
||||||
|
"${SemanticAttributes.HTTP_ROUTE}" String
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_SCHEME)) {
|
||||||
|
"${SemanticAttributes.HTTP_SCHEME}" "http"
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_SERVER_NAME)) {
|
||||||
|
"${SemanticAttributes.HTTP_SERVER_NAME}" String
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.HTTP_TARGET)) {
|
||||||
|
"${SemanticAttributes.HTTP_TARGET}" endpoint.path + "?id=$requestId"
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.NET_PEER_NAME)) {
|
||||||
|
"${SemanticAttributes.NET_PEER_NAME}" "localhost"
|
||||||
|
}
|
||||||
|
if (extraAttributes.contains(SemanticAttributes.NET_TRANSPORT)) {
|
||||||
|
"${SemanticAttributes.NET_TRANSPORT}" IP_TCP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue