Observability
Every queue event the framework emits targets nest_rs::queue. One
target, every worker-side log filterable by RUST_LOG=nest_rs::queue=debug,
one span wrapping each job so its events share the same fields. The
service-level logs your #[process] method emits keep their own target
(e.g. features::audio) — same convention as the rest of the framework.
Targets and levels
Section titled “Targets and levels”| Target | What | Level |
|---|---|---|
nest_rs::queue | Boot registration, per-job span + events, skipped/unversioned warnings. | debug on job start, info on success, warn on failure. |
nest_rs::transport | Boot: attached module-contributed transport transport=QueueWorker. | info. |
features::<feature> | Your service-level logs inside the #[process] method. | Your call. |
Hot paths respect RUST_LOG=info: job started sits at debug, so a
worker at info logs one job ok line per successful ack and a
job failed line per failed attempt — nothing per-poll noise.
The per-job span
Section titled “The per-job span”The worker opens one span per job attempt, target nest_rs::queue,
name process job. The handler runs inside it, so any tracing event
your service emits — and the framework’s own start/end events — are
tagged with the span’s fields:
| Field | Notes |
|---|---|
queue | The Redis queue name. |
processor | The processor host name, e.g. AudioProcessor::transcode. |
job_id | The apalis TaskId for this job. |
attempt | The apalis attempt counter (1 on the first try, higher on retries). |
attempt distinguishes retries of the same job_id. The OTLP appender
from nest-rs-opentelemetry exports these as span attributes.
What a job’s life looks like
Section titled “What a job’s life looks like”On success the worker emits job started at debug and job ok at
info (carrying elapsed_ms, the wall time of the handler call):
DEBUG process job{queue=audio processor=AudioProcessor::transcode job_id=01HFEX… attempt=1}: nest_rs::queue: job started INFO process job{queue=audio processor=AudioProcessor::transcode job_id=01HFEX… attempt=1}: features::audio: transcoded file=track-1717405126521.mp3 INFO process job{queue=audio processor=AudioProcessor::transcode job_id=01HFEX… attempt=1}: nest_rs::queue: job ok elapsed_ms=342A failed attempt emits job failed at warn, carrying elapsed_ms
and the error:
DEBUG process job{queue=audio processor=AudioProcessor::transcode job_id=01HFFA… attempt=1}: nest_rs::queue: job started WARN process job{queue=audio processor=AudioProcessor::transcode job_id=01HFFA… attempt=1}: nest_rs::queue: job failed elapsed_ms=12 error=connection refusedThe attempt field on the span is how you correlate a retry: apalis
re-runs the same job_id with attempt=2, opening a fresh process job span. There is no separate terminal line — the retry budget lives
in the apalis RetryLayer; see Retries and
failure for the policy behind attempt.
What boot looks like
Section titled “What boot looks like”Each active processor logs one attached line (from the transport) and
one registered queue processor line carrying its concurrency and
retries budget. A #[process] method that compiled in but whose
provider isn’t reachable from this app’s module tree is skipped with a
warn:
INFO nest_rs::transport: attached module-contributed transport transport=QueueWorker INFO nest_rs::queue: registered queue processor processor=AudioProcessor::transcode queue=audio concurrency=5 retries=3 WARN nest_rs::queue: skipped #[process] method: provider unreachable from app's module tree processor=ReportJobs::weekly queue=reportsThe skip line is warn — a linked-but-unimported handler is usually a
wiring mistake worth surfacing, and it confirms a shared features
crate isn’t accidentally activating handlers this binary doesn’t want.
See Wiring the worker for the module-gating rule.
Filtering recipes
Section titled “Filtering recipes”RUST_LOG=info,nest_rs::queue=debug nestrs run dev workerinfo everywhere, debug on the queue path: adds the job started
line per poll on top of the job ok / job failed outcomes.
RUST_LOG=nest_rs::queue=warn nestrs run dev workerStrictly failures, skips, and unversioned warnings — useful for tailing a healthy worker where the only thing you care about is “did anything go wrong.”
Going further
Section titled “Going further”- OpenTelemetry — installing the OTLP appender so these spans and structured logs reach a backend.
- OpenTelemetry / traces — how
nest_rs::queuespans appear in a trace UI, attribute conventions. - OpenTelemetry / logs — structured-log export, severity mapping.
- Retries and failure — the policy
behind the
attemptfield and thefailedoutcome.
Built by YV17labs