Lightweight state breadcrumb tracking with context propagation
Project description
statecrumbs
Lightweight runtime state breadcrumbs for crash debugging, observability, and post‑mortem analysis. Capture small, structured “crumbs” of state as your code executes - thread‑safe, context‑aware, and bounded in memory.
Features
- Bounded ring buffer (no log spam) via
max_crumbs - Zero‑overhead crash hook that never blocks the real exception
- Context propagation with scoped values (
request_id,pipeline_id, etc.) - Thread‑safe recording
- Structured output as JSON or readable text
- Crash-time snapshot: dump recent state automatically on unhandled exceptions
- Works alongside the standard logging module (no handler/level setup required)
- Tiny API and zero config by default; add where it matters
- ISO‑8601 timestamps and optional thread names
How this differs from logging
- Logging emits an unbounded text stream; statecrumbs keeps a small, in‑memory, structured buffer.
- Logging focuses on levels/formatters/handlers; statecrumbs focuses on concise event breadcrumbs.
- Logging requires configuration to persist/rotate; statecrumbs is optimized for post‑mortem snapshots.
- Scoped context is automatic via a context manager; no adapters or extra plumbing.
- Designed to be complementary: keep your logs, add statecrumbs for crisp crash/debug context.
Installation
From PyPI:
pip install statecrumbs
Quick start
from statecrumbs import enable, crumb
# Install crash hook and set a small buffer
enable(on_crash="stderr", max_crumbs=5)
crumb("start", user="alice")
crumb("before_error", x=10)
1 / 0 # On crash, recent crumbs are dumped to stderr as JSON
Common use cases
- Debugging flaky tests and CI failures
- Post‑mortem triage of production crashes
- Tracing requests across service boundaries
- Understanding data pipeline stages and failures
- Capturing lightweight state without committing to full logging
Example crash output (shape):
[
{
"event": "start",
"ts": "2025-12-30T10:23:45.123456+00:00",
"thread": "MainThread"
},
{
"event": "before_error",
"ts": "2025-12-30T10:23:45.234567+00:00",
"thread": "MainThread",
"x": "10"
}
]
Core API
-
enable(**kwargs): Configure and install the crash hook.- Supported config keys (defaults shown):
max_crumbs: int = 50on_crash: str = "stderr"(one of"stderr" | "file" | "none" | callable)output_file: str = "statecrumbs.json"(used whenon_crash == "file")include_timestamp: bool = True(adds ISO‑8601tsfield)include_thread: bool = True(addsthreadfield)
- Supported config keys (defaults shown):
-
crumb(event: str, **data): Record a breadcrumb.- Adds fields:
event: required stringts: ISO‑8601 UTC timestamp (e.g.2025-12-30T10:23:45.123456+00:00) if enabledthread: current thread name if enabledcontext: dict of scoped values if any (see Context below)- any
**datayou pass, stored viarepr(value)
- Never raises; no‑op for empty/invalid events.
- Adds fields:
-
snapshot(): Return a copy of the current crumb buffer (oldest to newest). -
clear(): Clear the buffer. -
dump(): Print current crumbs as pretty JSON to stdout.
Crash behavior
When the crash hook is installed (via enable()), an unhandled exception will trigger a dump of snapshot() according to on_crash:
"stderr": write JSON to stderr (default)"file": write tooutput_file"none": do nothingcallable: a callable receiving thecrumbslist
The hook is defensive and won’t block or replace the real exception.
Context
Use the context manager to add scoped metadata automatically to all crumbs recorded within the scope:
from statecrumbs import crumb, snapshot, clear
from statecrumbs.context import context
clear()
with context(request_id="req-999", user="bob"):
crumb("request_start")
with context(step="db_query"):
crumb("query_started")
crumb("query_finished")
crumb("request_end")
for c in snapshot():
print(c)
Each crumb within the context(...) scope includes a context field containing the merged key/values active at capture time.
Available context helpers:
context(**kwargs): context manager that merges keys for the duration of the block.get_context(): returns the current context dict (for advanced use).
Formatting and export
formatter.format_json(crumbs) -> str: pretty JSON string.formatter.format_text(crumbs) -> str: simple text format.export.export_json(path): write the currentsnapshot()to a file as pretty JSON.
Thread‑safety and buffer semantics
- All crumb operations are protected by a lock; concurrent
crumb()calls are safe. - The buffer is a ring with capacity
max_crumbs. When full, oldest crumbs are dropped.
Examples in repo
examples/basic_crumb.py: minimal breadcrumbs + crash behavior.examples/data_ingest.py: staged pipeline with success/failure paths, context scoping, and JSON export.examples/web_request.py: request‑scoped context and nested steps.examples/basic_context.py: simple request‑scoped context.examples/nested_context.py: nested contexts across multiple steps.examples/prod_scale_pipeline.py: large batch pipeline with many crumbs and buffer tuning.
Run an example:
python examples/basic_crumb.py
Testing
The project includes a small test suite:
pip install -e .[test] # or just ensure pytest is available
pytest
Data model
A typical crumb looks like:
{
"event": "transform_completed",
"ts": "2025-12-30T10:23:45.987654+00:00",
"thread": "MainThread",
"context": {"pipeline_id": "ingest-42", "stage": "transform"},
"records": "4"
}
Notes:
tsis ISO‑8601 UTC with timezone offset.- Arbitrary fields come from
**dataand are stored usingrepr(value)to avoid serialization errors.
FAQ
- Why both context and ad‑hoc fields?
- Use
context(...)for durable, cross‑cutting metadata (request IDs, stages). Use**datafor event‑specific details.
- Use
- What happens if values aren’t JSON‑serializable?
- Values from
**dataare stored viarepr(value). Ifreprfails, the value falls back to"<unserializable>".
- Values from
- Can I disable timestamps or thread names?
- Yes, set
include_timestamp=Falseorinclude_thread=Falseinenable(...).
- Yes, set
License
MIT © Hemapriya N
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file statecrumbs-0.1.1.tar.gz.
File metadata
- Download URL: statecrumbs-0.1.1.tar.gz
- Upload date:
- Size: 7.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
53af07af64e08f350e46ee3396b5b943239496274aea7b4fd0d9224095cadb23
|
|
| MD5 |
9d67844b947fb422d12757678297d007
|
|
| BLAKE2b-256 |
b2f658dd47aab9724d0f8fbb8c492b8e9fd9b23b1b35dae00bb1082a87a46aae
|
File details
Details for the file statecrumbs-0.1.1-py3-none-any.whl.
File metadata
- Download URL: statecrumbs-0.1.1-py3-none-any.whl
- Upload date:
- Size: 7.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
114f35fddc983fd997ce83395b94064b81fc17544d0d58cae38be4e431c2f74b
|
|
| MD5 |
f68aa7e9ef0d4c748a50166d1f603ecc
|
|
| BLAKE2b-256 |
5070eae93db408c686f412042213d68ed063bcb94264f125a24fe2f97b5281a2
|