Skip to content

Commit fb10a2a

Browse files
Standardized metrics export - feature flagged (#409)
1 parent fd9cb96 commit fb10a2a

37 files changed

Lines changed: 2896 additions & 1821 deletions

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
12+
- Canonical metrics mode: opt-in harmonized metric surface via `WORKER_CANONICAL_METRICS=true` -- [details](METRICS.md#detailed-technical-notes--unreleased)
13+
- `MetricsSettings` gains `clean_directory` and `clean_dead_pids` for opt-in stale `.db` file cleanup (both default to `False`)
14+
- `CONDUCTOR_MP_START_METHOD` env var to control the worker pool's multiprocessing start method
15+
16+
### Changed
17+
18+
- Legacy metrics emit unchanged by default; no env var required
19+
- `metrics_collector.py` is now a compatibility shim; `from conductor.client.telemetry.metrics_collector import MetricsCollector` continues to work

METRICS.md

Lines changed: 334 additions & 294 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ with TaskHandler(configuration=config, metrics_settings=metrics, scan_for_annota
322322
curl http://localhost:8000/metrics
323323
```
324324
325-
See [examples/metrics_example.py](examples/metrics_example.py) and [METRICS.md](METRICS.md) for details on all tracked metrics.
325+
Legacy metrics are emitted by default. Set `WORKER_CANONICAL_METRICS=true` before starting workers to use the canonical metric catalog. See [examples/metrics_example.py](examples/metrics_example.py) and [METRICS.md](METRICS.md) for the full legacy and canonical reference.
326326
327327
### Managing Workflow Executions
328328

WORKER_CONFIGURATION.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,6 @@ export conductor.worker.process_order.paused=true
330330
When a worker is paused:
331331
- It stops polling for new tasks
332332
- Already-executing tasks complete normally
333-
- The `task_paused_total` metric is incremented for each skipped poll
334333
- No code changes or process restarts required
335334

336335
**Use cases:**
@@ -346,11 +345,7 @@ unset conductor.worker.all.paused
346345
export conductor.worker.all.paused=false
347346
```
348347

349-
**Monitor paused workers** using the `task_paused_total` metric:
350-
```promql
351-
# Check how many times workers were paused
352-
task_paused_total{taskType="process_order"}
353-
```
348+
See [METRICS.md](METRICS.md) for the current Python SDK metrics catalog.
354349

355350
### Multi-Region Deployment
356351

docs/design/WORKER_DESIGN.md

Lines changed: 7 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -767,114 +767,13 @@ Workers package - all worker modules auto-discovered
767767

768768
## Metrics & Monitoring
769769

770-
The SDK provides comprehensive Prometheus metrics collection with two deployment modes:
770+
This design document describes how worker events flow through the SDK. The
771+
current user-facing metrics setup, legacy and canonical metric catalogs,
772+
`WORKER_CANONICAL_METRICS` behavior, Prometheus examples, and migration guidance
773+
are maintained in [`../../METRICS.md`](../../METRICS.md).
771774

772-
### Configuration
773-
774-
**HTTP Mode (Recommended - Metrics served from memory):**
775-
```python
776-
from conductor.client.configuration.settings.metrics_settings import MetricsSettings
777-
778-
metrics_settings = MetricsSettings(
779-
directory="/tmp/conductor-metrics", # .db files for multiprocess coordination
780-
update_interval=0.1, # Update every 100ms
781-
http_port=8000 # Expose metrics via HTTP
782-
)
783-
784-
with TaskHandler(
785-
configuration=config,
786-
metrics_settings=metrics_settings
787-
) as handler:
788-
handler.start_processes()
789-
```
790-
791-
**File Mode (Metrics written to file):**
792-
```python
793-
metrics_settings = MetricsSettings(
794-
directory="/tmp/conductor-metrics",
795-
file_name="metrics.prom",
796-
update_interval=1.0,
797-
http_port=None # No HTTP server - write to file instead
798-
)
799-
```
800-
801-
### Modes
802-
803-
| Mode | HTTP Server | File Writes | Use Case |
804-
|------|-------------|-------------|----------|
805-
| HTTP (`http_port` set) | ✅ Built-in | ❌ Disabled | Prometheus scraping, production |
806-
| File (`http_port=None`) | ❌ Disabled | ✅ Enabled | File-based monitoring, testing |
807-
808-
**HTTP Mode Benefits:**
809-
- Metrics served directly from memory (no file I/O)
810-
- Built-in HTTP server with `/metrics` and `/health` endpoints
811-
- Automatic aggregation across worker processes (no PID labels)
812-
- Ready for Prometheus scraping out-of-the-box
813-
814-
### Key Metrics
815-
816-
**Task Metrics:**
817-
- `task_poll_time_seconds{taskType,quantile}` - Poll latency (includes batch polling)
818-
- `task_execute_time_seconds{taskType,quantile}` - Actual execution time (async tasks: from submission to completion)
819-
- `task_execute_error_total{taskType,exception}` - Execution errors by type
820-
- `task_poll_total{taskType}` - Total poll count
821-
- `task_result_size_bytes{taskType,quantile}` - Task output size
822-
823-
**API Metrics:**
824-
- `http_api_client_request{method,uri,status,quantile}` - API request latency
825-
- `http_api_client_request_count{method,uri,status}` - Request count by endpoint
826-
- `http_api_client_request_sum{method,uri,status}` - Total request time
827-
828-
**Labels:**
829-
- `taskType`: Task definition name
830-
- `method`: HTTP method (GET, POST, PUT)
831-
- `uri`: API endpoint path
832-
- `status`: HTTP status code
833-
- `exception`: Exception type (for errors)
834-
- `quantile`: 0.5, 0.75, 0.9, 0.95, 0.99
835-
836-
**Important Notes:**
837-
- **No PID labels**: Metrics are automatically aggregated across processes
838-
- **Async execution time**: Includes actual execution time, not just coroutine submission time
839-
- **Multiprocess safe**: Uses SQLite .db files in `directory` for coordination
840-
841-
### Prometheus Integration
842-
843-
**Scrape Config:**
844-
```yaml
845-
scrape_configs:
846-
- job_name: 'conductor-workers'
847-
static_configs:
848-
- targets: ['localhost:8000']
849-
scrape_interval: 15s
850-
```
851-
852-
**Accessing Metrics:**
853-
```bash
854-
# Metrics endpoint
855-
curl http://localhost:8000/metrics
856-
857-
# Health check
858-
curl http://localhost:8000/health
859-
860-
# Watch specific metric
861-
watch -n 1 'curl -s http://localhost:8000/metrics | grep task_execute_time_seconds'
862-
```
863-
864-
**PromQL Examples:**
865-
```promql
866-
# Average execution time
867-
rate(task_execute_time_seconds_sum[5m]) / rate(task_execute_time_seconds_count[5m])
868-
869-
# Success rate
870-
sum(rate(task_execute_time_seconds_count{status="SUCCESS"}[5m])) / sum(rate(task_execute_time_seconds_count[5m]))
871-
872-
# p95 latency
873-
task_execute_time_seconds{quantile="0.95"}
874-
875-
# Error rate
876-
sum(rate(task_execute_error_total[5m])) by (taskType)
877-
```
775+
Keep metric names and PromQL examples out of this design document so the SDK has
776+
one source of truth for legacy and canonical metrics.
878777

879778
---
880779

@@ -1264,8 +1163,8 @@ class CostTracker(TaskRunnerEventsListener):
12641163
```python
12651164
handler = TaskHandler(
12661165
configuration=config,
1166+
metrics_settings=metrics_settings,
12671167
event_listeners=[
1268-
PrometheusMetricsCollector(),
12691168
SLAMonitor(threshold_ms=5000),
12701169
CostTracker(cost_per_second={'ml_task': 0.05}),
12711170
CustomAuditLogger()

docs/design/WORKER_SDK_IMPLEMENTATION_GUIDE.md

Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,44 +1789,15 @@ Response: void
17891789

17901790
## 15. Metrics & Monitoring
17911791

1792-
### 15.1 Required Metrics
1792+
The Python SDK's current metrics behavior is documented in
1793+
[`../../METRICS.md`](../../METRICS.md). That file is the source of truth for:
17931794

1794-
**Via Event System (Recommended):**
1795+
- Enabling metrics with `MetricsSettings`
1796+
- Selecting canonical metrics with `WORKER_CANONICAL_METRICS`
1797+
- The complete legacy and canonical Prometheus catalogs
1798+
- Migration guidance from legacy quantile gauges to canonical histograms
17951799

1796-
Implement MetricsCollector as EventListener:
1797-
1798-
```
1799-
class MetricsCollector implements TaskRunnerEventsListener {
1800-
on_poll_started(event):
1801-
increment_counter("task_poll_total", labels={taskType: event.taskType})
1802-
1803-
on_poll_completed(event):
1804-
record_histogram("task_poll_time_seconds", event.durationMs / 1000)
1805-
increment_counter("task_poll_total", labels={taskType: event.taskType})
1806-
1807-
on_task_execution_completed(event):
1808-
record_histogram("task_execute_time_seconds", event.durationMs / 1000)
1809-
record_histogram("task_result_size_bytes", event.outputSizeBytes)
1810-
1811-
on_task_execution_failure(event):
1812-
increment_counter("task_execute_error_total",
1813-
labels={taskType: event.taskType, exception: event.cause.type})
1814-
1815-
on_task_update_failure(event):
1816-
increment_counter("task_update_failed_total",
1817-
labels={taskType: event.taskType})
1818-
// CRITICAL: Alert operations team
1819-
}
1820-
```
1821-
1822-
**Metric Names (Prometheus format):**
1823-
- `task_poll_total{taskType}`
1824-
- `task_poll_time_seconds{taskType,quantile}`
1825-
- `task_execute_time_seconds{taskType,quantile}`
1826-
- `task_execute_error_total{taskType,exception}`
1827-
- `task_result_size_bytes{taskType,quantile}`
1828-
- `task_update_error_total{taskType,exception}`
1829-
- `task_update_failed_total{taskType}` ← CRITICAL metric
1800+
Do not duplicate metric names or PromQL examples in this implementation guide.
18301801

18311802
---
18321803

0 commit comments

Comments
 (0)