Rename ktor tracing to ktor telemetry (#12855)

This commit is contained in:
Trask Stalnaker 2024-12-16 19:50:28 -08:00 committed by GitHub
parent b08d272e23
commit 9865c17db8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
64 changed files with 1297 additions and 99 deletions

View File

@ -1,6 +1,7 @@
# Library Instrumentation for Ktor version 1.x
This package contains libraries to help instrument Ktor. Currently, only server instrumentation is supported.
This package contains libraries to help instrument Ktor.
Currently, only server instrumentation is supported.
## Quickstart
@ -29,14 +30,14 @@ implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-1.0:OPENTELE
## Usage
Initialize instrumentation by installing the `KtorServerTracing` feature. You must set the `OpenTelemetry` to use with
the feature.
Initialize instrumentation by installing the `KtorServerTelemetry` feature.
You must set the `OpenTelemetry` to use with the feature.
```kotlin
OpenTelemetry openTelemetry = ...
embeddedServer(Netty, 8080) {
install(KtorServerTracing) {
install(KtorServerTelemetry) {
setOpenTelemetry(openTelemetry)
}
}

View File

@ -0,0 +1,169 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v1_0
import io.ktor.application.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.util.*
import io.ktor.util.pipeline.*
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.context.Context
import io.opentelemetry.extension.kotlin.asContextElement
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor
import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource
import kotlinx.coroutines.withContext
class KtorServerTelemetry private constructor(
private val instrumenter: Instrumenter<ApplicationRequest, ApplicationResponse>,
) {
class Configuration {
internal lateinit var builder: DefaultHttpServerInstrumenterBuilder<ApplicationRequest, ApplicationResponse>
internal var spanKindExtractor:
(SpanKindExtractor<ApplicationRequest>) -> SpanKindExtractor<ApplicationRequest> = { a -> a }
fun setOpenTelemetry(openTelemetry: OpenTelemetry) {
this.builder =
DefaultHttpServerInstrumenterBuilder.create(
INSTRUMENTATION_NAME,
openTelemetry,
KtorHttpServerAttributesGetter.INSTANCE
)
}
fun setStatusExtractor(
extractor: (SpanStatusExtractor<in ApplicationRequest, in ApplicationResponse>) -> SpanStatusExtractor<in ApplicationRequest, in ApplicationResponse>
) {
builder.setStatusExtractor { prevExtractor ->
SpanStatusExtractor { spanStatusBuilder: SpanStatusBuilder,
request: ApplicationRequest,
response: ApplicationResponse?,
throwable: Throwable? ->
extractor(prevExtractor).extract(spanStatusBuilder, request, response, throwable)
}
}
}
fun setSpanKindExtractor(extractor: (SpanKindExtractor<ApplicationRequest>) -> SpanKindExtractor<ApplicationRequest>) {
this.spanKindExtractor = extractor
}
fun addAttributesExtractor(extractor: AttributesExtractor<in ApplicationRequest, in ApplicationResponse>) {
builder.addAttributesExtractor(extractor)
}
fun setCapturedRequestHeaders(requestHeaders: List<String>) {
builder.setCapturedRequestHeaders(requestHeaders)
}
fun setCapturedResponseHeaders(responseHeaders: List<String>) {
builder.setCapturedResponseHeaders(responseHeaders)
}
fun setKnownMethods(knownMethods: Set<String>) {
builder.setKnownMethods(knownMethods)
}
internal fun isOpenTelemetryInitialized(): Boolean = this::builder.isInitialized
}
private fun start(call: ApplicationCall): Context? {
val parentContext = Context.current()
if (!instrumenter.shouldStart(parentContext, call.request)) {
return null
}
return instrumenter.start(parentContext, call.request)
}
private fun end(context: Context, call: ApplicationCall, error: Throwable?) {
instrumenter.end(context, call.request, call.response, error)
}
companion object Feature : ApplicationFeature<Application, Configuration, KtorServerTelemetry> {
private const val INSTRUMENTATION_NAME = "io.opentelemetry.ktor-1.0"
private val contextKey = AttributeKey<Context>("OpenTelemetry")
private val errorKey = AttributeKey<Throwable>("OpenTelemetryException")
override val key: AttributeKey<KtorServerTelemetry> = AttributeKey("OpenTelemetry")
override fun install(pipeline: Application, configure: Configuration.() -> Unit): KtorServerTelemetry {
val configuration = Configuration().apply(configure)
if (!configuration.isOpenTelemetryInitialized()) {
throw IllegalArgumentException("OpenTelemetry must be set")
}
val instrumenter = InstrumenterUtil.buildUpstreamInstrumenter(
configuration.builder.instrumenterBuilder(),
ApplicationRequestGetter,
configuration.spanKindExtractor(SpanKindExtractor.alwaysServer())
)
val feature = KtorServerTelemetry(instrumenter)
val startPhase = PipelinePhase("OpenTelemetry")
pipeline.insertPhaseBefore(ApplicationCallPipeline.Monitoring, startPhase)
pipeline.intercept(startPhase) {
val context = feature.start(call)
if (context != null) {
call.attributes.put(contextKey, context)
withContext(context.asContextElement()) {
try {
proceed()
} catch (err: Throwable) {
// Stash error for reporting later since need ktor to finish setting up the response
call.attributes.put(errorKey, err)
throw err
}
}
} else {
proceed()
}
}
val postSendPhase = PipelinePhase("OpenTelemetryPostSend")
pipeline.sendPipeline.insertPhaseAfter(ApplicationSendPipeline.After, postSendPhase)
pipeline.sendPipeline.intercept(postSendPhase) {
val context = call.attributes.getOrNull(contextKey)
if (context != null) {
var error: Throwable? = call.attributes.getOrNull(errorKey)
try {
proceed()
} catch (t: Throwable) {
error = t
throw t
} finally {
feature.end(context, call, error)
}
} else {
proceed()
}
}
pipeline.environment.monitor.subscribe(Routing.RoutingCallStarted) { call ->
val context = call.attributes.getOrNull(contextKey)
if (context != null) {
HttpServerRoute.update(context, HttpServerRouteSource.SERVER, { _, arg -> arg.route.parent.toString() }, call)
}
}
return feature
}
}
}

View File

@ -25,6 +25,7 @@ import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource
import kotlinx.coroutines.withContext
@Deprecated("Use KtorServerTelemetry instead", ReplaceWith("KtorServerTelemetry"))
class KtorServerTracing private constructor(
private val instrumenter: Instrumenter<ApplicationRequest, ApplicationResponse>,
) {

View File

@ -0,0 +1,143 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v1_0
import io.ktor.application.*
import io.ktor.http.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.trace.SpanKind
import io.opentelemetry.api.trace.StatusCode
import io.opentelemetry.context.Context
import io.opentelemetry.extension.kotlin.asContextElement
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
import io.opentelemetry.semconv.ServerAttributes
import kotlinx.coroutines.withContext
import org.junit.jupiter.api.extension.RegisterExtension
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
class KtorHttpServerOldTest : AbstractHttpServerTest<ApplicationEngine>() {
companion object {
@JvmStatic
@RegisterExtension
val testing = HttpServerInstrumentationExtension.forLibrary()
}
override fun setupServer(): ApplicationEngine {
return embeddedServer(Netty, port = port) {
KtorOldTestUtil.installOpenTelemetry(this, testing.openTelemetry)
routing {
get(ServerEndpoint.SUCCESS.path) {
controller(ServerEndpoint.SUCCESS) {
call.respondText(ServerEndpoint.SUCCESS.body, status = HttpStatusCode.fromValue(ServerEndpoint.SUCCESS.status))
}
}
get(ServerEndpoint.REDIRECT.path) {
controller(ServerEndpoint.REDIRECT) {
call.respondRedirect(ServerEndpoint.REDIRECT.body)
}
}
get(ServerEndpoint.ERROR.path) {
controller(ServerEndpoint.ERROR) {
call.respondText(ServerEndpoint.ERROR.body, status = HttpStatusCode.fromValue(ServerEndpoint.ERROR.status))
}
}
get(ServerEndpoint.EXCEPTION.path) {
controller(ServerEndpoint.EXCEPTION) {
throw IllegalStateException(ServerEndpoint.EXCEPTION.body)
}
}
get("/query") {
controller(ServerEndpoint.QUERY_PARAM) {
call.respondText("some=${call.request.queryParameters["some"]}", status = HttpStatusCode.fromValue(ServerEndpoint.QUERY_PARAM.status))
}
}
get("/path/{id}/param") {
controller(ServerEndpoint.PATH_PARAM) {
call.respondText(
call.parameters["id"]
?: "",
status = HttpStatusCode.fromValue(ServerEndpoint.PATH_PARAM.status),
)
}
}
get("/child") {
controller(ServerEndpoint.INDEXED_CHILD) {
ServerEndpoint.INDEXED_CHILD.collectSpanAttributes { call.request.queryParameters[it] }
call.respondText(ServerEndpoint.INDEXED_CHILD.body, status = HttpStatusCode.fromValue(ServerEndpoint.INDEXED_CHILD.status))
}
}
get("/captureHeaders") {
controller(ServerEndpoint.CAPTURE_HEADERS) {
call.response.header("X-Test-Response", call.request.header("X-Test-Request") ?: "")
call.respondText(ServerEndpoint.CAPTURE_HEADERS.body, status = HttpStatusCode.fromValue(ServerEndpoint.CAPTURE_HEADERS.status))
}
}
}
}.start()
}
override fun stopServer(server: ApplicationEngine) {
server.stop(0, 10, TimeUnit.SECONDS)
}
// Copy in HttpServerTest.controller but make it a suspending function
private suspend fun controller(endpoint: ServerEndpoint, wrapped: suspend () -> Unit) {
assert(Span.current().spanContext.isValid, { "Controller should have a parent span. " })
if (endpoint == ServerEndpoint.NOT_FOUND) {
wrapped()
}
val span = testing.openTelemetry.getTracer("test").spanBuilder("controller").setSpanKind(SpanKind.INTERNAL).startSpan()
try {
withContext(Context.current().with(span).asContextElement()) {
wrapped()
}
span.end()
} catch (e: Exception) {
span.setStatus(StatusCode.ERROR)
span.recordException(if (e is ExecutionException) e.cause ?: e else e)
span.end()
throw e
}
}
override fun configure(options: HttpServerTestOptions) {
options.setTestPathParam(true)
options.setHttpAttributes {
HttpServerTestOptions.DEFAULT_HTTP_ATTRIBUTES - ServerAttributes.SERVER_PORT
}
options.setExpectedHttpRoute { endpoint, method ->
when (endpoint) {
ServerEndpoint.PATH_PARAM -> "/path/{id}/param"
else -> expectedHttpRoute(endpoint, method)
}
}
// ktor does not have a controller lifecycle so the server span ends immediately when the
// response is sent, which is before the controller span finishes.
options.setVerifyServerSpanEndTime(false)
options.setResponseCodeOnNonStandardHttpMethod(404)
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v1_0
import io.ktor.application.*
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest
class KtorOldTestUtil {
companion object {
fun installOpenTelemetry(application: Application, openTelemetry: OpenTelemetry) {
application.install(KtorServerTracing) {
setOpenTelemetry(openTelemetry)
setCapturedRequestHeaders(listOf(AbstractHttpServerTest.TEST_REQUEST_HEADER))
setCapturedResponseHeaders(listOf(AbstractHttpServerTest.TEST_RESPONSE_HEADER))
}
}
}
}

View File

@ -58,7 +58,7 @@ class KtorServerSpanKindExtractorTest : AbstractHttpServerUsingTest<ApplicationE
override fun setupServer(): ApplicationEngine {
return embeddedServer(Netty, port = port) {
install(KtorServerTracing) {
install(KtorServerTelemetry) {
setOpenTelemetry(testing.openTelemetry)
setSpanKindExtractor {
SpanKindExtractor { req ->

View File

@ -12,7 +12,7 @@ import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTes
class KtorTestUtil {
companion object {
fun installOpenTelemetry(application: Application, openTelemetry: OpenTelemetry) {
application.install(KtorServerTracing) {
application.install(KtorServerTelemetry) {
setOpenTelemetry(openTelemetry)
setCapturedRequestHeaders(listOf(AbstractHttpServerTest.TEST_REQUEST_HEADER))
setCapturedResponseHeaders(listOf(AbstractHttpServerTest.TEST_RESPONSE_HEADER))

View File

@ -0,0 +1,42 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.common
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.opentelemetry.context.Context
import io.opentelemetry.context.propagation.ContextPropagators
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter
abstract class AbstractKtorClientTelemetry(
private val instrumenter: Instrumenter<HttpRequestData, HttpResponse>,
private val propagators: ContextPropagators,
) {
internal fun createSpan(requestBuilder: HttpRequestBuilder): Context? {
val parentContext = Context.current()
val requestData = requestBuilder.build()
return if (instrumenter.shouldStart(parentContext, requestData)) {
instrumenter.start(parentContext, requestData)
} else {
null
}
}
internal fun populateRequestHeaders(requestBuilder: HttpRequestBuilder, context: Context) {
propagators.textMapPropagator.inject(context, requestBuilder, KtorHttpHeadersSetter)
}
internal fun endSpan(context: Context, call: HttpClientCall, error: Throwable?) {
endSpan(context, HttpRequestBuilder().takeFrom(call.request), call.response, error)
}
internal fun endSpan(context: Context, requestBuilder: HttpRequestBuilder, response: HttpResponse?, error: Throwable?) {
instrumenter.end(context, requestBuilder.build(), response, error)
}
}

View File

@ -0,0 +1,121 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.common
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.api.common.AttributesBuilder
import io.opentelemetry.context.Context
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorBuilderUtil
import java.util.function.Function
abstract class AbstractKtorClientTelemetryBuilder(
private val instrumentationName: String
) {
companion object {
init {
KtorBuilderUtil.clientBuilderExtractor = { it.builder }
}
}
internal lateinit var openTelemetry: OpenTelemetry
protected lateinit var builder: DefaultHttpClientInstrumenterBuilder<HttpRequestData, HttpResponse>
internal fun builder(): DefaultHttpClientInstrumenterBuilder<HttpRequestData, HttpResponse> {
return builder
}
fun setOpenTelemetry(openTelemetry: OpenTelemetry) {
this.openTelemetry = openTelemetry
this.builder = DefaultHttpClientInstrumenterBuilder.create(
instrumentationName,
openTelemetry,
KtorHttpClientAttributesGetter
)
}
protected fun getOpenTelemetry(): OpenTelemetry {
return openTelemetry
}
fun capturedRequestHeaders(vararg headers: String) = capturedRequestHeaders(headers.asIterable())
fun capturedRequestHeaders(headers: Iterable<String>) {
builder.setCapturedRequestHeaders(headers.toList())
}
fun capturedResponseHeaders(vararg headers: String) = capturedResponseHeaders(headers.asIterable())
fun capturedResponseHeaders(headers: Iterable<String>) {
builder.setCapturedResponseHeaders(headers.toList())
}
fun knownMethods(vararg methods: String) = knownMethods(methods.asIterable())
fun knownMethods(vararg methods: HttpMethod) = knownMethods(methods.asIterable())
@JvmName("knownMethodsJvm")
fun knownMethods(methods: Iterable<HttpMethod>) = knownMethods(methods.map { it.value })
fun knownMethods(methods: Iterable<String>) {
builder.setKnownMethods(methods.toSet())
}
fun attributesExtractor(extractorBuilder: ExtractorBuilder.() -> Unit = {}) {
val builder = ExtractorBuilder().apply(extractorBuilder).build()
this.builder.addAttributesExtractor(object : AttributesExtractor<HttpRequestData, HttpResponse> {
override fun onStart(attributes: AttributesBuilder, parentContext: Context, request: HttpRequestData) {
builder.onStart(OnStartData(attributes, parentContext, request))
}
override fun onEnd(attributes: AttributesBuilder, context: Context, request: HttpRequestData, response: HttpResponse?, error: Throwable?) {
builder.onEnd(OnEndData(attributes, context, request, response, error))
}
})
}
fun spanNameExtractor(spanNameExtractorTransformer: Function<SpanNameExtractor<in HttpRequestData>, out SpanNameExtractor<in HttpRequestData>>) {
builder.setSpanNameExtractor(spanNameExtractorTransformer)
}
class ExtractorBuilder {
private var onStart: OnStartData.() -> Unit = {}
private var onEnd: OnEndData.() -> Unit = {}
fun onStart(block: OnStartData.() -> Unit) {
onStart = block
}
fun onEnd(block: OnEndData.() -> Unit) {
onEnd = block
}
internal fun build(): Extractor {
return Extractor(onStart, onEnd)
}
}
internal class Extractor(val onStart: OnStartData.() -> Unit, val onEnd: OnEndData.() -> Unit)
data class OnStartData(
val attributes: AttributesBuilder,
val parentContext: Context,
val request: HttpRequestData
)
data class OnEndData(
val attributes: AttributesBuilder,
val parentContext: Context,
val request: HttpRequestData,
val response: HttpResponse?,
val error: Throwable?
)
}

View File

@ -0,0 +1,155 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.common
import io.ktor.http.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.api.common.AttributesBuilder
import io.opentelemetry.api.trace.SpanKind
import io.opentelemetry.context.Context
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorBuilderUtil
import java.util.function.Function
abstract class AbstractKtorServerTelemetryBuilder(private val instrumentationName: String) {
companion object {
init {
KtorBuilderUtil.serverBuilderExtractor = { it.builder }
}
}
internal lateinit var builder: DefaultHttpServerInstrumenterBuilder<ApplicationRequest, ApplicationResponse>
internal var spanKindExtractor:
(SpanKindExtractor<ApplicationRequest>) -> SpanKindExtractor<ApplicationRequest> = { a -> a }
fun setOpenTelemetry(openTelemetry: OpenTelemetry) {
this.builder =
DefaultHttpServerInstrumenterBuilder.create(
instrumentationName,
openTelemetry,
KtorHttpServerAttributesGetter.INSTANCE
)
}
fun spanStatusExtractor(extract: SpanStatusData.(SpanStatusExtractor<in ApplicationRequest, in ApplicationResponse>) -> Unit) {
builder.setStatusExtractor { prevExtractor ->
SpanStatusExtractor { spanStatusBuilder: SpanStatusBuilder,
request: ApplicationRequest,
response: ApplicationResponse?,
throwable: Throwable? ->
extract(
SpanStatusData(spanStatusBuilder, request, response, throwable),
prevExtractor
)
}
}
}
data class SpanStatusData(
val spanStatusBuilder: SpanStatusBuilder,
val request: ApplicationRequest,
val response: ApplicationResponse?,
val error: Throwable?
)
fun spanKindExtractor(extract: ApplicationRequest.(SpanKindExtractor<ApplicationRequest>) -> SpanKind) {
spanKindExtractor = { prevExtractor ->
SpanKindExtractor<ApplicationRequest> { request: ApplicationRequest ->
extract(request, prevExtractor)
}
}
}
fun attributesExtractor(extractorBuilder: ExtractorBuilder.() -> Unit = {}) {
val builder = ExtractorBuilder().apply(extractorBuilder).build()
this.builder.addAttributesExtractor(
object : AttributesExtractor<ApplicationRequest, ApplicationResponse> {
override fun onStart(attributes: AttributesBuilder, parentContext: Context, request: ApplicationRequest) {
builder.onStart(OnStartData(attributes, parentContext, request))
}
override fun onEnd(attributes: AttributesBuilder, context: Context, request: ApplicationRequest, response: ApplicationResponse?, error: Throwable?) {
builder.onEnd(OnEndData(attributes, context, request, response, error))
}
}
)
}
fun spanNameExtractor(spanNameExtractorTransformer: Function<SpanNameExtractor<in ApplicationRequest>, out SpanNameExtractor<in ApplicationRequest>>) {
builder.setSpanNameExtractor(spanNameExtractorTransformer)
}
class ExtractorBuilder {
private var onStart: OnStartData.() -> Unit = {}
private var onEnd: OnEndData.() -> Unit = {}
fun onStart(block: OnStartData.() -> Unit) {
onStart = block
}
fun onEnd(block: OnEndData.() -> Unit) {
onEnd = block
}
internal fun build(): Extractor {
return Extractor(onStart, onEnd)
}
}
internal class Extractor(val onStart: OnStartData.() -> Unit, val onEnd: OnEndData.() -> Unit)
data class OnStartData(
val attributes: AttributesBuilder,
val parentContext: Context,
val request: ApplicationRequest
)
data class OnEndData(
val attributes: AttributesBuilder,
val parentContext: Context,
val request: ApplicationRequest,
val response: ApplicationResponse?,
val error: Throwable?
)
fun capturedRequestHeaders(vararg headers: String) = capturedRequestHeaders(headers.asIterable())
fun capturedRequestHeaders(headers: Iterable<String>) {
builder.setCapturedRequestHeaders(headers.toList())
}
fun capturedResponseHeaders(vararg headers: String) = capturedResponseHeaders(headers.asIterable())
fun capturedResponseHeaders(headers: Iterable<String>) {
builder.setCapturedResponseHeaders(headers.toList())
}
fun knownMethods(vararg methods: String) = knownMethods(methods.asIterable())
fun knownMethods(vararg methods: HttpMethod) = knownMethods(methods.asIterable())
@JvmName("knownMethodsJvm")
fun knownMethods(methods: Iterable<HttpMethod>) = knownMethods(methods.map { it.value })
fun knownMethods(methods: Iterable<String>) {
methods.toSet().apply {
builder.setKnownMethods(this)
}
}
/**
* {@link #setOpenTelemetry(OpenTelemetry)} sets the serverBuilder to a non-null value.
*/
fun isOpenTelemetryInitialized(): Boolean = this::builder.isInitialized
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.server
package io.opentelemetry.instrumentation.ktor.v2_0.common
import io.ktor.server.request.*
import io.opentelemetry.context.propagation.TextMapGetter

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.client
package io.opentelemetry.instrumentation.ktor.v2_0.common
import io.ktor.client.request.*
import io.ktor.client.statement.*

View File

@ -3,9 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.client
package io.opentelemetry.instrumentation.ktor.v2_0.common
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.*
import io.opentelemetry.context.propagation.TextMapSetter
internal object KtorHttpHeadersSetter : TextMapSetter<HttpRequestBuilder> {

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.server
package io.opentelemetry.instrumentation.ktor.v2_0.common
import io.ktor.server.plugins.*
import io.ktor.server.request.*

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.client
package io.opentelemetry.instrumentation.ktor.v2_0.common.client
import io.ktor.client.call.*
import io.ktor.client.request.*
@ -11,7 +11,9 @@ import io.ktor.client.statement.*
import io.opentelemetry.context.Context
import io.opentelemetry.context.propagation.ContextPropagators
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter
import io.opentelemetry.instrumentation.ktor.v2_0.common.KtorHttpHeadersSetter
@Deprecated("Use AbstractKtorClientTelemetry instead", ReplaceWith("AbstractKtorClientTelemetry"))
abstract class AbstractKtorClientTracing(
private val instrumenter: Instrumenter<HttpRequestData, HttpResponse>,
private val propagators: ContextPropagators,

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.client
package io.opentelemetry.instrumentation.ktor.v2_0.common.client
import io.ktor.client.request.*
import io.ktor.client.statement.*
@ -14,15 +14,17 @@ import io.opentelemetry.context.Context
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor
import io.opentelemetry.instrumentation.ktor.internal.KtorBuilderUtil
import io.opentelemetry.instrumentation.ktor.v2_0.common.KtorHttpClientAttributesGetter
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorBuilderUtilOld
import java.util.function.Function
@Deprecated("Use AbstractKtorClientTelemetryBuilder instead", ReplaceWith("AbstractKtorClientTelemetryBuilder"))
abstract class AbstractKtorClientTracingBuilder(
private val instrumentationName: String
) {
companion object {
init {
KtorBuilderUtil.clientBuilderExtractor = { it.clientBuilder }
KtorBuilderUtilOld.clientBuilderExtractor = { it.clientBuilder }
}
}
@ -161,13 +163,14 @@ abstract class AbstractKtorClientTracingBuilder(
*
* @param emitExperimentalHttpClientMetrics `true` if the experimental HTTP client metrics are to be emitted.
*/
@Deprecated("Please use method `emitExperimentalHttpClientMetrics`")
@Deprecated("Please use method `Experimental.emitExperimentalTelemetry`")
fun setEmitExperimentalHttpClientMetrics(emitExperimentalHttpClientMetrics: Boolean) {
if (emitExperimentalHttpClientMetrics) {
emitExperimentalHttpClientMetrics()
}
}
@Deprecated("Please use method `Experimental.emitExperimentalTelemetry`")
fun emitExperimentalHttpClientMetrics() {
clientBuilder.setEmitExperimentalHttpClientMetrics(true)
}

View File

@ -0,0 +1,22 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.common.internal
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorClientTelemetryBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorServerTelemetryBuilder
class Experimental private constructor() {
companion object {
fun emitExperimentalTelemetry(builder: AbstractKtorClientTelemetryBuilder) {
builder.builder().setEmitExperimentalHttpClientMetrics(true)
}
fun emitExperimentalTelemetry(builder: AbstractKtorServerTelemetryBuilder) {
builder.builder.setEmitExperimentalHttpServerMetrics(true)
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.common.internal
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorClientTelemetryBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorServerTelemetryBuilder
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
object KtorBuilderUtil {
lateinit var clientBuilderExtractor: (AbstractKtorClientTelemetryBuilder) -> DefaultHttpClientInstrumenterBuilder<HttpRequestData, HttpResponse>
lateinit var serverBuilderExtractor: (
AbstractKtorServerTelemetryBuilder
) -> DefaultHttpServerInstrumenterBuilder<ApplicationRequest, ApplicationResponse>
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.internal
package io.opentelemetry.instrumentation.ktor.v2_0.common.internal
import io.ktor.client.request.*
import io.ktor.client.statement.*
@ -11,14 +11,15 @@ import io.ktor.server.request.*
import io.ktor.server.response.*
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder
import io.opentelemetry.instrumentation.ktor.client.AbstractKtorClientTracingBuilder
import io.opentelemetry.instrumentation.ktor.server.AbstractKtorServerTracingBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.common.client.AbstractKtorClientTracingBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.common.server.AbstractKtorServerTracingBuilder
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
object KtorBuilderUtil {
@Deprecated("Use KtorBuilderUtil instead", ReplaceWith("KtorBuilderUtil"))
object KtorBuilderUtilOld {
lateinit var clientBuilderExtractor: (AbstractKtorClientTracingBuilder) -> DefaultHttpClientInstrumenterBuilder<HttpRequestData, HttpResponse>
lateinit var serverBuilderExtractor: (
AbstractKtorServerTracingBuilder

View File

@ -0,0 +1,88 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.common.internal
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.util.*
import io.ktor.util.pipeline.*
import io.opentelemetry.context.Context
import io.opentelemetry.extension.kotlin.asContextElement
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientRequestResendCount
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorClientTelemetry
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
object KtorClientTelemetryUtil {
private val openTelemetryContextKey = AttributeKey<Context>("OpenTelemetry")
fun install(plugin: AbstractKtorClientTelemetry, scope: HttpClient) {
installSpanCreation(plugin, scope)
installSpanEnd(plugin, scope)
}
private fun installSpanCreation(plugin: AbstractKtorClientTelemetry, scope: HttpClient) {
val initializeRequestPhase = PipelinePhase("OpenTelemetryInitializeRequest")
scope.requestPipeline.insertPhaseAfter(HttpRequestPipeline.State, initializeRequestPhase)
scope.requestPipeline.intercept(initializeRequestPhase) {
val openTelemetryContext = HttpClientRequestResendCount.initialize(Context.current())
withContext(openTelemetryContext.asContextElement()) { proceed() }
}
val createSpanPhase = PipelinePhase("OpenTelemetryCreateSpan")
scope.sendPipeline.insertPhaseAfter(HttpSendPipeline.State, createSpanPhase)
scope.sendPipeline.intercept(createSpanPhase) {
val requestBuilder = context
val openTelemetryContext = plugin.createSpan(requestBuilder)
if (openTelemetryContext != null) {
try {
requestBuilder.attributes.put(openTelemetryContextKey, openTelemetryContext)
plugin.populateRequestHeaders(requestBuilder, openTelemetryContext)
withContext(openTelemetryContext.asContextElement()) { proceed() }
} catch (e: Throwable) {
plugin.endSpan(openTelemetryContext, requestBuilder, null, e)
throw e
}
} else {
proceed()
}
}
}
@OptIn(InternalCoroutinesApi::class)
private fun installSpanEnd(plugin: AbstractKtorClientTelemetry, scope: HttpClient) {
val endSpanPhase = PipelinePhase("OpenTelemetryEndSpan")
scope.receivePipeline.insertPhaseBefore(HttpReceivePipeline.State, endSpanPhase)
scope.receivePipeline.intercept(endSpanPhase) {
val openTelemetryContext = it.call.attributes.getOrNull(openTelemetryContextKey)
openTelemetryContext ?: return@intercept
scope.launch {
val job = it.call.coroutineContext.job
job.join()
val cause = if (!job.isCancelled) {
null
} else {
kotlin.runCatching { job.getCancellationException() }.getOrNull()
}
plugin.endSpan(openTelemetryContext, it.call, cause)
}
}
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.internal
package io.opentelemetry.instrumentation.ktor.v2_0.common.internal
import io.ktor.client.*
import io.ktor.client.request.*
@ -13,7 +13,7 @@ import io.ktor.util.pipeline.*
import io.opentelemetry.context.Context
import io.opentelemetry.extension.kotlin.asContextElement
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientRequestResendCount
import io.opentelemetry.instrumentation.ktor.client.AbstractKtorClientTracing
import io.opentelemetry.instrumentation.ktor.v2_0.common.client.AbstractKtorClientTracing
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.job
import kotlinx.coroutines.launch
@ -23,6 +23,7 @@ import kotlinx.coroutines.withContext
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
@Deprecated("Use KtorClientTelemetryUtil instead", ReplaceWith("KtorClientTelemetryUtil"))
object KtorClientTracingUtil {
private val openTelemetryContextKey = AttributeKey<Context>("OpenTelemetry")

View File

@ -0,0 +1,83 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.common.internal
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.util.*
import io.ktor.util.pipeline.*
import io.opentelemetry.context.Context
import io.opentelemetry.extension.kotlin.asContextElement
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor
import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorServerTelemetryBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.common.ApplicationRequestGetter
import kotlinx.coroutines.withContext
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
object KtorServerTelemetryUtil {
fun configureTelemetry(builder: AbstractKtorServerTelemetryBuilder, application: Application) {
val contextKey = AttributeKey<Context>("OpenTelemetry")
val errorKey = AttributeKey<Throwable>("OpenTelemetryException")
val instrumenter = instrumenter(builder)
val tracer = KtorServerTracer(instrumenter)
val startPhase = PipelinePhase("OpenTelemetry")
application.insertPhaseBefore(ApplicationCallPipeline.Monitoring, startPhase)
application.intercept(startPhase) {
val context = tracer.start(call)
if (context != null) {
call.attributes.put(contextKey, context)
withContext(context.asContextElement()) {
try {
proceed()
} catch (err: Throwable) {
// Stash error for reporting later since need ktor to finish setting up the response
call.attributes.put(errorKey, err)
throw err
}
}
} else {
proceed()
}
}
val postSendPhase = PipelinePhase("OpenTelemetryPostSend")
application.sendPipeline.insertPhaseAfter(ApplicationSendPipeline.After, postSendPhase)
application.sendPipeline.intercept(postSendPhase) {
val context = call.attributes.getOrNull(contextKey)
if (context != null) {
var error: Throwable? = call.attributes.getOrNull(errorKey)
try {
proceed()
} catch (t: Throwable) {
error = t
throw t
} finally {
tracer.end(context, call, error)
}
} else {
proceed()
}
}
}
private fun instrumenter(builder: AbstractKtorServerTelemetryBuilder): Instrumenter<ApplicationRequest, ApplicationResponse> {
return InstrumenterUtil.buildUpstreamInstrumenter(
builder.builder.instrumenterBuilder(),
ApplicationRequestGetter,
builder.spanKindExtractor(SpanKindExtractor.alwaysServer())
)
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.internal
package io.opentelemetry.instrumentation.ktor.v2_0.common.internal
import io.ktor.server.application.*
import io.ktor.server.request.*

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.internal
package io.opentelemetry.instrumentation.ktor.v2_0.common.internal
import io.ktor.server.application.*
import io.ktor.server.request.*
@ -15,14 +15,15 @@ import io.opentelemetry.extension.kotlin.asContextElement
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor
import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil
import io.opentelemetry.instrumentation.ktor.server.AbstractKtorServerTracingBuilder
import io.opentelemetry.instrumentation.ktor.server.ApplicationRequestGetter
import io.opentelemetry.instrumentation.ktor.v2_0.common.ApplicationRequestGetter
import io.opentelemetry.instrumentation.ktor.v2_0.common.server.AbstractKtorServerTracingBuilder
import kotlinx.coroutines.withContext
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
@Deprecated("Use KtorServerTelemetryUtil instead", ReplaceWith("KtorServerTelemetryUtil"))
object KtorServerTracingUtil {
fun configureTracing(builder: AbstractKtorServerTracingBuilder, application: Application) {

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.server
package io.opentelemetry.instrumentation.ktor.v2_0.common.server
import io.ktor.http.*
import io.ktor.server.request.*
@ -18,13 +18,15 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor
import io.opentelemetry.instrumentation.ktor.internal.KtorBuilderUtil
import io.opentelemetry.instrumentation.ktor.v2_0.common.KtorHttpServerAttributesGetter
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorBuilderUtilOld
import java.util.function.Function
@Deprecated("Use AbstractKtorServerTelemetryBuilder instead", ReplaceWith("AbstractKtorServerTelemetryBuilder"))
abstract class AbstractKtorServerTracingBuilder(private val instrumentationName: String) {
companion object {
init {
KtorBuilderUtil.serverBuilderExtractor = { it.serverBuilder }
KtorBuilderUtilOld.serverBuilderExtractor = { it.serverBuilder }
}
}

View File

@ -13,9 +13,9 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.ktor.client.HttpClientConfig;
import io.ktor.client.engine.HttpClientEngineConfig;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.ktor.internal.KtorBuilderUtil;
import io.opentelemetry.instrumentation.ktor.v2_0.client.KtorClientTracing;
import io.opentelemetry.instrumentation.ktor.v2_0.client.KtorClientTracingBuilder;
import io.opentelemetry.instrumentation.ktor.v2_0.KtorClientTelemetry;
import io.opentelemetry.instrumentation.ktor.v2_0.KtorClientTelemetryBuilder;
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorBuilderUtil;
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
@ -46,14 +46,14 @@ public class HttpClientInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter
public static void onEnter(
@Advice.Argument(1) HttpClientConfig<HttpClientEngineConfig> httpClientConfig) {
httpClientConfig.install(KtorClientTracing.Companion, new SetupFunction());
httpClientConfig.install(KtorClientTelemetry.Companion, new SetupFunction());
}
}
public static class SetupFunction implements Function1<KtorClientTracingBuilder, Unit> {
public static class SetupFunction implements Function1<KtorClientTelemetryBuilder, Unit> {
@Override
public Unit invoke(KtorClientTracingBuilder builder) {
public Unit invoke(KtorClientTelemetryBuilder builder) {
builder.setOpenTelemetry(GlobalOpenTelemetry.get());
KtorBuilderUtil.clientBuilderExtractor.invoke(builder).configure(AgentCommonConfig.get());
return kotlin.Unit.INSTANCE;

View File

@ -11,9 +11,9 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import io.ktor.server.application.Application;
import io.ktor.server.application.ApplicationPluginKt;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.ktor.internal.KtorBuilderUtil;
import io.opentelemetry.instrumentation.ktor.server.AbstractKtorServerTracingBuilder;
import io.opentelemetry.instrumentation.ktor.v2_0.server.KtorServerTracingBuilderKt;
import io.opentelemetry.instrumentation.ktor.v2_0.KtorServerTelemetryBuilderKt;
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorServerTelemetryBuilder;
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorBuilderUtil;
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
@ -41,15 +41,15 @@ public class ServerInstrumentation implements TypeInstrumentation {
@Advice.OnMethodExit
public static void onExit(@Advice.FieldValue("_applicationInstance") Application application) {
ApplicationPluginKt.install(
application, KtorServerTracingBuilderKt.getKtorServerTracing(), new SetupFunction());
application, KtorServerTelemetryBuilderKt.getKtorServerTelemetry(), new SetupFunction());
}
}
public static class SetupFunction
implements Function1<AbstractKtorServerTracingBuilder, kotlin.Unit> {
implements Function1<AbstractKtorServerTelemetryBuilder, kotlin.Unit> {
@Override
public Unit invoke(AbstractKtorServerTracingBuilder builder) {
public Unit invoke(AbstractKtorServerTelemetryBuilder builder) {
builder.setOpenTelemetry(GlobalOpenTelemetry.get());
KtorBuilderUtil.serverBuilderExtractor.invoke(builder).configure(AgentCommonConfig.get());
return kotlin.Unit.INSTANCE;

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.client
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.client.*
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0.server
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.server.application.*
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension

View File

@ -1,6 +1,7 @@
# Library Instrumentation for Ktor version 2.x
This package contains libraries to help instrument Ktor. Server and client instrumentations are supported.
This package contains libraries to help instrument Ktor.
Server and client instrumentations are supported.
## Quickstart
@ -31,14 +32,14 @@ implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-2.0:OPENTELE
## Initializing server instrumentation
Initialize instrumentation by installing the `KtorServerTracing` feature. You must set the `OpenTelemetry` to use with
the feature.
Initialize instrumentation by installing the `KtorServerTelemetry` feature.
You must set the `OpenTelemetry` to use with the feature.
```kotlin
val openTelemetry: OpenTelemetry = ...
embeddedServer(Netty, 8080) {
install(KtorServerTracing) {
install(KtorServerTelemetry) {
setOpenTelemetry(openTelemetry)
}
}
@ -46,14 +47,15 @@ embeddedServer(Netty, 8080) {
## Initializing client instrumentation
Initialize instrumentation by installing the `KtorClientTracing` feature. You must set the `OpenTelemetry` to use with
Initialize instrumentation by installing the `KtorClientTelemetry` feature. You must set the
`OpenTelemetry` to use with
the feature.
```kotlin
val openTelemetry: OpenTelemetry = ...
val client = HttpClient {
install(KtorClientTracing) {
install(KtorClientTelemetry) {
setOpenTelemetry(openTelemetry)
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.client.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.util.*
import io.opentelemetry.context.propagation.ContextPropagators
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorClientTelemetry
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorClientTelemetryUtil
class KtorClientTelemetry internal constructor(
instrumenter: Instrumenter<HttpRequestData, HttpResponse>,
propagators: ContextPropagators
) : AbstractKtorClientTelemetry(instrumenter, propagators) {
companion object : HttpClientPlugin<KtorClientTelemetryBuilder, KtorClientTelemetry> {
override val key = AttributeKey<KtorClientTelemetry>("OpenTelemetry")
override fun prepare(block: KtorClientTelemetryBuilder.() -> Unit) = KtorClientTelemetryBuilder().apply(block).build()
override fun install(plugin: KtorClientTelemetry, scope: HttpClient) {
KtorClientTelemetryUtil.install(plugin, scope)
}
}
}

View File

@ -0,0 +1,17 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0
import io.opentelemetry.instrumentation.ktor.v2_0.InstrumentationProperties.INSTRUMENTATION_NAME
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorClientTelemetryBuilder
class KtorClientTelemetryBuilder : AbstractKtorClientTelemetryBuilder(INSTRUMENTATION_NAME) {
internal fun build(): KtorClientTelemetry = KtorClientTelemetry(
instrumenter = builder.build(),
propagators = getOpenTelemetry().propagators,
)
}

View File

@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.server.application.*
import io.ktor.server.routing.*
import io.opentelemetry.context.Context
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource
import io.opentelemetry.instrumentation.ktor.v2_0.InstrumentationProperties.INSTRUMENTATION_NAME
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorServerTelemetryBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorServerTelemetryUtil
class KtorServerTelemetryBuilder internal constructor(
instrumentationName: String
) : AbstractKtorServerTelemetryBuilder(instrumentationName)
val KtorServerTelemetry = createRouteScopedPlugin("OpenTelemetry", { KtorServerTelemetryBuilder(INSTRUMENTATION_NAME) }) {
require(pluginConfig.isOpenTelemetryInitialized()) { "OpenTelemetry must be set" }
KtorServerTelemetryUtil.configureTelemetry(pluginConfig, application)
application.environment.monitor.subscribe(Routing.RoutingCallStarted) { call ->
HttpServerRoute.update(Context.current(), HttpServerRouteSource.SERVER, { _, arg -> arg.route.parent.toString() }, call)
}
}

View File

@ -12,9 +12,10 @@ import io.ktor.client.statement.*
import io.ktor.util.*
import io.opentelemetry.context.propagation.ContextPropagators
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter
import io.opentelemetry.instrumentation.ktor.client.AbstractKtorClientTracing
import io.opentelemetry.instrumentation.ktor.internal.KtorClientTracingUtil
import io.opentelemetry.instrumentation.ktor.v2_0.common.client.AbstractKtorClientTracing
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorClientTracingUtil
@Deprecated("Use KtorClientTelemetry instead", ReplaceWith("KtorClientTelemetry"))
class KtorClientTracing internal constructor(
instrumenter: Instrumenter<HttpRequestData, HttpResponse>,
propagators: ContextPropagators

View File

@ -5,9 +5,10 @@
package io.opentelemetry.instrumentation.ktor.v2_0.client
import io.opentelemetry.instrumentation.ktor.client.AbstractKtorClientTracingBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.InstrumentationProperties.INSTRUMENTATION_NAME
import io.opentelemetry.instrumentation.ktor.v2_0.common.client.AbstractKtorClientTracingBuilder
@Deprecated("Use KtorClientTelemetryBuilder instead", ReplaceWith("KtorClientTelemetryBuilder"))
class KtorClientTracingBuilder : AbstractKtorClientTracingBuilder(INSTRUMENTATION_NAME) {
internal fun build(): KtorClientTracing = KtorClientTracing(

View File

@ -10,10 +10,11 @@ import io.ktor.server.routing.*
import io.opentelemetry.context.Context
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource
import io.opentelemetry.instrumentation.ktor.internal.KtorServerTracingUtil
import io.opentelemetry.instrumentation.ktor.server.AbstractKtorServerTracingBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.InstrumentationProperties.INSTRUMENTATION_NAME
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorServerTracingUtil
import io.opentelemetry.instrumentation.ktor.v2_0.common.server.AbstractKtorServerTracingBuilder
@Deprecated("Use KtorServerTelemetryBuilder instead", ReplaceWith("KtorServerTelemetryBuilder"))
class KtorServerTracingBuilder internal constructor(
instrumentationName: String
) : AbstractKtorServerTracingBuilder(instrumentationName)

View File

@ -0,0 +1,28 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.client.*
import io.opentelemetry.instrumentation.ktor.v2_0.client.KtorClientTracing
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension
import org.junit.jupiter.api.extension.RegisterExtension
class KtorHttpClientOldTest : AbstractKtorHttpClientTest() {
companion object {
@JvmStatic
@RegisterExtension
private val TESTING = HttpClientInstrumentationExtension.forLibrary()
}
override fun HttpClientConfig<*>.installTracing() {
install(KtorClientTracing) {
setOpenTelemetry(TESTING.openTelemetry)
capturedRequestHeaders(TEST_REQUEST_HEADER)
capturedResponseHeaders(TEST_RESPONSE_HEADER)
}
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0.client
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.client.*
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension
@ -18,7 +18,7 @@ class KtorHttpClientTest : AbstractKtorHttpClientTest() {
}
override fun HttpClientConfig<*>.installTracing() {
install(KtorClientTracing) {
install(KtorClientTelemetry) {
setOpenTelemetry(TESTING.openTelemetry)
capturedRequestHeaders(TEST_REQUEST_HEADER)
capturedResponseHeaders(TEST_RESPONSE_HEADER)

View File

@ -0,0 +1,28 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.server.application.*
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension
import org.junit.jupiter.api.extension.RegisterExtension
class KtorHttpServerOldTest : AbstractKtorHttpServerTest() {
companion object {
@JvmStatic
@RegisterExtension
val TESTING: InstrumentationExtension = HttpServerInstrumentationExtension.forLibrary()
}
override fun getTesting(): InstrumentationExtension {
return TESTING
}
override fun installOpenTelemetry(application: Application) {
KtorOldTestUtil.installOpenTelemetry(application, TESTING.openTelemetry)
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.server
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.server.application.*
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension

View File

@ -0,0 +1,23 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.server.application.*
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.instrumentation.ktor.v2_0.server.KtorServerTracing
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest
class KtorOldTestUtil {
companion object {
fun installOpenTelemetry(application: Application, openTelemetry: OpenTelemetry) {
application.install(KtorServerTracing) {
setOpenTelemetry(openTelemetry)
capturedRequestHeaders(AbstractHttpServerTest.TEST_REQUEST_HEADER)
capturedResponseHeaders(AbstractHttpServerTest.TEST_RESPONSE_HEADER)
}
}
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.server
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.http.*
import io.ktor.server.application.*
@ -13,6 +13,7 @@ import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.opentelemetry.api.trace.SpanKind
import io.opentelemetry.instrumentation.ktor.v2_0.server.KtorServerTracing
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerUsingTest
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.server
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.server.application.*
import io.opentelemetry.api.OpenTelemetry
@ -12,7 +12,7 @@ import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTes
class KtorTestUtil {
companion object {
fun installOpenTelemetry(application: Application, openTelemetry: OpenTelemetry) {
application.install(KtorServerTracing) {
application.install(KtorServerTelemetry) {
setOpenTelemetry(openTelemetry)
capturedRequestHeaders(AbstractHttpServerTest.TEST_REQUEST_HEADER)
capturedResponseHeaders(AbstractHttpServerTest.TEST_RESPONSE_HEADER)

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.client
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.client.*
import io.ktor.client.engine.cio.*
@ -17,7 +17,10 @@ import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES
import io.opentelemetry.semconv.NetworkAttributes
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.AfterAll
import java.net.URI

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.server
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.http.*
import io.ktor.server.application.*

View File

@ -3,10 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.client
package io.opentelemetry.instrumentation.ktor.v2_0
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.request.*
import io.opentelemetry.instrumentation.testing.junit.http.SingleConnection
import kotlinx.coroutines.runBlocking

View File

@ -13,9 +13,9 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.ktor.client.HttpClientConfig;
import io.ktor.client.engine.HttpClientEngineConfig;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.ktor.internal.KtorBuilderUtil;
import io.opentelemetry.instrumentation.ktor.v3_0.client.KtorClientTracing;
import io.opentelemetry.instrumentation.ktor.v3_0.client.KtorClientTracingBuilder;
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorBuilderUtil;
import io.opentelemetry.instrumentation.ktor.v3_0.KtorClientTelemetry;
import io.opentelemetry.instrumentation.ktor.v3_0.KtorClientTelemetryBuilder;
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
@ -46,14 +46,14 @@ public class HttpClientInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter
public static void onEnter(
@Advice.Argument(1) HttpClientConfig<HttpClientEngineConfig> httpClientConfig) {
httpClientConfig.install(KtorClientTracing.Companion, new SetupFunction());
httpClientConfig.install(KtorClientTelemetry.Companion, new SetupFunction());
}
}
public static class SetupFunction implements Function1<KtorClientTracingBuilder, Unit> {
public static class SetupFunction implements Function1<KtorClientTelemetryBuilder, Unit> {
@Override
public Unit invoke(KtorClientTracingBuilder builder) {
public Unit invoke(KtorClientTelemetryBuilder builder) {
builder.setOpenTelemetry(GlobalOpenTelemetry.get());
KtorBuilderUtil.clientBuilderExtractor.invoke(builder).configure(AgentCommonConfig.get());
return Unit.INSTANCE;

View File

@ -11,9 +11,9 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import io.ktor.server.application.Application;
import io.ktor.server.application.ApplicationPluginKt;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.ktor.internal.KtorBuilderUtil;
import io.opentelemetry.instrumentation.ktor.server.AbstractKtorServerTracingBuilder;
import io.opentelemetry.instrumentation.ktor.v3_0.server.KtorServerTracingBuilderKt;
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorServerTelemetryBuilder;
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorBuilderUtil;
import io.opentelemetry.instrumentation.ktor.v3_0.KtorServerTelemetryBuilderKt;
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
@ -41,14 +41,14 @@ public class ServerInstrumentation implements TypeInstrumentation {
@Advice.OnMethodExit
public static void onExit(@Advice.FieldValue("_applicationInstance") Application application) {
ApplicationPluginKt.install(
application, KtorServerTracingBuilderKt.getKtorServerTracing(), new SetupFunction());
application, KtorServerTelemetryBuilderKt.getKtorServerTelemetry(), new SetupFunction());
}
}
public static class SetupFunction implements Function1<AbstractKtorServerTracingBuilder, Unit> {
public static class SetupFunction implements Function1<AbstractKtorServerTelemetryBuilder, Unit> {
@Override
public Unit invoke(AbstractKtorServerTracingBuilder builder) {
public Unit invoke(AbstractKtorServerTelemetryBuilder builder) {
builder.setOpenTelemetry(GlobalOpenTelemetry.get());
KtorBuilderUtil.serverBuilderExtractor.invoke(builder).configure(AgentCommonConfig.get());
return Unit.INSTANCE;

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0.client
package io.opentelemetry.instrumentation.ktor.v3_0
import io.ktor.client.*
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.server
package io.opentelemetry.instrumentation.ktor.v3_0
import io.ktor.server.application.*
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension

View File

@ -1,6 +1,7 @@
# Library Instrumentation for Ktor version 3.0 and higher
This package contains libraries to help instrument Ktor. Server and client instrumentations are supported.
This package contains libraries to help instrument Ktor.
Server and client instrumentations are supported.
## Quickstart
@ -31,14 +32,14 @@ implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-3.0:OPENTELE
## Initializing server instrumentation
Initialize instrumentation by installing the `KtorServerTracing` feature. You must set the `OpenTelemetry` to use with
the feature.
Initialize instrumentation by installing the `KtorServerTelemetry` feature.
You must set the `OpenTelemetry` to use with the feature.
```kotlin
val openTelemetry: OpenTelemetry = ...
embeddedServer(Netty, 8080) {
install(KtorServerTracing) {
install(KtorServerTelemetry) {
setOpenTelemetry(openTelemetry)
}
}
@ -46,14 +47,15 @@ embeddedServer(Netty, 8080) {
## Initializing client instrumentation
Initialize instrumentation by installing the `KtorClientTracing` feature. You must set the `OpenTelemetry` to use with
Initialize instrumentation by installing the `KtorClientTelemetry` feature. You must set the
`OpenTelemetry` to use with
the feature.
```kotlin
val openTelemetry: OpenTelemetry = ...
val client = HttpClient {
install(KtorClientTracing) {
install(KtorClientTelemetry) {
setOpenTelemetry(openTelemetry)
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0
import io.ktor.client.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.util.*
import io.opentelemetry.context.propagation.ContextPropagators
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorClientTelemetry
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorClientTelemetryUtil
class KtorClientTelemetry internal constructor(
instrumenter: Instrumenter<HttpRequestData, HttpResponse>,
propagators: ContextPropagators
) : AbstractKtorClientTelemetry(instrumenter, propagators) {
companion object : HttpClientPlugin<KtorClientTelemetryBuilder, KtorClientTelemetry> {
override val key = AttributeKey<KtorClientTelemetry>("OpenTelemetry")
override fun prepare(block: KtorClientTelemetryBuilder.() -> Unit) = KtorClientTelemetryBuilder().apply(block).build()
override fun install(plugin: KtorClientTelemetry, scope: HttpClient) {
KtorClientTelemetryUtil.install(plugin, scope)
}
}
}

View File

@ -0,0 +1,17 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorClientTelemetryBuilder
import io.opentelemetry.instrumentation.ktor.v3_0.InstrumentationProperties.INSTRUMENTATION_NAME
class KtorClientTelemetryBuilder : AbstractKtorClientTelemetryBuilder(INSTRUMENTATION_NAME) {
internal fun build(): KtorClientTelemetry = KtorClientTelemetry(
instrumenter = builder.build(),
propagators = getOpenTelemetry().propagators,
)
}

View File

@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0
import io.ktor.server.application.*
import io.ktor.server.routing.*
import io.opentelemetry.context.Context
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource
import io.opentelemetry.instrumentation.ktor.v2_0.common.AbstractKtorServerTelemetryBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorServerTelemetryUtil
import io.opentelemetry.instrumentation.ktor.v3_0.InstrumentationProperties.INSTRUMENTATION_NAME
class KtorServerTelemetryBuilder internal constructor(
instrumentationName: String
) : AbstractKtorServerTelemetryBuilder(instrumentationName)
val KtorServerTelemetry = createRouteScopedPlugin("OpenTelemetry", { KtorServerTelemetryBuilder(INSTRUMENTATION_NAME) }) {
require(pluginConfig.isOpenTelemetryInitialized()) { "OpenTelemetry must be set" }
KtorServerTelemetryUtil.configureTelemetry(pluginConfig, application)
application.monitor.subscribe(RoutingRoot.RoutingCallStarted) { call ->
HttpServerRoute.update(Context.current(), HttpServerRouteSource.SERVER, { _, arg -> arg.route.parent.toString() }, call)
}
}

View File

@ -12,9 +12,10 @@ import io.ktor.client.statement.*
import io.ktor.util.*
import io.opentelemetry.context.propagation.ContextPropagators
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter
import io.opentelemetry.instrumentation.ktor.client.AbstractKtorClientTracing
import io.opentelemetry.instrumentation.ktor.internal.KtorClientTracingUtil
import io.opentelemetry.instrumentation.ktor.v2_0.common.client.AbstractKtorClientTracing
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorClientTracingUtil
@Deprecated("Use KtorClientTelemetry instead", ReplaceWith("KtorClientTelemetry"))
class KtorClientTracing internal constructor(
instrumenter: Instrumenter<HttpRequestData, HttpResponse>,
propagators: ContextPropagators

View File

@ -5,9 +5,10 @@
package io.opentelemetry.instrumentation.ktor.v3_0.client
import io.opentelemetry.instrumentation.ktor.client.AbstractKtorClientTracingBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.common.client.AbstractKtorClientTracingBuilder
import io.opentelemetry.instrumentation.ktor.v3_0.InstrumentationProperties.INSTRUMENTATION_NAME
@Deprecated("Use KtorClientTelemetryBuilder instead", ReplaceWith("KtorClientTelemetryBuilder"))
class KtorClientTracingBuilder : AbstractKtorClientTracingBuilder(INSTRUMENTATION_NAME) {
internal fun build(): KtorClientTracing = KtorClientTracing(

View File

@ -10,10 +10,11 @@ import io.ktor.server.routing.*
import io.opentelemetry.context.Context
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource
import io.opentelemetry.instrumentation.ktor.internal.KtorServerTracingUtil
import io.opentelemetry.instrumentation.ktor.server.AbstractKtorServerTracingBuilder
import io.opentelemetry.instrumentation.ktor.v2_0.common.internal.KtorServerTracingUtil
import io.opentelemetry.instrumentation.ktor.v2_0.common.server.AbstractKtorServerTracingBuilder
import io.opentelemetry.instrumentation.ktor.v3_0.InstrumentationProperties.INSTRUMENTATION_NAME
@Deprecated("Use KtorServerTelemetryBuilder instead", ReplaceWith("KtorServerTelemetryBuilder"))
class KtorServerTracingBuilder internal constructor(
instrumentationName: String
) : AbstractKtorServerTracingBuilder(instrumentationName)

View File

@ -0,0 +1,28 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0
import io.ktor.client.*
import io.opentelemetry.instrumentation.ktor.v3_0.client.KtorClientTracing
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension
import org.junit.jupiter.api.extension.RegisterExtension
class KtorHttpClientOldTest : AbstractKtorHttpClientTest() {
companion object {
@JvmStatic
@RegisterExtension
private val TESTING = HttpClientInstrumentationExtension.forLibrary()
}
override fun HttpClientConfig<*>.installTracing() {
install(KtorClientTracing) {
setOpenTelemetry(TESTING.openTelemetry)
capturedRequestHeaders(TEST_REQUEST_HEADER)
capturedResponseHeaders(TEST_RESPONSE_HEADER)
}
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v2_0.client
package io.opentelemetry.instrumentation.ktor.v3_0
import io.ktor.client.*
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension
@ -18,7 +18,7 @@ class KtorHttpClientTest : AbstractKtorHttpClientTest() {
}
override fun HttpClientConfig<*>.installTracing() {
install(KtorClientTracing) {
install(KtorClientTelemetry) {
setOpenTelemetry(TESTING.openTelemetry)
capturedRequestHeaders(TEST_REQUEST_HEADER)
capturedResponseHeaders(TEST_RESPONSE_HEADER)

View File

@ -0,0 +1,35 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0
import io.ktor.server.application.*
import io.opentelemetry.instrumentation.ktor.v3_0.server.KtorServerTracing
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension
import org.junit.jupiter.api.extension.RegisterExtension
class KtorHttpServerOldTest : AbstractKtorHttpServerTest() {
companion object {
@JvmStatic
@RegisterExtension
val TESTING: InstrumentationExtension = HttpServerInstrumentationExtension.forLibrary()
}
override fun getTesting(): InstrumentationExtension {
return TESTING
}
override fun installOpenTelemetry(application: Application) {
application.apply {
install(KtorServerTracing) {
setOpenTelemetry(TESTING.openTelemetry)
capturedRequestHeaders(TEST_REQUEST_HEADER)
capturedResponseHeaders(TEST_RESPONSE_HEADER)
}
}
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0.server
package io.opentelemetry.instrumentation.ktor.v3_0
import io.ktor.server.application.*
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension
@ -24,7 +24,7 @@ class KtorHttpServerTest : AbstractKtorHttpServerTest() {
override fun installOpenTelemetry(application: Application) {
application.apply {
install(KtorServerTracing) {
install(KtorServerTelemetry) {
setOpenTelemetry(TESTING.openTelemetry)
capturedRequestHeaders(TEST_REQUEST_HEADER)
capturedResponseHeaders(TEST_RESPONSE_HEADER)

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0.client
package io.opentelemetry.instrumentation.ktor.v3_0
import io.ktor.client.*
import io.ktor.client.engine.cio.*
@ -17,7 +17,10 @@ import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES
import io.opentelemetry.semconv.NetworkAttributes
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.AfterAll
import java.net.URI

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0.server
package io.opentelemetry.instrumentation.ktor.v3_0
import io.ktor.http.*
import io.ktor.server.application.*

View File

@ -3,10 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ktor.v3_0.client
package io.opentelemetry.instrumentation.ktor.v3_0
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.request.*
import io.opentelemetry.instrumentation.testing.junit.http.SingleConnection
import kotlinx.coroutines.runBlocking