stemtrace 0.1.1__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {stemtrace-0.1.1 → stemtrace-0.2.0}/.bumpversion.toml +1 -1
- {stemtrace-0.1.1 → stemtrace-0.2.0}/.github/workflows/ci.yml +1 -1
- {stemtrace-0.1.1 → stemtrace-0.2.0}/.github/workflows/e2e.yml +1 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/CHANGELOG.md +10 -3
- {stemtrace-0.1.1 → stemtrace-0.2.0}/CONTRIBUTING.md +3 -3
- {stemtrace-0.1.1 → stemtrace-0.2.0}/Makefile +2 -2
- {stemtrace-0.1.1 → stemtrace-0.2.0}/PKG-INFO +92 -62
- {stemtrace-0.1.1 → stemtrace-0.2.0}/README.md +91 -58
- stemtrace-0.2.0/docs/screenshots/task_details.png +0 -0
- stemtrace-0.2.0/docs/screenshots/unregistered.png +0 -0
- stemtrace-0.2.0/docs/screenshots/workflow.png +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/examples/celery_app.py +7 -1
- {stemtrace-0.1.1 → stemtrace-0.2.0}/examples/fastapi_integration.py +18 -18
- {stemtrace-0.1.1 → stemtrace-0.2.0}/examples/with_auth.py +10 -11
- stemtrace-0.2.0/progress.md +5 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/pyproject.toml +1 -6
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/__init__.py +77 -62
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/core/events.py +40 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/core/graph.py +71 -2
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/core/ports.py +3 -3
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/library/scrubbing.py +1 -1
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/library/signals.py +144 -4
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/library/transports/memory.py +6 -4
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/library/transports/redis.py +51 -7
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/__main__.py +5 -5
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/api/__init__.py +6 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/api/routes.py +201 -7
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/api/schemas.py +43 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/consumer.py +77 -5
- stemtrace-0.2.0/src/stemtrace/server/fastapi/extension.py +170 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/fastapi/router.py +12 -3
- stemtrace-0.2.0/src/stemtrace/server/store.py +514 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/package-lock.json +83 -23
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/package.json +4 -4
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/api/client.ts +51 -6
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/api/queries.ts +17 -3
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/hooks/WebSocketContext.tsx +18 -4
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/main.tsx +5 -1
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/routes/__root.tsx +6 -0
- stemtrace-0.2.0/src/stemtrace/server/ui/frontend/src/routes/registry.tsx +411 -0
- stemtrace-0.2.0/src/stemtrace/server/ui/frontend/src/routes/workers.tsx +172 -0
- stemtrace-0.2.0/src/stemtrace/server/ui/frontend/src/vite-env.d.ts +10 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/tests/fixtures/mock-api.ts +69 -1
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/tests/fixtures/mock-data.ts +67 -0
- stemtrace-0.2.0/src/stemtrace/server/ui/frontend/tests/workers.spec.ts +64 -0
- stemtrace-0.2.0/src/stemtrace/server/ui/static.py +122 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/websocket.py +4 -1
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/e2e/conftest.py +1 -1
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/e2e/tasks.py +1 -1
- stemtrace-0.2.0/tests/e2e/test_workers_e2e.py +117 -0
- stemtrace-0.2.0/tests/integration/test_embedded_static.py +464 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/integration/test_fastapi_integration.py +228 -0
- stemtrace-0.2.0/tests/integration/test_static_ui.py +400 -0
- stemtrace-0.2.0/tests/integration/test_workers_refresh.py +83 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_bootsteps.py +47 -0
- stemtrace-0.2.0/tests/unit/test_consumer.py +436 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_events.py +108 -1
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_examples.py +21 -16
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_graph.py +232 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_init.py +46 -33
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_routes.py +174 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_scrubbing.py +36 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_signals.py +396 -1
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_store.py +721 -7
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_transports.py +113 -1
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_websocket.py +24 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/uv.lock +2 -17
- stemtrace-0.1.1/src/stemtrace/server/fastapi/extension.py +0 -121
- stemtrace-0.1.1/src/stemtrace/server/store.py +0 -260
- stemtrace-0.1.1/src/stemtrace/server/ui/frontend/src/routes/registry.tsx +0 -211
- stemtrace-0.1.1/src/stemtrace/server/ui/static.py +0 -56
- stemtrace-0.1.1/tests/integration/test_static_ui.py +0 -114
- stemtrace-0.1.1/tests/unit/test_consumer.py +0 -167
- {stemtrace-0.1.1 → stemtrace-0.2.0}/.dockerignore +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/.github/dependabot.yml +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/.github/workflows/release.yml +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/.gitignore +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/.pre-commit-config.yaml +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/.python-version +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/Dockerfile +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/Dockerfile.e2e +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/LICENSE +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/build_ui.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/docker-compose.e2e.yml +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/docker-compose.yml +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/core/__init__.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/core/exceptions.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/library/__init__.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/library/bootsteps.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/library/config.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/library/transports/__init__.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/py.typed +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/__init__.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/api/websocket.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/fastapi/__init__.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/fastapi/auth.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/__init__.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/biome.json +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/index.html +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/playwright.config.ts +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/postcss.config.js +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/components/DateRangePicker.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/components/Filters.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/components/TaskGraph.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/components/TaskList.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/components/TaskStateBadge.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/components/TaskTimeline.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/components/TimelineBar.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/components/Tooltip.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/hooks/useInfiniteScroll.ts +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/index.css +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/routes/graph.$rootId.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/routes/graphs.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/routes/index.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/routes/tasks.$taskId.tsx +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/src/utils/format.ts +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/tests/graphs.spec.ts +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/tests/registry.spec.ts +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/tests/task-detail.spec.ts +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/tests/tasks.spec.ts +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/tsconfig.json +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/tsconfig.tsbuildinfo +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/src/stemtrace/server/ui/frontend/vite.config.ts +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/__init__.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/conftest.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/e2e/__init__.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/e2e/test_api_e2e.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/integration/__init__.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/integration/test_redis_transport.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/__init__.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_auth.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_config.py +0 -0
- {stemtrace-0.1.1 → stemtrace-0.2.0}/tests/unit/test_exceptions.py +0 -0
|
@@ -76,7 +76,7 @@ jobs:
|
|
|
76
76
|
run: uv sync --all-extras
|
|
77
77
|
|
|
78
78
|
- name: Run tests
|
|
79
|
-
run: uv run pytest --cov=stemtrace --cov-branch --cov-report=xml --cov-fail-under=
|
|
79
|
+
run: uv run pytest --cov=stemtrace --cov-branch --cov-report=xml --cov-fail-under=95
|
|
80
80
|
|
|
81
81
|
- name: Upload coverage
|
|
82
82
|
uses: codecov/codecov-action@v5
|
|
@@ -7,7 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
## [0.
|
|
10
|
+
## [0.2.0] - 2026-01-03
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Workers API endpoints: `GET /api/workers` and `GET /api/workers/{hostname}`
|
|
14
|
+
- Worker lifecycle tracking via `worker_ready` / `worker_shutdown` events to maintain a live worker registry
|
|
15
|
+
- Workers UI page for inspecting online/offline workers and their registered tasks
|
|
16
|
+
- Task Registry now shows which workers registered each task (`registered_by`)
|
|
11
17
|
|
|
12
18
|
## [0.1.1] - 2025-12-31
|
|
13
19
|
|
|
@@ -28,7 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
28
34
|
- **Core domain models**: `TaskEvent`, `TaskState`, `TaskNode`, `TaskGraph`
|
|
29
35
|
- **Protocol definitions**: `EventTransport`, `TaskRepository`, `AsyncEventConsumer`
|
|
30
36
|
- **Event transports**: Redis Streams (`RedisTransport`), in-memory (`MemoryTransport`)
|
|
31
|
-
- **Celery signal integration**: Automatic event capture via `stemtrace.
|
|
37
|
+
- **Celery signal integration**: Automatic event capture via `stemtrace.init_worker(app)`
|
|
32
38
|
- **Server components**:
|
|
33
39
|
- `GraphStore` — Thread-safe in-memory graph storage with LRU eviction
|
|
34
40
|
- `EventConsumer` / `AsyncEventConsumer` — Background event processing
|
|
@@ -44,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
44
50
|
- **E2E test suite**: Docker API tests + Playwright browser tests
|
|
45
51
|
- **Comprehensive test suite**: 350+ Python tests, 90%+ coverage
|
|
46
52
|
|
|
47
|
-
[unreleased]: https://github.com/iansokolskyi/stemtrace/compare/v0.
|
|
53
|
+
[unreleased]: https://github.com/iansokolskyi/stemtrace/compare/v0.2.0...HEAD
|
|
54
|
+
[0.2.0]: https://github.com/iansokolskyi/stemtrace/compare/v0.1.1...v0.2.0
|
|
48
55
|
[0.1.1]: https://github.com/iansokolskyi/stemtrace/compare/v0.1.0...v0.1.1
|
|
49
56
|
[0.1.0]: https://github.com/iansokolskyi/stemtrace/releases/tag/v0.1.0
|
|
@@ -19,7 +19,7 @@ git clone https://github.com/iansokolskyi/stemtrace.git
|
|
|
19
19
|
cd stemtrace
|
|
20
20
|
|
|
21
21
|
# Install dependencies (creates venv automatically)
|
|
22
|
-
uv sync --
|
|
22
|
+
uv sync --extra dev
|
|
23
23
|
|
|
24
24
|
# Install pre-commit hooks
|
|
25
25
|
uv run pre-commit install
|
|
@@ -79,7 +79,7 @@ uv run pytest
|
|
|
79
79
|
uv run pytest --cov=stemtrace --cov-report=term-missing
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
**Coverage requirement:**
|
|
82
|
+
**Coverage requirement:** 95% minimum
|
|
83
83
|
|
|
84
84
|
### Updating Dependencies
|
|
85
85
|
|
|
@@ -132,7 +132,7 @@ uv lock --upgrade
|
|
|
132
132
|
### Code Quality
|
|
133
133
|
|
|
134
134
|
- Google-style docstrings on all public APIs
|
|
135
|
-
-
|
|
135
|
+
- Pydantic models: immutable with `model_config = ConfigDict(frozen=True)`, mutable with `validate_assignment=True`
|
|
136
136
|
- Fire-and-forget pattern for publishers (never block)
|
|
137
137
|
- Fakes over mocks in tests
|
|
138
138
|
|
|
@@ -36,7 +36,7 @@ format:
|
|
|
36
36
|
uv run ruff check --fix src/ tests/
|
|
37
37
|
|
|
38
38
|
test:
|
|
39
|
-
uv run pytest --cov=stemtrace --cov-report=term-missing --cov-fail-under=
|
|
39
|
+
uv run pytest --cov=stemtrace --cov-report=term-missing --cov-fail-under=95
|
|
40
40
|
|
|
41
41
|
# Run tests without coverage (faster iteration)
|
|
42
42
|
test-fast:
|
|
@@ -170,7 +170,7 @@ e2e-mock:
|
|
|
170
170
|
e2e-up:
|
|
171
171
|
docker compose -f docker-compose.e2e.yml up -d --wait
|
|
172
172
|
@echo "Waiting for services..."
|
|
173
|
-
@timeout 60 bash -c 'until curl -sf http://localhost:8000/stemtrace/api/health -o /dev/null; do sleep 2; done' ||
|
|
173
|
+
@timeout 60 bash -c 'until curl -sf http://localhost:8000/stemtrace/api/health -o /dev/null; do sleep 2; done' || (echo "❌ Service failed to become healthy"; exit 1)
|
|
174
174
|
@echo "✅ E2E environment ready at http://localhost:8000"
|
|
175
175
|
|
|
176
176
|
# Stop E2E test environment
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stemtrace
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A lightweight Celery task flow visualizer
|
|
5
5
|
Project-URL: Homepage, https://github.com/iansokolskyi/stemtrace
|
|
6
6
|
Project-URL: Documentation, https://github.com/iansokolskyi/stemtrace#readme
|
|
@@ -41,7 +41,6 @@ Requires-Dist: build>=1.0.0; extra == 'dev'
|
|
|
41
41
|
Requires-Dist: bump-my-version>=0.28.0; extra == 'dev'
|
|
42
42
|
Requires-Dist: hatchling>=1.20.0; extra == 'dev'
|
|
43
43
|
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
44
|
-
Requires-Dist: pika>=1.3.0; extra == 'dev'
|
|
45
44
|
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
|
|
46
45
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
47
46
|
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
@@ -50,15 +49,13 @@ Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
|
50
49
|
Requires-Dist: twine>=5.0.0; extra == 'dev'
|
|
51
50
|
Requires-Dist: types-redis>=4.0.0; extra == 'dev'
|
|
52
51
|
Requires-Dist: websockets>=12.0; extra == 'dev'
|
|
53
|
-
Provides-Extra: rabbitmq
|
|
54
|
-
Requires-Dist: pika>=1.3.0; extra == 'rabbitmq'
|
|
55
52
|
Description-Content-Type: text/markdown
|
|
56
53
|
|
|
57
54
|
# stemtrace 🌿
|
|
58
55
|
|
|
59
56
|
**Zero-infrastructure Celery task flow visualizer**
|
|
60
57
|
|
|
61
|
-
[](https://pypi.org/project/stemtrace/)
|
|
62
59
|
[](https://pypi.org/project/stemtrace/)
|
|
63
60
|
[](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml)
|
|
64
61
|
[](https://codecov.io/gh/iansokolskyi/stemtrace)
|
|
@@ -72,7 +69,9 @@ Description-Content-Type: text/markdown
|
|
|
72
69
|
|
|
73
70
|
Ever stared at a failed Celery task wondering "what called this?" or "why did it retry 5 times?"
|
|
74
71
|
|
|
75
|
-
Stemtrace captures your task executions as a graph — visualize parent→child flows, see retry chains, track groups and chords, all without adding any new infrastructure.
|
|
72
|
+
Stemtrace captures your task executions as a graph — visualize parent→child flows, see retry chains, track groups and chords, all without adding any new infrastructure.
|
|
73
|
+
|
|
74
|
+
**Today stemtrace supports Redis** for event transport (via Redis Streams). **RabbitMQ support is planned.**
|
|
76
75
|
|
|
77
76
|
## ✨ Features
|
|
78
77
|
|
|
@@ -86,20 +85,58 @@ Stemtrace captures your task executions as a graph — visualize parent→child
|
|
|
86
85
|
- **Groups & Chords** — Automatic visualization of `group()` and `chord()` patterns
|
|
87
86
|
- **Parent-Child Tracking** — See which task spawned which
|
|
88
87
|
|
|
88
|
+
**Worker Monitoring & Registry**
|
|
89
|
+
- **Workers page** — See which workers are online/offline and what tasks they have registered
|
|
90
|
+
- **Registry status badges** — Quickly spot tasks that are active, never run, or not registered by any current worker
|
|
91
|
+
|
|
89
92
|
**Production Ready**
|
|
90
93
|
- **Zero Infrastructure** — Uses your existing Redis broker, no database needed
|
|
91
94
|
- **Sensitive Data Scrubbing** — Passwords and API keys filtered automatically
|
|
92
95
|
- **Read-Only** — Safe for production; never modifies your task queue
|
|
93
96
|
- **FastAPI Integration** — Mount into your existing app with one line
|
|
94
97
|
|
|
98
|
+
## 🔎 What you’ll see in the dashboard
|
|
99
|
+
|
|
100
|
+
### Task details (timing, inputs/outputs, errors)
|
|
101
|
+
|
|
102
|
+
- **What you’ll see**: Per-task execution timing (including how long it spent in each state), parameters (args/kwargs), return value, and the full event history.
|
|
103
|
+
- **Why it helps**: Quickly answer “what happened?” for a single task: slow queueing vs slow execution, which retry succeeded, and (on failures) the exception + traceback for debugging.
|
|
104
|
+
|
|
105
|
+
<p align="center">
|
|
106
|
+
<img src="docs/screenshots/task_details.png" width="900" alt="Task detail view showing parameters, result, and timeline" />
|
|
107
|
+
</p>
|
|
108
|
+
|
|
109
|
+
### Flow graphs (chains, groups, chords)
|
|
110
|
+
|
|
111
|
+
- **What you’ll see**: An interactive DAG of your workflow with parent→child edges, plus clear GROUP/CHORD containers for Celery canvas patterns.
|
|
112
|
+
- **Why it helps**: Understand fan-out/fan-in at a glance (especially chords), spot which branch failed, and debug “why didn’t my callback run?” without grepping logs.
|
|
113
|
+
|
|
114
|
+
<p align="center">
|
|
115
|
+
<img src="docs/screenshots/workflow.png" width="900" alt="Chord visualization in the workflow graph" />
|
|
116
|
+
</p>
|
|
117
|
+
|
|
118
|
+
### Task registry (registration status + warnings)
|
|
119
|
+
|
|
120
|
+
- **What you’ll see**: A registry of tasks with status badges like **Active**, **Never Run**, and **Not Registered** plus “registered by …” worker info.
|
|
121
|
+
- **Why it helps**: Catch misconfigurations where tasks get stuck in **PENDING** because no current worker has the task registered (common in multi-repo or deploy drift scenarios).
|
|
122
|
+
|
|
123
|
+
<p align="center">
|
|
124
|
+
<img src="docs/screenshots/unregistered.png" width="900" alt="Task registry showing not-registered warning and status badges" />
|
|
125
|
+
</p>
|
|
126
|
+
|
|
95
127
|
## 🚀 Quick Start
|
|
96
128
|
|
|
97
129
|
### 1. Install
|
|
98
130
|
|
|
99
131
|
```bash
|
|
132
|
+
# Using pip
|
|
100
133
|
pip install stemtrace
|
|
134
|
+
|
|
135
|
+
# Using uv
|
|
136
|
+
uv add stemtrace
|
|
101
137
|
```
|
|
102
138
|
|
|
139
|
+
|
|
103
140
|
### 2. Instrument your Celery app
|
|
104
141
|
|
|
105
142
|
```python
|
|
@@ -108,8 +145,10 @@ import stemtrace
|
|
|
108
145
|
|
|
109
146
|
app = Celery("myapp", broker="redis://localhost:6379/0")
|
|
110
147
|
|
|
111
|
-
# One line to enable
|
|
112
|
-
|
|
148
|
+
# One line to enable event capture.
|
|
149
|
+
# Tip: put this in the module where you define your Celery app so it's imported by
|
|
150
|
+
# both Celery workers and any code that calls app.send_task()/delay().
|
|
151
|
+
stemtrace.init_worker(app)
|
|
113
152
|
```
|
|
114
153
|
|
|
115
154
|
### 3. View the dashboard
|
|
@@ -126,11 +165,11 @@ Open [http://localhost:8000](http://localhost:8000).
|
|
|
126
165
|
|
|
127
166
|
```python
|
|
128
167
|
from fastapi import FastAPI
|
|
129
|
-
|
|
168
|
+
import stemtrace
|
|
130
169
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
170
|
+
app = FastAPI(lifespan=my_lifespan) # Your existing app
|
|
171
|
+
|
|
172
|
+
stemtrace.init_app(app, broker_url="redis://localhost:6379/0")
|
|
134
173
|
```
|
|
135
174
|
|
|
136
175
|
Access at `/stemtrace/` in your existing app — no new services to deploy.
|
|
@@ -187,7 +226,7 @@ stemtrace is designed as two decoupled components:
|
|
|
187
226
|
```python
|
|
188
227
|
import stemtrace
|
|
189
228
|
|
|
190
|
-
stemtrace.
|
|
229
|
+
stemtrace.init_worker(
|
|
191
230
|
app,
|
|
192
231
|
# Optional: override broker URL (defaults to Celery's broker_url)
|
|
193
232
|
transport_url="redis://localhost:6379/0",
|
|
@@ -204,9 +243,6 @@ stemtrace.init(
|
|
|
204
243
|
safe_keys=frozenset({"public_key"}), # Never scrub these keys
|
|
205
244
|
)
|
|
206
245
|
|
|
207
|
-
# Configuration also available via StemtraceConfig model:
|
|
208
|
-
# - max_data_size: Maximum serialized data size (default: 10KB)
|
|
209
|
-
|
|
210
246
|
# Introspection (after init)
|
|
211
247
|
stemtrace.is_initialized() # -> True
|
|
212
248
|
stemtrace.get_config() # -> StemtraceConfig
|
|
@@ -225,7 +261,7 @@ Scrubbed values appear as `[Filtered]` in the UI.
|
|
|
225
261
|
|
|
226
262
|
### Canvas Graph Visualization
|
|
227
263
|
|
|
228
|
-
stemtrace automatically detects and visualizes Celery canvas constructs:
|
|
264
|
+
`stemtrace` automatically detects and visualizes Celery canvas constructs:
|
|
229
265
|
|
|
230
266
|
```text
|
|
231
267
|
# Parent-spawned group: GROUP is child of parent
|
|
@@ -261,9 +297,7 @@ batch_processor
|
|
|
261
297
|
|
|
262
298
|
| Variable | Description | Default |
|
|
263
299
|
|----------|-------------|---------|
|
|
264
|
-
| `STEMTRACE_BROKER_URL` | Broker
|
|
265
|
-
| `STEMTRACE_TTL` | Event TTL in seconds | `86400` |
|
|
266
|
-
| `STEMTRACE_PREFIX` | Key/queue prefix | `stemtrace` |
|
|
300
|
+
| `STEMTRACE_BROKER_URL` | Broker URL for `stemtrace server` / `stemtrace consume` and `stemtrace.init_app()` | `redis://localhost:6379/0` |
|
|
267
301
|
|
|
268
302
|
### Supported Brokers
|
|
269
303
|
|
|
@@ -271,7 +305,6 @@ batch_processor
|
|
|
271
305
|
|--------|------------|--------|
|
|
272
306
|
| Redis | `redis://`, `rediss://` | ✅ Supported |
|
|
273
307
|
| RabbitMQ | `amqp://`, `amqps://` | 🚧 Planned |
|
|
274
|
-
| Amazon SQS | `sqs://` | 🚧 Planned |
|
|
275
308
|
|
|
276
309
|
## 🐳 Docker
|
|
277
310
|
|
|
@@ -295,12 +328,12 @@ services:
|
|
|
295
328
|
|
|
296
329
|
## 🖥️ Deployment Options
|
|
297
330
|
|
|
298
|
-
stemtrace offers two deployment modes depending on your needs:
|
|
331
|
+
`stemtrace` offers two deployment modes depending on your needs:
|
|
299
332
|
|
|
300
333
|
| Mode | Best For | Command |
|
|
301
334
|
|------|----------|---------|
|
|
302
335
|
| **Standalone Server** | Dedicated monitoring, simple setup | `stemtrace server` |
|
|
303
|
-
| **FastAPI Embedded** | Single-app deployment, existing FastAPI apps | `
|
|
336
|
+
| **FastAPI Embedded** | Single-app deployment, existing FastAPI apps | `stemtrace.init_app(...)` |
|
|
304
337
|
|
|
305
338
|
### Option 1: Standalone Server (Recommended)
|
|
306
339
|
|
|
@@ -326,66 +359,63 @@ stemtrace server \
|
|
|
326
359
|
|
|
327
360
|
#### High-Scale Production Setup
|
|
328
361
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
```bash
|
|
332
|
-
# Terminal 1: Run consumer (processes events)
|
|
333
|
-
stemtrace consume
|
|
334
|
-
|
|
335
|
-
# Terminal 2: Run API server (separate process, shares state via broker)
|
|
336
|
-
stemtrace server
|
|
337
|
-
```
|
|
362
|
+
Note: `stemtrace server` includes an embedded consumer today (single-process). A multi-process deployment mode is planned.
|
|
338
363
|
### Option 2: FastAPI Embedded
|
|
339
364
|
|
|
340
365
|
Mount stemtrace directly into your existing FastAPI application:
|
|
341
366
|
|
|
342
367
|
```python
|
|
343
368
|
from fastapi import FastAPI
|
|
344
|
-
|
|
369
|
+
import stemtrace
|
|
370
|
+
|
|
371
|
+
app = FastAPI(lifespan=my_lifespan) # Your existing app with lifespan
|
|
372
|
+
|
|
373
|
+
stemtrace.init_app(app, broker_url="redis://localhost:6379/0") # Wraps lifespan, adds /stemtrace routes
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
That's it. `init_app()` automatically:
|
|
377
|
+
- Wraps your existing lifespan (Sentry, DB connections, etc. keep working)
|
|
378
|
+
- Mounts the dashboard at `/stemtrace/`
|
|
379
|
+
- Starts the event consumer
|
|
380
|
+
|
|
381
|
+
#### Configuration Options
|
|
345
382
|
|
|
346
|
-
|
|
383
|
+
```python
|
|
384
|
+
import stemtrace
|
|
385
|
+
|
|
386
|
+
# Returns the underlying StemtraceExtension if you need it (optional).
|
|
387
|
+
extension = stemtrace.init_app(
|
|
388
|
+
app,
|
|
347
389
|
broker_url="redis://localhost:6379/0",
|
|
348
|
-
#
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
390
|
+
prefix="/stemtrace", # Mount path AND event stream prefix (normalized)
|
|
391
|
+
ttl=86400, # Event TTL in seconds
|
|
392
|
+
max_nodes=10000, # Max nodes in memory
|
|
393
|
+
embedded_consumer=True, # Run consumer in FastAPI process
|
|
394
|
+
serve_ui=True, # Serve React dashboard
|
|
395
|
+
auth_dependency=None, # Optional auth (see below)
|
|
354
396
|
)
|
|
355
|
-
app = FastAPI(lifespan=flow.lifespan)
|
|
356
|
-
app.include_router(flow.router, prefix="/stemtrace")
|
|
357
397
|
```
|
|
358
398
|
|
|
359
|
-
Access the dashboard at `/stemtrace/` within your app.
|
|
360
|
-
|
|
361
399
|
#### With Custom Authentication
|
|
362
400
|
|
|
363
|
-
Use your existing auth middleware:
|
|
364
|
-
|
|
365
401
|
```python
|
|
366
402
|
from fastapi import Depends
|
|
367
|
-
|
|
403
|
+
import stemtrace
|
|
368
404
|
from your_app.auth import require_admin
|
|
369
405
|
|
|
370
|
-
|
|
371
|
-
broker_url="redis://localhost:6379/0",
|
|
372
|
-
auth_dependency=Depends(require_admin),
|
|
373
|
-
)
|
|
374
|
-
app = FastAPI(lifespan=flow.lifespan)
|
|
375
|
-
app.include_router(flow.router, prefix="/stemtrace")
|
|
406
|
+
stemtrace.init_app(app, broker_url="redis://localhost:6379/0", auth_dependency=Depends(require_admin))
|
|
376
407
|
```
|
|
377
408
|
|
|
378
409
|
Or use built-in auth helpers:
|
|
379
410
|
|
|
380
411
|
```python
|
|
381
|
-
|
|
412
|
+
import stemtrace
|
|
382
413
|
|
|
383
|
-
|
|
414
|
+
stemtrace.init_app(
|
|
415
|
+
app,
|
|
384
416
|
broker_url="redis://localhost:6379/0",
|
|
385
|
-
auth_dependency=require_basic_auth("admin", "secret"),
|
|
417
|
+
auth_dependency=stemtrace.require_basic_auth("admin", "secret"),
|
|
386
418
|
)
|
|
387
|
-
app = FastAPI(lifespan=flow.lifespan)
|
|
388
|
-
app.include_router(flow.router, prefix="/stemtrace")
|
|
389
419
|
```
|
|
390
420
|
|
|
391
421
|
#### Embedded Consumer Modes
|
|
@@ -393,7 +423,7 @@ app.include_router(flow.router, prefix="/stemtrace")
|
|
|
393
423
|
| Mode | Use Case | Setup |
|
|
394
424
|
|------|----------|-------|
|
|
395
425
|
| Embedded | Development, simple apps | Default — consumer runs in FastAPI process |
|
|
396
|
-
| External | Production, high scale |
|
|
426
|
+
| External | Production, high scale | Planned |
|
|
397
427
|
|
|
398
428
|
## 🗺️ Roadmap
|
|
399
429
|
|
|
@@ -406,12 +436,12 @@ app.include_router(flow.router, prefix="/stemtrace")
|
|
|
406
436
|
- ✅ **Sensitive data scrubbing** — Passwords and API keys filtered automatically
|
|
407
437
|
- ✅ **Real-time updates** — WebSocket-powered live dashboard
|
|
408
438
|
- ✅ **FastAPI integration** — Mount into your existing app
|
|
409
|
-
- ✅ **
|
|
439
|
+
- ✅ **Workers page** — Monitor online/offline workers and their registered tasks
|
|
440
|
+
- ✅ **Task registry** — Browse discovered + registered tasks with clear status badges
|
|
410
441
|
|
|
411
442
|
### Coming Soon
|
|
412
443
|
|
|
413
444
|
- 🔜 **RabbitMQ support** — Use your existing RabbitMQ broker
|
|
414
|
-
- 🔜 **Worker monitoring** — See which worker processed each task
|
|
415
445
|
- 🔜 **Anomaly detection** — Spot stuck, orphaned, or failed tasks
|
|
416
446
|
- 🔜 **Dashboard with stats** — Success rates, durations, failure trends
|
|
417
447
|
- 🔜 **OpenTelemetry export** — Send traces to Jaeger, Tempo, Datadog
|
|
@@ -427,7 +457,7 @@ See our [Contributing Guide](CONTRIBUTING.md) to get started.
|
|
|
427
457
|
```bash
|
|
428
458
|
git clone https://github.com/iansokolskyi/stemtrace.git
|
|
429
459
|
cd stemtrace
|
|
430
|
-
uv sync --
|
|
460
|
+
uv sync --extra dev # Install dependencies
|
|
431
461
|
make check # Run tests
|
|
432
462
|
```
|
|
433
463
|
|