44package software.aws.toolkits.jetbrains.services.telemetry.otel
55
66import com.intellij.platform.diagnostic.telemetry.helpers.use as ijUse
7+ import com.intellij.platform.diagnostic.telemetry.helpers.useWithScope as ijUseWithScope
78import io.opentelemetry.api.common.AttributeKey
89import io.opentelemetry.api.common.Attributes
910import io.opentelemetry.api.trace.Span
@@ -13,16 +14,21 @@ import io.opentelemetry.api.trace.SpanKind
1314import io.opentelemetry.context.Context
1415import io.opentelemetry.context.ContextKey
1516import io.opentelemetry.context.Scope
17+ import kotlinx.coroutines.CoroutineScope
1618import software.amazon.awssdk.services.toolkittelemetry.model.AWSProduct
19+ import software.aws.toolkits.core.utils.getLogger
20+ import software.aws.toolkits.core.utils.warn
1721import software.aws.toolkits.jetbrains.services.telemetry.PluginResolver
1822import java.time.Instant
1923import java.util.concurrent.TimeUnit
24+ import kotlin.coroutines.CoroutineContext
25+ import kotlin.coroutines.EmptyCoroutineContext
2026
2127val AWS_PRODUCT_CONTEXT_KEY = ContextKey .named<AWSProduct >(" pluginDescriptor" )
22- private val PLUGIN_ATTRIBUTE_KEY = AttributeKey .stringKey(" plugin" )
28+ internal val PLUGIN_ATTRIBUTE_KEY = AttributeKey .stringKey(" plugin" )
2329
2430class DefaultSpanBuilder (delegate : SpanBuilder ) : AbstractSpanBuilder<DefaultSpanBuilder, AbstractBaseSpan>(delegate) {
25- override fun doStartSpan () = BaseSpan (parent!! , delegate.startSpan())
31+ override fun doStartSpan () = BaseSpan (parent, delegate.startSpan())
2632}
2733
2834abstract class AbstractSpanBuilder <Builder : AbstractSpanBuilder <Builder , Span >, Span : AbstractBaseSpan >(protected val delegate : SpanBuilder ) : SpanBuilder {
@@ -36,6 +42,19 @@ abstract class AbstractSpanBuilder<Builder : AbstractSpanBuilder<Builder, Span>,
3642 operation(span as Span )
3743 }
3844
45+ /* *
46+ * Same as [com.intellij.platform.diagnostic.telemetry.helpers.useWithScope] except downcasts to specific subclass of [BaseSpan]
47+ *
48+ * @inheritdoc
49+ */
50+ suspend inline fun <T > useWithScope (
51+ context : CoroutineContext = EmptyCoroutineContext ,
52+ crossinline operation : suspend CoroutineScope .(Span ) -> T
53+ ): T =
54+ ijUseWithScope(context) { span ->
55+ operation(span as Span )
56+ }
57+
3958 protected var parent: Context ? = null
4059 override fun setParent (context : Context ): Builder {
4160 parent = context
@@ -123,25 +142,31 @@ abstract class AbstractSpanBuilder<Builder : AbstractSpanBuilder<Builder, Span>,
123142 if (contextValue == null ) {
124143 // FIX_WHEN_MIN_IS_243: Kotlin compiler can't figure out difference between class/type parameter until 2.x
125144 val s = io.opentelemetry.api.trace.Span .fromContextOrNull(parent)
126- if (s is AbstractBaseSpan ) {
127- setParent( s.context.with (io.opentelemetry.api.trace.Span .fromContext(parent) ))
145+ parent = if (s is AbstractBaseSpan && s.context != null ) {
146+ s.context.with (io.opentelemetry.api.trace.Span .fromContext(parent))
128147 } else {
129- setParent( parent.with (AWS_PRODUCT_CONTEXT_KEY , resolvePluginName() ))
148+ parent.with (AWS_PRODUCT_CONTEXT_KEY , resolvePluginName())
130149 }
150+ setParent(parent)
131151 }
152+ requireNotNull(parent)
132153
133- setAttribute(
134- PLUGIN_ATTRIBUTE_KEY ,
135- (parent.get(AWS_PRODUCT_CONTEXT_KEY ) ? : resolvePluginName()).name
136- )
154+ parent.get(AWS_PRODUCT_CONTEXT_KEY )?.toString()?.let {
155+ setAttribute(PLUGIN_ATTRIBUTE_KEY , it)
156+ } ? : run {
157+ LOG .warn { " Reached setAttribute with null AWS_PRODUCT_CONTEXT_KEY, but should not be possible" }
158+ }
137159
138160 return doStartSpan()
139161 }
140162
141- private fun resolvePluginName () = PluginResolver .Companion .fromStackTrace(Thread .currentThread().stackTrace).product
163+ private companion object {
164+ val LOG = getLogger<AbstractSpanBuilder <* , * >>()
165+ fun resolvePluginName () = PluginResolver .Companion .fromStackTrace(Thread .currentThread().stackTrace).product
166+ }
142167}
143168
144- abstract class AbstractBaseSpan (internal val context : Context , private val delegate : Span ) : Span by delegate {
169+ abstract class AbstractBaseSpan (internal val context : Context ? , private val delegate : Span ) : Span by delegate {
145170 /* *
146171 * Same as [com.intellij.platform.diagnostic.telemetry.helpers.use] except downcasts to specific subclass of [BaseSpan]
147172 *
@@ -155,12 +180,12 @@ abstract class AbstractBaseSpan(internal val context: Context, private val deleg
155180 fun metadata (key : String , value : String ) = setAttribute(key, value)
156181
157182 override fun makeCurrent (): Scope =
158- context.with (this ).makeCurrent()
183+ context? .with (this )?.makeCurrent() ? : super .makeCurrent()
159184}
160185
161186/* *
162187 * Placeholder; will be generated
163188 */
164- class BaseSpan (context : Context , delegate : Span ) : AbstractBaseSpan(context, delegate) {
189+ class BaseSpan (context : Context ? , delegate : Span ) : AbstractBaseSpan(context, delegate) {
165190 fun reason (reason : String ) = metadata(" reason" , reason)
166191}
0 commit comments