Lifecycle and troubleshooting
configure_observability() is explicit and process-scoped. Calling it again
with the same effective configuration is idempotent. Calling it with different
settings while the prior handle is active raises PravalConfigurationError.
Graceful shutdown
from praval.observability import force_flush, shutdown_observability
flushed = force_flush(5_000)
closed = shutdown_observability(5_000)
if not (flushed and closed):
# Report degraded telemetry without failing an already completed request.
pass
The timeout is a total bound. Praval makes a bounded attempt for each component
it owns, isolates exceptions, and returns False for timeout or failure. It
does not shut down host-owned providers. Batch workers are daemon threads and
shutdown is safe to call more than once.
Diagnosis
No spans: verify
observability.enabled, signal enablement, sampling, endpoint reachability, and that shutdown/flush occurs after work.No local traces: enable
observability.local.enabled, retain traces, and flush before querying.Collector gaps: inspect export failures, drops, queue depth, and Collector logs. Collector downtime never becomes an unbounded application wait.
Broken parentage: preserve Spore metadata through custom serialization and extract before starting consumer work.
Async cross-talk: create tasks normally; Praval uses context variables and OpenTelemetry context rather than process-global current span IDs.
Configuration error: ensure an endpoint has at least one signal, local storage has traces enabled, batch size does not exceed queue capacity, and a named header environment variable exists.
Provider ownership
With host-owned providers, construct processors, exporters, and metric readers
before passing providers to configure_observability(). The returned handle
has an empty owned_signals set and Praval will neither replace globals nor
shut those providers down. In particular, an OTLP metric reader cannot be
attached after a host-owned MeterProvider is constructed.
With Praval-owned providers, the observability extra must be installed and the enabled topology must have a usable local or OTLP destination. The handle records each owned signal. Reconfiguration with an identical effective config returns the active handle; a different config fails until shutdown resets the process lifecycle.
Failure sequence
For a missing signal, diagnose in this order:
configuration precedence and selected service resource;
signal enablement and parent-based sampling;
instrumentation active before the operation;
SDK queue depth/drops and export failure type;
endpoint/protocol/headers/TLS and Collector receiver;
Collector processor/exporter logs and backend ingestion;
bounded flush/shutdown result.
Do not repeatedly reconfigure to recover an exporter. Fix the external state or restart the owned lifecycle cleanly. A failed flush means delivery is unknown; it does not mean agent execution failed.
Async and process boundaries
Context variables follow normal asyncio task creation. If custom code clears context, starts work in a raw thread/process, or manually serializes a Spore, it owns context handoff. Extract remote context before creating consumer work, and detach it afterward so later tasks cannot inherit the wrong trace.