james
04/28/2022, 5:05 AMrunBlocking {
val error = IllegalAccessException("Bad things are happening!")
val message = "Isn't it a nice day"
Span.current()?.let { span ->
val attributes = Attributes.builder().apply {
message?.let { put("error_message", it) }
}
.build()
span.setStatus(StatusCode.ERROR)
span.recordException(throwable, attributes)
}
launch {
// This will cause the spans to get linked together and then sync metadata from the above.
@WithSpan
suspendFunction()
}
}
The reason i'm asking is that i'm trying to understand some strange behavior in our system where the context doesn't always propagate from a parent to child correctly.ephemient
04/28/2022, 5:24 AMOliver.O
04/28/2022, 1:34 PMContextElement
in coroutine land is the key, like so:
withContext(span.asContextElement()) {
// ...
}
A stripped-down version of code I am working on looks like this:
import io.opentelemetry.api.GlobalOpenTelemetry
import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.trace.SpanBuilder
import io.opentelemetry.api.trace.StatusCode
import io.opentelemetry.api.trace.Tracer
import io.opentelemetry.extension.kotlin.asContextElement
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.withContext
import kotlin.coroutines.coroutineContext
val tracer: Tracer = GlobalOpenTelemetry.getTracer("myPackage", "0.0.0")
/**
* Executes [block] in a tracing span with optional SpanBuilder [parameters].
*
* [parameters] example: `parameters = { setParent(parentContext); addLink(span1.spanContext) }`
*
* The span will be
* * a child of a parent context, if set via [parameters], or
* * a child of the current span (from the current coroutine context), or
* * a top-level span.
*/
suspend fun <Result> withSpan(
name: String,
parameters: (SpanBuilder.() -> Unit)? = null,
block: suspend (span: Span?) -> Result
): Result {
val span: Span = tracer.spanBuilder(name).run {
if (parameters != null)
parameters()
coroutineContext[CoroutineName]?.let {
setAttribute("coroutine.name", it.name)
}
startSpan()
}
return withContext(span.asContextElement()) {
try {
block(span)
} catch (throwable: Throwable) {
span.setStatus(StatusCode.ERROR)
span.recordException(throwable)
throw throwable
} finally {
span.end()
}
}
}
Andrew Louis
08/24/2022, 4:23 PMOliver.O
08/25/2022, 11:11 AMAndrew Louis
09/20/2022, 12:25 PMOliver.O
09/21/2022, 10:03 PM/**
* Executes [block] in a tracing span with optional SpanBuilder [parameters].
*
* [parameters] example: `parameters = { setParent(parentContext); addLink(span1.spanContext) }`
*
* The span will be
* - a child of a parent context, if set via [parameters], or
* - a child of the current span (from the current coroutine context), or
* - a top-level span.
*
* Guidelines:
* - [Trace Semantic Conventions](<https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/>)
* - [Attribute Naming](<https://opentelemetry.io/docs/reference/specification/common/attribute-naming/>)
*/
suspend fun <Result> withSpan(
name: String,
parameters: (SpanBuilder.() -> Unit)? = null,
exceptionIsError: (Throwable) -> Boolean = { it !is CancellationException },
block: suspend (span: Span?) -> Result
): Result {
val span: Span = tracer.spanBuilder(name).run {
if (parameters != null) {
parameters()
}
coroutineContext[CoroutineName]?.let {
setAttribute("coroutine.name", it.name)
}
startSpan()
}
return withContext(span.asContextElement()) {
try {
block(span).also {
span.setStatus(StatusCode.OK)
}
} catch (throwable: Throwable) {
if (exceptionIsError(throwable)) {
span.setStatus(StatusCode.ERROR)
span.recordException(throwable)
} else {
span.addEvent(
"Completed with exception",
attributes(
"exception.type" to throwable.javaClass.name,
"exception.message" to (throwable.message ?: "(none)")
)
)
span.setStatus(StatusCode.OK)
}
throw throwable
} finally {
span.end()
}
}
}
The compiler plugin exists to generate names for the "feature flags on steroids" stuff and is not required for the above.Andrew Louis
09/22/2022, 2:40 AMStatusCode.OK
yourself?
I saw a caveat against that behaviour - and so I generally avoidAndrew Louis
09/22/2022, 2:40 AMGenerally, Instrumentation Libraries SHOULD NOT set the status code to, unless explicitly configured to do so. Instrumentation Libraries SHOULD leave the status code asOk
unless there is an error, as described above.Unset
Oliver.O
09/22/2022, 8:49 AMUnset
does not make much sense to me. So in this case the above code is that of an 'application developer', I guess:
Application developers and Operators may set the status code to.Ok
When span status is set toit SHOULD be considered final and any further attempts to change it SHOULD be ignored.Ok
Oliver.O
09/22/2022, 8:51 AM