From 3defa4a36b3e1e285a97910180e9eb95f22d0eac Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Sun, 8 Dec 2024 13:17:05 -0800 Subject: [PATCH] Rename ktor tracing to ktor telemetry --- .../ktor/v1_0/KtorServerTelemetry.kt | 169 +++++++++++++++ .../ktor/v1_0/KtorServerTracing.kt | 1 + .../v1_0/KtorServerSpanKindExtractorTest.kt | 2 +- .../instrumentation/ktor/v1_0/KtorTestUtil.kt | 2 +- .../client/AbstractKtorClientTelemetry.kt | 42 ++++ .../AbstractKtorClientTelemetryBuilder.kt | 121 +++++++++++ .../ktor/client/AbstractKtorClientTracing.kt | 1 + .../AbstractKtorClientTracingBuilder.kt | 8 +- .../ktor/client/internal/Experimental.kt | 52 +++++ .../ktor/internal/KtorBuilderUtil.kt | 8 +- .../ktor/internal/KtorBuilderUtilOld.kt | 27 +++ .../ktor/internal/KtorClientTelemetryUtil.kt | 88 ++++++++ .../ktor/internal/KtorClientTracingUtil.kt | 1 + .../ktor/internal/KtorServerTelemetryUtil.kt | 83 ++++++++ .../ktor/internal/KtorServerTracingUtil.kt | 1 + .../AbstractKtorServerTelemetryBuilder.kt | 195 ++++++++++++++++++ .../AbstractKtorServerTracingBuilder.kt | 2 +- .../ktor/v2_0/HttpClientInstrumentation.java | 10 +- .../ktor/v2_0/ServerInstrumentation.java | 10 +- .../ktor/v2_0/client/KtorClientTelemetry.kt | 33 +++ .../v2_0/client/KtorClientTelemetryBuilder.kt | 17 ++ .../ktor/v2_0/client/KtorClientTracing.kt | 1 + .../v2_0/client/KtorClientTracingBuilder.kt | 1 + .../v2_0/server/KtorServerTelemetryBuilder.kt | 29 +++ .../v2_0/server/KtorServerTracingBuilder.kt | 1 + .../ktor/v3_0/HttpClientInstrumentation.java | 10 +- .../ktor/v3_0/ServerInstrumentation.java | 10 +- .../ktor/v3_0/client/KtorClientTelemetry.kt | 33 +++ .../v3_0/client/KtorClientTelemetryBuilder.kt | 17 ++ .../ktor/v3_0/client/KtorClientTracing.kt | 1 + .../v3_0/client/KtorClientTracingBuilder.kt | 1 + .../v3_0/server/KtorServerTelemetryBuilder.kt | 29 +++ .../v3_0/server/KtorServerTracingBuilder.kt | 1 + 33 files changed, 977 insertions(+), 30 deletions(-) create mode 100644 instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTelemetry.kt create mode 100644 instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTelemetry.kt create mode 100644 instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTelemetryBuilder.kt create mode 100644 instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/internal/Experimental.kt create mode 100644 instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorBuilderUtilOld.kt create mode 100644 instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorClientTelemetryUtil.kt create mode 100644 instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorServerTelemetryUtil.kt create mode 100644 instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/server/AbstractKtorServerTelemetryBuilder.kt create mode 100644 instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTelemetry.kt create mode 100644 instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTelemetryBuilder.kt create mode 100644 instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTelemetryBuilder.kt create mode 100644 instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTelemetry.kt create mode 100644 instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTelemetryBuilder.kt create mode 100644 instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/server/KtorServerTelemetryBuilder.kt diff --git a/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTelemetry.kt b/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTelemetry.kt new file mode 100644 index 000000000000..1ecdf34b18d9 --- /dev/null +++ b/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTelemetry.kt @@ -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, +) { + + class Configuration { + internal lateinit var builder: DefaultHttpServerInstrumenterBuilder + + internal var spanKindExtractor: + (SpanKindExtractor) -> SpanKindExtractor = { a -> a } + + fun setOpenTelemetry(openTelemetry: OpenTelemetry) { + this.builder = + DefaultHttpServerInstrumenterBuilder.create( + INSTRUMENTATION_NAME, + openTelemetry, + KtorHttpServerAttributesGetter.INSTANCE + ) + } + + fun setStatusExtractor( + extractor: (SpanStatusExtractor) -> SpanStatusExtractor + ) { + builder.setStatusExtractor { prevExtractor -> + SpanStatusExtractor { spanStatusBuilder: SpanStatusBuilder, + request: ApplicationRequest, + response: ApplicationResponse?, + throwable: Throwable? -> + extractor(prevExtractor).extract(spanStatusBuilder, request, response, throwable) + } + } + } + + fun setSpanKindExtractor(extractor: (SpanKindExtractor) -> SpanKindExtractor) { + this.spanKindExtractor = extractor + } + + fun addAttributeExtractor(extractor: AttributesExtractor) { + builder.addAttributesExtractor(extractor) + } + + fun setCapturedRequestHeaders(requestHeaders: List) { + builder.setCapturedRequestHeaders(requestHeaders) + } + + fun setCapturedResponseHeaders(responseHeaders: List) { + builder.setCapturedResponseHeaders(responseHeaders) + } + + fun setKnownMethods(knownMethods: Set) { + 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 { + private const val INSTRUMENTATION_NAME = "io.opentelemetry.ktor-1.0" + + private val contextKey = AttributeKey("OpenTelemetry") + private val errorKey = AttributeKey("OpenTelemetryException") + + override val key: AttributeKey = 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 + } + } +} diff --git a/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTracing.kt b/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTracing.kt index 844cbe2895fd..1073053d0639 100644 --- a/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTracing.kt +++ b/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTracing.kt @@ -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, ) { diff --git a/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerSpanKindExtractorTest.kt b/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerSpanKindExtractorTest.kt index 364c67a9e1a5..a494ed33aced 100644 --- a/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerSpanKindExtractorTest.kt +++ b/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerSpanKindExtractorTest.kt @@ -58,7 +58,7 @@ class KtorServerSpanKindExtractorTest : AbstractHttpServerUsingTest diff --git a/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorTestUtil.kt b/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorTestUtil.kt index 6daf7382ec40..85fd9d6b78fa 100644 --- a/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorTestUtil.kt +++ b/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorTestUtil.kt @@ -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)) diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTelemetry.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTelemetry.kt new file mode 100644 index 000000000000..cfff0ef78119 --- /dev/null +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTelemetry.kt @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.client + +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, + 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) + } +} diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTelemetryBuilder.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTelemetryBuilder.kt new file mode 100644 index 000000000000..eddeec494faa --- /dev/null +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTelemetryBuilder.kt @@ -0,0 +1,121 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.client + +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.ktor.internal.KtorBuilderUtil + +abstract class AbstractKtorClientTelemetryBuilder( + private val instrumentationName: String +) { + companion object { + init { + KtorBuilderUtil.clientBuilderExtractor = { it.clientBuilder } + } + } + + internal lateinit var openTelemetry: OpenTelemetry + protected lateinit var clientBuilder: DefaultHttpClientInstrumenterBuilder + + fun setOpenTelemetry(openTelemetry: OpenTelemetry) { + this.openTelemetry = openTelemetry + this.clientBuilder = DefaultHttpClientInstrumenterBuilder.create( + instrumentationName, + openTelemetry, + KtorHttpClientAttributesGetter + ) + } + + protected fun getOpenTelemetry(): OpenTelemetry { + return openTelemetry + } + + fun capturedRequestHeaders(vararg headers: String) = capturedRequestHeaders(headers.asIterable()) + + fun capturedRequestHeaders(headers: Iterable) { + clientBuilder.setCapturedRequestHeaders(headers.toList()) + } + + fun capturedResponseHeaders(vararg headers: String) = capturedResponseHeaders(headers.asIterable()) + + fun capturedResponseHeaders(headers: Iterable) { + clientBuilder.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) = knownMethods(methods.map { it.value }) + + fun knownMethods(methods: Iterable) { + clientBuilder.setKnownMethods(methods.toSet()) + } + + fun attributeExtractor(extractorBuilder: ExtractorBuilder.() -> Unit = {}) { + val builder = ExtractorBuilder().apply(extractorBuilder).build() + this.clientBuilder.addAttributeExtractor( + object : AttributesExtractor { + 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)) + } + } + ) + } + + 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? + ) + + /** + * Can be used via the unstable method {@link + * Experimental#setEmitExperimentalHttpClientMetrics(AbstractKtorClientTelemetryBuilder, boolean)}. + */ + internal fun emitExperimentalHttpClientMetrics() { + clientBuilder.setEmitExperimentalHttpClientMetrics(true) + } +} diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTracing.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTracing.kt index 925cb6cf3156..0e1c099e4e7e 100644 --- a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTracing.kt +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTracing.kt @@ -12,6 +12,7 @@ import io.opentelemetry.context.Context import io.opentelemetry.context.propagation.ContextPropagators import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter +@Deprecated("Use AbstractKtorClientTelemetry instead", ReplaceWith("AbstractKtorClientTelemetry")) abstract class AbstractKtorClientTracing( private val instrumenter: Instrumenter, private val propagators: ContextPropagators, diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTracingBuilder.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTracingBuilder.kt index 3b99bba1f6af..410bacbd4340 100644 --- a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTracingBuilder.kt +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/AbstractKtorClientTracingBuilder.kt @@ -13,14 +13,15 @@ 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.ktor.internal.KtorBuilderUtil +import io.opentelemetry.instrumentation.ktor.internal.KtorBuilderUtilOld +@Deprecated("Use AbstractKtorClientTelemetryBuilder instead", ReplaceWith("AbstractKtorClientTelemetryBuilder")) abstract class AbstractKtorClientTracingBuilder( private val instrumentationName: String ) { companion object { init { - KtorBuilderUtil.clientBuilderExtractor = { it.clientBuilder } + KtorBuilderUtilOld.clientBuilderExtractor = { it.clientBuilder } } } @@ -159,13 +160,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.emitExperimentalHttpClientMetrics`") fun setEmitExperimentalHttpClientMetrics(emitExperimentalHttpClientMetrics: Boolean) { if (emitExperimentalHttpClientMetrics) { emitExperimentalHttpClientMetrics() } } + @Deprecated("Please use method `Experimental.emitExperimentalHttpClientMetrics`") fun emitExperimentalHttpClientMetrics() { clientBuilder.setEmitExperimentalHttpClientMetrics(true) } diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/internal/Experimental.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/internal/Experimental.kt new file mode 100644 index 000000000000..d929740cfdcc --- /dev/null +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/client/internal/Experimental.kt @@ -0,0 +1,52 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.client.internal + +import io.opentelemetry.instrumentation.ktor.client.AbstractKtorClientTelemetryBuilder +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import java.util.logging.Level +import java.util.logging.Logger + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +// TODO (trask) update the above javadoc similar to +// https://github.com/open-telemetry/opentelemetry-java/pull/6886 +class Experimental { + fun setEmitExperimentalHttpClientMetrics(builder: AbstractKtorClientTelemetryBuilder?, emitExperimentalHttpClientMetrics: Boolean) { + if (emitExperimentalHttpClientMetricsMethod != null) { + try { + emitExperimentalHttpClientMetricsMethod.invoke(builder, emitExperimentalHttpClientMetrics) + } catch (e: IllegalAccessException) { + logger.log(Level.FINE, e.message, e) + } catch (e: InvocationTargetException) { + logger.log(Level.FINE, e.message, e) + } + } + } + + companion object { + private val logger: Logger = Logger.getLogger(Experimental::class.java.name) + + private val emitExperimentalHttpClientMetricsMethod = getEmitExperimentalHttpClientMetricsMethod() + + fun getEmitExperimentalHttpClientMetricsMethod(): Method? { + try { + val method = AbstractKtorClientTelemetryBuilder::class.java.getMethod( + "setEmitExperimentalHttpClientMetrics", + Boolean::class.javaPrimitiveType + ) + method.setAccessible(true) + return method + } catch (e: NoSuchMethodException) { + logger.log(Level.FINE, e.message, e) + return null + } + } + } +} diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorBuilderUtil.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorBuilderUtil.kt index ec074cca66bd..ac3ee94db3fa 100644 --- a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorBuilderUtil.kt +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorBuilderUtil.kt @@ -11,16 +11,16 @@ 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.client.AbstractKtorClientTelemetryBuilder +import io.opentelemetry.instrumentation.ktor.server.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: (AbstractKtorClientTracingBuilder) -> DefaultHttpClientInstrumenterBuilder + lateinit var clientBuilderExtractor: (AbstractKtorClientTelemetryBuilder) -> DefaultHttpClientInstrumenterBuilder lateinit var serverBuilderExtractor: ( - AbstractKtorServerTracingBuilder + AbstractKtorServerTelemetryBuilder ) -> DefaultHttpServerInstrumenterBuilder } diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorBuilderUtilOld.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorBuilderUtilOld.kt new file mode 100644 index 000000000000..6e00c3d4a98d --- /dev/null +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorBuilderUtilOld.kt @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.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.client.AbstractKtorClientTracingBuilder +import io.opentelemetry.instrumentation.ktor.server.AbstractKtorServerTracingBuilder + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +@Deprecated("Use KtorBuilderUtil instead", ReplaceWith("KtorBuilderUtil")) +object KtorBuilderUtilOld { + lateinit var clientBuilderExtractor: (AbstractKtorClientTracingBuilder) -> DefaultHttpClientInstrumenterBuilder + lateinit var serverBuilderExtractor: ( + AbstractKtorServerTracingBuilder + ) -> DefaultHttpServerInstrumenterBuilder +} diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorClientTelemetryUtil.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorClientTelemetryUtil.kt new file mode 100644 index 000000000000..630f4bb14e2c --- /dev/null +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorClientTelemetryUtil.kt @@ -0,0 +1,88 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.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.client.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("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) + } + } + } +} diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorClientTracingUtil.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorClientTracingUtil.kt index b1735217a99a..4ed1d6d3fea1 100644 --- a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorClientTracingUtil.kt +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorClientTracingUtil.kt @@ -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("OpenTelemetry") diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorServerTelemetryUtil.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorServerTelemetryUtil.kt new file mode 100644 index 000000000000..7ec23c7923c5 --- /dev/null +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorServerTelemetryUtil.kt @@ -0,0 +1,83 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.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.server.AbstractKtorServerTelemetryBuilder +import io.opentelemetry.instrumentation.ktor.server.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("OpenTelemetry") + val errorKey = AttributeKey("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 { + return InstrumenterUtil.buildUpstreamInstrumenter( + builder.serverBuilder.instrumenterBuilder(), + ApplicationRequestGetter, + builder.spanKindExtractor(SpanKindExtractor.alwaysServer()) + ) + } +} diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorServerTracingUtil.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorServerTracingUtil.kt index e5e3ea4fc908..975803e11ee2 100644 --- a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorServerTracingUtil.kt +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/internal/KtorServerTracingUtil.kt @@ -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 KtorServerTelemetryUtil instead", ReplaceWith("KtorServerTelemetryUtil")) object KtorServerTracingUtil { fun configureTracing(builder: AbstractKtorServerTracingBuilder, application: Application) { diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/server/AbstractKtorServerTelemetryBuilder.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/server/AbstractKtorServerTelemetryBuilder.kt new file mode 100644 index 000000000000..2195e52584d0 --- /dev/null +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/server/AbstractKtorServerTelemetryBuilder.kt @@ -0,0 +1,195 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.server + +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.SpanStatusBuilder +import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor +import io.opentelemetry.instrumentation.ktor.internal.KtorBuilderUtil + +abstract class AbstractKtorServerTelemetryBuilder(private val instrumentationName: String) { + companion object { + init { + KtorBuilderUtil.serverBuilderExtractor = { it.serverBuilder } + } + } + + internal lateinit var serverBuilder: DefaultHttpServerInstrumenterBuilder + + internal var spanKindExtractor: + (SpanKindExtractor) -> SpanKindExtractor = { a -> a } + + fun setOpenTelemetry(openTelemetry: OpenTelemetry) { + this.serverBuilder = + DefaultHttpServerInstrumenterBuilder.create( + instrumentationName, + openTelemetry, + KtorHttpServerAttributesGetter.INSTANCE + ) + } + + @Deprecated("Please use method `spanStatusExtractor`") + fun setStatusExtractor( + extractor: (SpanStatusExtractor) -> SpanStatusExtractor + ) { + spanStatusExtractor { prevStatusExtractor -> + extractor(prevStatusExtractor).extract(spanStatusBuilder, request, response, error) + } + } + + fun spanStatusExtractor(extract: SpanStatusData.(SpanStatusExtractor) -> Unit) { + serverBuilder.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? + ) + + @Deprecated("Please use method `spanKindExtractor`") + fun setSpanKindExtractor(extractor: (SpanKindExtractor) -> SpanKindExtractor) { + spanKindExtractor { prevSpanKindExtractor -> + extractor(prevSpanKindExtractor).extract(this) + } + } + + fun spanKindExtractor(extract: ApplicationRequest.(SpanKindExtractor) -> SpanKind) { + spanKindExtractor = { prevExtractor -> + SpanKindExtractor { request: ApplicationRequest -> + extract(request, prevExtractor) + } + } + } + + @Deprecated("Please use method `attributeExtractor`") + fun addAttributeExtractor(extractor: AttributesExtractor) { + attributeExtractor { + onStart { + extractor.onStart(attributes, parentContext, request) + } + onEnd { + extractor.onEnd(attributes, parentContext, request, response, error) + } + } + } + + fun attributeExtractor(extractorBuilder: ExtractorBuilder.() -> Unit = {}) { + val builder = ExtractorBuilder().apply(extractorBuilder).build() + serverBuilder.addAttributesExtractor( + object : AttributesExtractor { + 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)) + } + } + ) + } + + 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? + ) + + @Deprecated( + "Please use method `capturedRequestHeaders`", + ReplaceWith("capturedRequestHeaders(headers)") + ) + fun setCapturedRequestHeaders(headers: List) = capturedRequestHeaders(headers) + + fun capturedRequestHeaders(vararg headers: String) = capturedRequestHeaders(headers.asIterable()) + + fun capturedRequestHeaders(headers: Iterable) { + serverBuilder.setCapturedRequestHeaders(headers.toList()) + } + + @Deprecated( + "Please use method `capturedResponseHeaders`", + ReplaceWith("capturedResponseHeaders(headers)") + ) + fun setCapturedResponseHeaders(headers: List) = capturedResponseHeaders(headers) + + fun capturedResponseHeaders(vararg headers: String) = capturedResponseHeaders(headers.asIterable()) + + fun capturedResponseHeaders(headers: Iterable) { + serverBuilder.setCapturedResponseHeaders(headers.toList()) + } + + @Deprecated( + "Please use method `knownMethods`", + ReplaceWith("knownMethods(knownMethods)") + ) + fun setKnownMethods(knownMethods: Set) = knownMethods(knownMethods) + + fun knownMethods(vararg methods: String) = knownMethods(methods.asIterable()) + + fun knownMethods(vararg methods: HttpMethod) = knownMethods(methods.asIterable()) + + @JvmName("knownMethodsJvm") + fun knownMethods(methods: Iterable) = knownMethods(methods.map { it.value }) + + fun knownMethods(methods: Iterable) { + methods.toSet().apply { + serverBuilder.setKnownMethods(this) + } + } + + /** + * {@link #setOpenTelemetry(OpenTelemetry)} sets the serverBuilder to a non-null value. + */ + fun isOpenTelemetryInitialized(): Boolean = this::serverBuilder.isInitialized +} diff --git a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/server/AbstractKtorServerTracingBuilder.kt b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/server/AbstractKtorServerTracingBuilder.kt index 65abba99460c..b3d3217d9a9f 100644 --- a/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/server/AbstractKtorServerTracingBuilder.kt +++ b/instrumentation/ktor/ktor-2-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/server/AbstractKtorServerTracingBuilder.kt @@ -6,7 +6,6 @@ package io.opentelemetry.instrumentation.ktor.server import io.ktor.http.* -import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.opentelemetry.api.OpenTelemetry @@ -20,6 +19,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor import io.opentelemetry.instrumentation.ktor.internal.KtorBuilderUtil +@Deprecated("Use AbstractKtorServerTelemetryBuilder instead", ReplaceWith("AbstractKtorServerTelemetryBuilder")) abstract class AbstractKtorServerTracingBuilder(private val instrumentationName: String) { companion object { init { diff --git a/instrumentation/ktor/ktor-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v2_0/HttpClientInstrumentation.java b/instrumentation/ktor/ktor-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v2_0/HttpClientInstrumentation.java index 79fb525fdb7a..f1c116177394 100644 --- a/instrumentation/ktor/ktor-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v2_0/HttpClientInstrumentation.java +++ b/instrumentation/ktor/ktor-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v2_0/HttpClientInstrumentation.java @@ -14,8 +14,8 @@ 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.client.KtorClientTelemetry; +import io.opentelemetry.instrumentation.ktor.v2_0.client.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 static class ConstructorAdvice { @Advice.OnMethodEnter public static void onEnter( @Advice.Argument(1) HttpClientConfig httpClientConfig) { - httpClientConfig.install(KtorClientTracing.Companion, new SetupFunction()); + httpClientConfig.install(KtorClientTelemetry.Companion, new SetupFunction()); } } - public static class SetupFunction implements Function1 { + public static class SetupFunction implements Function1 { @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; diff --git a/instrumentation/ktor/ktor-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v2_0/ServerInstrumentation.java b/instrumentation/ktor/ktor-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v2_0/ServerInstrumentation.java index 214d94d80df0..94463567087a 100644 --- a/instrumentation/ktor/ktor-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v2_0/ServerInstrumentation.java +++ b/instrumentation/ktor/ktor-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v2_0/ServerInstrumentation.java @@ -12,8 +12,8 @@ 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.server.AbstractKtorServerTelemetryBuilder; +import io.opentelemetry.instrumentation.ktor.v2_0.server.KtorServerTelemetryBuilderKt; 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 static class ConstructorAdvice { @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 { + implements Function1 { @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; diff --git a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTelemetry.kt b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTelemetry.kt new file mode 100644 index 000000000000..69e25b2349b9 --- /dev/null +++ b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTelemetry.kt @@ -0,0 +1,33 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.v2_0.client + +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.client.AbstractKtorClientTelemetry +import io.opentelemetry.instrumentation.ktor.internal.KtorClientTelemetryUtil + +class KtorClientTelemetry internal constructor( + instrumenter: Instrumenter, + propagators: ContextPropagators +) : AbstractKtorClientTelemetry(instrumenter, propagators) { + + companion object : HttpClientPlugin { + + override val key = AttributeKey("OpenTelemetry") + + override fun prepare(block: KtorClientTelemetryBuilder.() -> Unit) = KtorClientTelemetryBuilder().apply(block).build() + + override fun install(plugin: KtorClientTelemetry, scope: HttpClient) { + KtorClientTelemetryUtil.install(plugin, scope) + } + } +} diff --git a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTelemetryBuilder.kt b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTelemetryBuilder.kt new file mode 100644 index 000000000000..38abca4a477a --- /dev/null +++ b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTelemetryBuilder.kt @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.v2_0.client + +import io.opentelemetry.instrumentation.ktor.client.AbstractKtorClientTelemetryBuilder +import io.opentelemetry.instrumentation.ktor.v2_0.InstrumentationProperties.INSTRUMENTATION_NAME + +class KtorClientTelemetryBuilder : AbstractKtorClientTelemetryBuilder(INSTRUMENTATION_NAME) { + + internal fun build(): KtorClientTelemetry = KtorClientTelemetry( + instrumenter = clientBuilder.build(), + propagators = getOpenTelemetry().propagators, + ) +} diff --git a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracing.kt b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracing.kt index 429e2ccdfe0c..b7e4b17e927e 100644 --- a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracing.kt +++ b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracing.kt @@ -15,6 +15,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter import io.opentelemetry.instrumentation.ktor.client.AbstractKtorClientTracing import io.opentelemetry.instrumentation.ktor.internal.KtorClientTracingUtil +@Deprecated("Use KtorClientTelemetry instead", ReplaceWith("KtorClientTelemetry")) class KtorClientTracing internal constructor( instrumenter: Instrumenter, propagators: ContextPropagators diff --git a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracingBuilder.kt b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracingBuilder.kt index b93fb82f02da..fc0b04d705cb 100644 --- a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracingBuilder.kt +++ b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/client/KtorClientTracingBuilder.kt @@ -8,6 +8,7 @@ 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 +@Deprecated("Use KtorClientTelemetryBuilder instead", ReplaceWith("KtorClientTelemetryBuilder")) class KtorClientTracingBuilder : AbstractKtorClientTracingBuilder(INSTRUMENTATION_NAME) { internal fun build(): KtorClientTracing = KtorClientTracing( diff --git a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTelemetryBuilder.kt b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTelemetryBuilder.kt new file mode 100644 index 000000000000..aba80c987944 --- /dev/null +++ b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTelemetryBuilder.kt @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.v2_0.server + +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.internal.KtorServerTelemetryUtil +import io.opentelemetry.instrumentation.ktor.server.AbstractKtorServerTelemetryBuilder +import io.opentelemetry.instrumentation.ktor.v2_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.environment.monitor.subscribe(Routing.RoutingCallStarted) { call -> + HttpServerRoute.update(Context.current(), HttpServerRouteSource.SERVER, { _, arg -> arg.route.parent.toString() }, call) + } +} diff --git a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTracingBuilder.kt b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTracingBuilder.kt index b2c4be9b86db..811a4d862f91 100644 --- a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTracingBuilder.kt +++ b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTracingBuilder.kt @@ -14,6 +14,7 @@ import io.opentelemetry.instrumentation.ktor.internal.KtorServerTracingUtil import io.opentelemetry.instrumentation.ktor.server.AbstractKtorServerTracingBuilder import io.opentelemetry.instrumentation.ktor.v2_0.InstrumentationProperties.INSTRUMENTATION_NAME +@Deprecated("Use KtorServerTelemetryBuilder instead", ReplaceWith("KtorServerTelemetryBuilder")) class KtorServerTracingBuilder internal constructor( instrumentationName: String ) : AbstractKtorServerTracingBuilder(instrumentationName) diff --git a/instrumentation/ktor/ktor-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v3_0/HttpClientInstrumentation.java b/instrumentation/ktor/ktor-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v3_0/HttpClientInstrumentation.java index 09dfcca67e20..8920f0d249ab 100644 --- a/instrumentation/ktor/ktor-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v3_0/HttpClientInstrumentation.java +++ b/instrumentation/ktor/ktor-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v3_0/HttpClientInstrumentation.java @@ -14,8 +14,8 @@ 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.v3_0.client.KtorClientTelemetry; +import io.opentelemetry.instrumentation.ktor.v3_0.client.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 static class ConstructorAdvice { @Advice.OnMethodEnter public static void onEnter( @Advice.Argument(1) HttpClientConfig httpClientConfig) { - httpClientConfig.install(KtorClientTracing.Companion, new SetupFunction()); + httpClientConfig.install(KtorClientTelemetry.Companion, new SetupFunction()); } } - public static class SetupFunction implements Function1 { + public static class SetupFunction implements Function1 { @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; diff --git a/instrumentation/ktor/ktor-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v3_0/ServerInstrumentation.java b/instrumentation/ktor/ktor-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v3_0/ServerInstrumentation.java index df4335fa686d..eb0639c54dbb 100644 --- a/instrumentation/ktor/ktor-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v3_0/ServerInstrumentation.java +++ b/instrumentation/ktor/ktor-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ktor/v3_0/ServerInstrumentation.java @@ -12,8 +12,8 @@ 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.server.AbstractKtorServerTelemetryBuilder; +import io.opentelemetry.instrumentation.ktor.v3_0.server.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 static class ConstructorAdvice { @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 { + public static class SetupFunction implements Function1 { @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; diff --git a/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTelemetry.kt b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTelemetry.kt new file mode 100644 index 000000000000..d15f78fc3c80 --- /dev/null +++ b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTelemetry.kt @@ -0,0 +1,33 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.v3_0.client + +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.client.AbstractKtorClientTelemetry +import io.opentelemetry.instrumentation.ktor.internal.KtorClientTelemetryUtil + +class KtorClientTelemetry internal constructor( + instrumenter: Instrumenter, + propagators: ContextPropagators +) : AbstractKtorClientTelemetry(instrumenter, propagators) { + + companion object : HttpClientPlugin { + + override val key = AttributeKey("OpenTelemetry") + + override fun prepare(block: KtorClientTelemetryBuilder.() -> Unit) = KtorClientTelemetryBuilder().apply(block).build() + + override fun install(plugin: KtorClientTelemetry, scope: HttpClient) { + KtorClientTelemetryUtil.install(plugin, scope) + } + } +} diff --git a/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTelemetryBuilder.kt b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTelemetryBuilder.kt new file mode 100644 index 000000000000..793a3e81becf --- /dev/null +++ b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTelemetryBuilder.kt @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.v3_0.client + +import io.opentelemetry.instrumentation.ktor.client.AbstractKtorClientTelemetryBuilder +import io.opentelemetry.instrumentation.ktor.v3_0.InstrumentationProperties.INSTRUMENTATION_NAME + +class KtorClientTelemetryBuilder : AbstractKtorClientTelemetryBuilder(INSTRUMENTATION_NAME) { + + internal fun build(): KtorClientTelemetry = KtorClientTelemetry( + instrumenter = clientBuilder.build(), + propagators = getOpenTelemetry().propagators, + ) +} diff --git a/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTracing.kt b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTracing.kt index d5bf578cc56c..623e31c5693c 100644 --- a/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTracing.kt +++ b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTracing.kt @@ -15,6 +15,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter import io.opentelemetry.instrumentation.ktor.client.AbstractKtorClientTracing import io.opentelemetry.instrumentation.ktor.internal.KtorClientTracingUtil +@Deprecated("Use KtorClientTelemetry instead", ReplaceWith("KtorClientTelemetry")) class KtorClientTracing internal constructor( instrumenter: Instrumenter, propagators: ContextPropagators diff --git a/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTracingBuilder.kt b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTracingBuilder.kt index 6f68939d9f06..64134a656a27 100644 --- a/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTracingBuilder.kt +++ b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/client/KtorClientTracingBuilder.kt @@ -8,6 +8,7 @@ package io.opentelemetry.instrumentation.ktor.v3_0.client import io.opentelemetry.instrumentation.ktor.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( diff --git a/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/server/KtorServerTelemetryBuilder.kt b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/server/KtorServerTelemetryBuilder.kt new file mode 100644 index 000000000000..a344e98a7593 --- /dev/null +++ b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/server/KtorServerTelemetryBuilder.kt @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.ktor.v3_0.server + +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.internal.KtorServerTelemetryUtil +import io.opentelemetry.instrumentation.ktor.server.AbstractKtorServerTelemetryBuilder +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) + } +} diff --git a/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/server/KtorServerTracingBuilder.kt b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/server/KtorServerTracingBuilder.kt index a086f7473fd8..be04c921979e 100644 --- a/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/server/KtorServerTracingBuilder.kt +++ b/instrumentation/ktor/ktor-3.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v3_0/server/KtorServerTracingBuilder.kt @@ -14,6 +14,7 @@ import io.opentelemetry.instrumentation.ktor.internal.KtorServerTracingUtil import io.opentelemetry.instrumentation.ktor.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)