Worker threads
Python threads don’t share the main thread’s excepthook — an uncaught exception in athreading.Thread normally prints to stderr (if you’re lucky) and vanishes. slomo hooks thread exceptions and unraisable errors, so worker crashes become first-class incidents:
slomo issues with its traceback and captured frame locals — same as a main-thread crash. See examples/background_worker.py for a complete runnable demo of worker-thread crashes you’d otherwise never see.
Events from all threads flow into the same session timeline, ordered by timestamp.
asyncio
Auto-tracing and@track both handle coroutines, and span context propagates across await — so concurrent tasks produce correctly-nested traces rather than an interleaved soup:
slomo session inspect renders each fetch as a child span of pipeline, with per-call durations, even though they ran concurrently. Async generators, retries, and exception paths are all recorded; examples/async_pipeline.py exercises all of them.
Exceptions inside tasks are recorded when they escape your coroutine — including tasks whose results are never awaited (those surface as unraisable errors).
Multiple processes
The rule: one session per process, one writer per session directory. There is no cross-process locking, because there is no shared mutable state.- Each process that calls
enable()(or inherits an enabled recorder throughfork) records its own session. - Forked children automatically start a fresh session labeled with
forked_from: <parent session id>in their metadata. - Gunicorn/uvicorn workers,
multiprocessingpools, and cron subprocesses all just work — runslomo sessionsand you’ll see one session per worker.
affected_sessions: 5, and slomo issue sessions SM-… lists them.
Shutdown and flushing
The writer thread batches events every 0.5 s, and slomo finalizes the session at interpreter exit. Two cases deserve an explicit call:flush()— call beforeos._exit(),exec*, or any exit path that skips normal interpreter teardown.disable()— call when a long-lived process wants to close its session early (e.g. at the end of a batch job inside a persistent worker).
Retention is per-store, not per-process:
retention_max_sessions (default 200) bounds the total number of stored sessions, so a busy multi-worker app naturally prunes its history. See slomo prune.