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:

  1. configuration precedence and selected service resource;

  2. signal enablement and parent-based sampling;

  3. instrumentation active before the operation;

  4. SDK queue depth/drops and export failure type;

  5. endpoint/protocol/headers/TLS and Collector receiver;

  6. Collector processor/exporter logs and backend ingestion;

  7. 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.