From 6e2d4046333699bb9718ddfe9a89835db94d3768 Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Fri, 7 Feb 2025 15:32:02 -0500 Subject: [PATCH 1/6] Replace `Span.makeCurrent()` with `Span.asContextElement()` --- gradle/libs.versions.toml | 1 + .../observability/telemetry-api/api/telemetry-api.api | 2 ++ .../kotlin/runtime/telemetry/trace/AbstractTraceSpan.kt | 3 +++ .../runtime/telemetry/trace/CoroutineContextTraceExt.kt | 2 +- .../smithy/kotlin/runtime/telemetry/trace/TraceSpan.kt | 3 +++ .../telemetry-provider-otel/build.gradle.kts | 1 + .../kotlin/runtime/telemetry/otel/OtelTracerProvider.kt | 9 +++++---- 7 files changed, 16 insertions(+), 5 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bdc0c8e98b..235d2a5266 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -57,6 +57,7 @@ okhttp4 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp4-versi okhttp-coroutines = { module = "com.squareup.okhttp3:okhttp-coroutines", version.ref = "okhttp-version" } opentelemetry-api = { module = "io.opentelemetry:opentelemetry-api", version.ref = "otel-version" } opentelemetry-sdk-testing = {module = "io.opentelemetry:opentelemetry-sdk-testing", version.ref = "otel-version" } +opentelemetry-kotlin-extension = { module = "io.opentelemetry:opentelemetry-extension-kotlin", version.ref = "otel-version" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j-version" } slf4j-api-v1x = { module = "org.slf4j:slf4j-api", version.ref = "slf4j-v1x-version" } slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j-version" } diff --git a/runtime/observability/telemetry-api/api/telemetry-api.api b/runtime/observability/telemetry-api/api/telemetry-api.api index 33adef236a..b4c26f2963 100644 --- a/runtime/observability/telemetry-api/api/telemetry-api.api +++ b/runtime/observability/telemetry-api/api/telemetry-api.api @@ -364,6 +364,7 @@ public final class aws/smithy/kotlin/runtime/telemetry/metrics/UpDownCounter$Def public abstract class aws/smithy/kotlin/runtime/telemetry/trace/AbstractTraceSpan : aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan { public fun ()V + public fun asContextElement ()Lkotlin/coroutines/CoroutineContext; public fun close ()V public fun emitEvent (Ljava/lang/String;Laws/smithy/kotlin/runtime/collections/Attributes;)V public fun getSpanContext ()Laws/smithy/kotlin/runtime/telemetry/trace/SpanContext; @@ -424,6 +425,7 @@ public final class aws/smithy/kotlin/runtime/telemetry/trace/SpanStatus : java/l public abstract interface class aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan : aws/smithy/kotlin/runtime/telemetry/context/Scope { public static final field Companion Laws/smithy/kotlin/runtime/telemetry/trace/TraceSpan$Companion; + public abstract fun asContextElement ()Lkotlin/coroutines/CoroutineContext; public abstract fun close ()V public abstract fun emitEvent (Ljava/lang/String;Laws/smithy/kotlin/runtime/collections/Attributes;)V public abstract fun getSpanContext ()Laws/smithy/kotlin/runtime/telemetry/trace/SpanContext; diff --git a/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/AbstractTraceSpan.kt b/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/AbstractTraceSpan.kt index fbc6369ec3..1892098b7b 100644 --- a/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/AbstractTraceSpan.kt +++ b/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/AbstractTraceSpan.kt @@ -6,6 +6,8 @@ package aws.smithy.kotlin.runtime.telemetry.trace import aws.smithy.kotlin.runtime.collections.AttributeKey import aws.smithy.kotlin.runtime.collections.Attributes +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext /** * An abstract implementation of a trace span. By default, this class uses no-op implementations for all members unless @@ -18,4 +20,5 @@ public abstract class AbstractTraceSpan : TraceSpan { override operator fun set(key: AttributeKey, value: T) { } override fun mergeAttributes(attributes: Attributes) { } override fun close() { } + override fun asContextElement(): CoroutineContext = EmptyCoroutineContext } diff --git a/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/CoroutineContextTraceExt.kt b/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/CoroutineContextTraceExt.kt index cb6795fe9c..73e84dd756 100644 --- a/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/CoroutineContextTraceExt.kt +++ b/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/CoroutineContextTraceExt.kt @@ -71,7 +71,7 @@ public suspend inline fun withSpan( // or else traces may be disconnected from their parent val updatedCtx = coroutineContext[TelemetryProviderContext]?.provider?.contextManager?.current() val telemetryCtxElement = (updatedCtx?.let { TelemetryContextElement(it) } ?: coroutineContext[TelemetryContextElement]) ?: EmptyCoroutineContext - withContext(context + TraceSpanContext(span) + telemetryCtxElement) { + withContext(context + TraceSpanContext(span) + telemetryCtxElement + span.asContextElement()) { block(span) } } catch (ex: Exception) { diff --git a/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan.kt b/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan.kt index d3c7972a85..77488bd686 100644 --- a/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan.kt +++ b/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan.kt @@ -9,6 +9,7 @@ import aws.smithy.kotlin.runtime.collections.AttributeKey import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.collections.emptyAttributes import aws.smithy.kotlin.runtime.telemetry.context.Scope +import kotlin.coroutines.CoroutineContext /** * Represents a single operation/task within a trace. Each trace contains a root span and @@ -27,6 +28,8 @@ public interface TraceSpan : Scope { */ public val spanContext: SpanContext + public fun asContextElement(): CoroutineContext + /** * Set an attribute on the span * @param key the attribute key to use diff --git a/runtime/observability/telemetry-provider-otel/build.gradle.kts b/runtime/observability/telemetry-provider-otel/build.gradle.kts index 8792275766..bdee5b1d6a 100644 --- a/runtime/observability/telemetry-provider-otel/build.gradle.kts +++ b/runtime/observability/telemetry-provider-otel/build.gradle.kts @@ -18,6 +18,7 @@ kotlin { jvmMain { dependencies { api(libs.opentelemetry.api) + api(libs.opentelemetry.kotlin.extension) } } all { diff --git a/runtime/observability/telemetry-provider-otel/jvm/src/aws/smithy/kotlin/runtime/telemetry/otel/OtelTracerProvider.kt b/runtime/observability/telemetry-provider-otel/jvm/src/aws/smithy/kotlin/runtime/telemetry/otel/OtelTracerProvider.kt index cf6eb39dfd..20ef72f139 100644 --- a/runtime/observability/telemetry-provider-otel/jvm/src/aws/smithy/kotlin/runtime/telemetry/otel/OtelTracerProvider.kt +++ b/runtime/observability/telemetry-provider-otel/jvm/src/aws/smithy/kotlin/runtime/telemetry/otel/OtelTracerProvider.kt @@ -10,6 +10,8 @@ import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.telemetry.context.Context import aws.smithy.kotlin.runtime.telemetry.trace.* import io.opentelemetry.api.OpenTelemetry +import io.opentelemetry.extension.kotlin.asContextElement +import kotlin.coroutines.CoroutineContext import io.opentelemetry.api.trace.Span as OtelSpan import io.opentelemetry.api.trace.SpanContext as OtelSpanContext import io.opentelemetry.api.trace.SpanKind as OtelSpanKind @@ -62,11 +64,11 @@ private class OtelSpanContextImpl(private val otelSpanContext: OtelSpanContext) internal class OtelTraceSpanImpl( private val otelSpan: OtelSpan, ) : TraceSpan { - - private val spanScope = otelSpan.makeCurrent() - override val spanContext: SpanContext get() = OtelSpanContextImpl(otelSpan.spanContext) + + override fun asContextElement(): CoroutineContext = otelSpan.asContextElement() + override fun set(key: AttributeKey, value: T) { key.otelAttrKeyOrNull(value)?.let { otelKey -> otelSpan.setAttribute(otelKey, value) @@ -90,7 +92,6 @@ internal class OtelTraceSpanImpl( override fun close() { otelSpan.end() - spanScope.close() } } From df45912a7693a4c54d787f9df7706f2ed273600a Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Fri, 7 Feb 2025 15:33:40 -0500 Subject: [PATCH 2/6] Changelog --- .changes/ec884c41-7bda-4033-a80b-5da20836a64b.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changes/ec884c41-7bda-4033-a80b-5da20836a64b.json diff --git a/.changes/ec884c41-7bda-4033-a80b-5da20836a64b.json b/.changes/ec884c41-7bda-4033-a80b-5da20836a64b.json new file mode 100644 index 0000000000..45deb43f26 --- /dev/null +++ b/.changes/ec884c41-7bda-4033-a80b-5da20836a64b.json @@ -0,0 +1,9 @@ +{ + "id": "ec884c41-7bda-4033-a80b-5da20836a64b", + "type": "bugfix", + "description": "⚠️ **IMPORTANT**: Fix OpenTelemetry span concurrency by using Span.asContextElement() instead of Span.makeCurrent()", + "issues": [ + "https://github.com/smithy-lang/smithy-kotlin/issues/1211" + ], + "requiresMinorVersionBump": true +} \ No newline at end of file From 334c543e5301e430562d698aa35f8bd2509134c5 Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Fri, 7 Feb 2025 17:36:32 -0500 Subject: [PATCH 3/6] Add KDocs and default impl --- .../aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan.kt b/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan.kt index 77488bd686..78a92e6057 100644 --- a/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan.kt +++ b/runtime/observability/telemetry-api/common/src/aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan.kt @@ -10,6 +10,7 @@ import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.collections.emptyAttributes import aws.smithy.kotlin.runtime.telemetry.context.Scope import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext /** * Represents a single operation/task within a trace. Each trace contains a root span and @@ -28,7 +29,10 @@ public interface TraceSpan : Scope { */ public val spanContext: SpanContext - public fun asContextElement(): CoroutineContext + /** + * A representation of this span as a [CoroutineContext] element + */ + public fun asContextElement(): CoroutineContext = EmptyCoroutineContext /** * Set an attribute on the span From c347e32168f2ded7e00f2e50cce787cc433297f7 Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Fri, 7 Feb 2025 17:41:27 -0500 Subject: [PATCH 4/6] apiDump --- runtime/observability/telemetry-api/api/telemetry-api.api | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/observability/telemetry-api/api/telemetry-api.api b/runtime/observability/telemetry-api/api/telemetry-api.api index b4c26f2963..cf14c4c7c0 100644 --- a/runtime/observability/telemetry-api/api/telemetry-api.api +++ b/runtime/observability/telemetry-api/api/telemetry-api.api @@ -439,6 +439,7 @@ public final class aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan$Companion } public final class aws/smithy/kotlin/runtime/telemetry/trace/TraceSpan$DefaultImpls { + public static fun asContextElement (Laws/smithy/kotlin/runtime/telemetry/trace/TraceSpan;)Lkotlin/coroutines/CoroutineContext; public static synthetic fun emitEvent$default (Laws/smithy/kotlin/runtime/telemetry/trace/TraceSpan;Ljava/lang/String;Laws/smithy/kotlin/runtime/collections/Attributes;ILjava/lang/Object;)V } From 50d77ed981edef3ab888872b457fcb7b74329135 Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Mon, 10 Feb 2025 10:52:32 -0500 Subject: [PATCH 5/6] Remove minor version bump from changelog --- .changes/ec884c41-7bda-4033-a80b-5da20836a64b.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.changes/ec884c41-7bda-4033-a80b-5da20836a64b.json b/.changes/ec884c41-7bda-4033-a80b-5da20836a64b.json index 45deb43f26..78aff6e839 100644 --- a/.changes/ec884c41-7bda-4033-a80b-5da20836a64b.json +++ b/.changes/ec884c41-7bda-4033-a80b-5da20836a64b.json @@ -1,9 +1,8 @@ { "id": "ec884c41-7bda-4033-a80b-5da20836a64b", "type": "bugfix", - "description": "⚠️ **IMPORTANT**: Fix OpenTelemetry span concurrency by using Span.asContextElement() instead of Span.makeCurrent()", + "description": "Fix OpenTelemetry span concurrency by using Span.asContextElement() instead of Span.makeCurrent()", "issues": [ "https://github.com/smithy-lang/smithy-kotlin/issues/1211" - ], - "requiresMinorVersionBump": true + ] } \ No newline at end of file From bf962141b747b80a04351da9e9924726229a2dd8 Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Mon, 10 Feb 2025 10:54:52 -0500 Subject: [PATCH 6/6] Add kat transform --- .brazil.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.brazil.json b/.brazil.json index dbb2135d1b..56055e11d1 100644 --- a/.brazil.json +++ b/.brazil.json @@ -7,6 +7,7 @@ "com.squareup.okhttp3:okhttp:5.*": "OkHttp3-5.x", "com.squareup.okio:okio-jvm:3.*": "OkioJvm-3.x", "io.opentelemetry:opentelemetry-api:1.*": "Maven-io-opentelemetry_opentelemetry-api-1.x", + "io.opentelemetry:opentelemetry-extension-kotlin:1.*": "Maven-io-opentelemetry_opentelemetry-extension-kotlin-1.x", "org.slf4j:slf4j-api:2.*": "Maven-org-slf4j_slf4j-api-2.x", "aws.sdk.kotlin.crt:aws-crt-kotlin:0.9.*": "AwsCrtKotlin-0.9.x", "aws.sdk.kotlin.crt:aws-crt-kotlin:0.8.*": "AwsCrtKotlin-0.8.x",