OpenTelemetry¶
asynk records traces and metrics with OpenTelemetry and needs nothing beyond installing the API:
What turns on, and when¶
The extra installs opentelemetry-api only. When an App is built, it checks
whether that package is importable; if it is, telemetry is on for that app.
There is no asynk setting for it. Without the package the app uses a
NullTelemetry that does nothing, and import asynk
never imports OpenTelemetry.
The API alone records nothing: spans and metrics go to whatever SDK your process
configures (and to the no-op default if it configures none). With no SDK, no
trace context is written to messages, and the only cost is a few no-op calls.
To record anything, set up the OpenTelemetry SDK and an exporter in your
process, for example in a before_startup hook, the way you would for any
other library.
To turn it off, set the standard OTEL_SDK_DISABLED=true before the app is
built. asynk has no variable of its own.
Telemetry fails open: an error in any of it is logged once, telemetry is switched off for the rest of the process, and publishing and tasks carry on.
Traces¶
- Publishing (
delay,App.publish) writes the active span's W3Ctraceparent(andtracestate, when there is one) into the envelope metadata through the global propagator. Metadata keys you set yourself win. There is no producer span. - Running a task happens under a
CONSUMERspan namedprocess <task name>whose parent is the context found in the metadata. The span hasmessaging.system.name(asynk),messaging.operation.nameandmessaging.operation.type(process),messaging.message.id(the envelope id),asynk.task.name,asynk.task.attemptandasynk.task.status(the finalTaskStatusvalue). - A task that ends
failed,retryingorretries_exceededgets the exception recorded on its span, statusERRORanderror.type. Askippedorunknown_taskoutcome is not an error. - The span is the outermost layer of the middleware
chain whatever you register, so your own layers run inside it and see it as
the current span. An exception raised by one of your layers, or a
cancellation, ends the span with
ERROR. - A retry is published again with the original metadata, so every attempt continues the first trace.
- Tasks published by the scheduler have no active span, so their worker spans start new traces.
Metrics¶
The instrumentation scope is asynk, versioned with asynk.__version__.
| Metric | Type | Unit | Meaning |
|---|---|---|---|
messaging.process.duration |
histogram | s |
Time to run a message through the chain, with asynk.task.name, asynk.task.status and error.type on errors |
messaging.client.sent.messages |
counter | {message} |
Messages published by App.publish; retries are not counted |
messaging.client.consumed.messages |
counter | {message} |
Messages a worker took for processing |
asynk.worker.active_tasks |
up-down counter | {task} |
Deliveries being handled, to compare with concurrency |
asynk.task.retries |
counter | {retry} |
Retries a worker published |
Not provided¶
Queue depth (there is no transport statistics API), producer spans, log correlation, broker-level spans from the transports, and a queue-time histogram.
Semantic conventions¶
The names follow the messaging span and messaging metric conventions, which are still in development, so attribute and metric names may change in OpenTelemetry and in asynk with them. Choices that go beyond them:
- asynk has no queue or topic name in its core, so the task name stands in
where the convention puts the destination: in the span name
(
process <task name>) and inasynk.task.name. The messaging destination attributes are not set. error.typeis the exception's fully qualified class name (just the class name for built-ins).messaging.client.consumed.messagesis counted when processing starts, as asynk has no separate receive step.