Skip to content

Commit 6208345

Browse files
committed
feat(28-02): emit structured telemetry from WorkerBoundary background task failures
- Call emit_failure_telemetry("worker_task_failed", run_id=run_id, error=str(exc)) inside WorkerBoundary._run's existing except block, alongside the existing self._status[run_id] = f"error:{exc}" assignment, so background workflow failures are visible to log-based monitoring instead of only an in-memory status dict polled via get_status. - Existing status-string contract (get_status/is_done semantics) is unchanged; only the additional telemetry call was added. - Extend tests/test_worker_boundary.py's error-path test to capture stderr and assert the emitted worker_task_failed telemetry line is parseable JSON containing run_id and error.
1 parent bfbec1b commit 6208345

2 files changed

Lines changed: 15 additions & 3 deletions

File tree

maf_starter/worker_boundary.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from enum import Enum
55
from typing import Any, Awaitable, Callable
66

7+
from maf_starter.telemetry import emit_failure_telemetry
8+
79

810
class WorkerProfile(str, Enum):
911
LOCAL = "local"
@@ -58,6 +60,7 @@ async def _run(self, run_id: str, workflow: Callable[[], Awaitable[Any]]) -> Non
5860
self._status[run_id] = "done"
5961
except Exception as exc: # noqa: BLE001
6062
self._status[run_id] = f"error:{exc}"
63+
emit_failure_telemetry("worker_task_failed", run_id=run_id, error=str(exc))
6164

6265
def get_status(self, run_id: str) -> _RunStatus | None:
6366
"""Return the current status string for *run_id*, or None if unknown."""

tests/test_worker_boundary.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from __future__ import annotations
22

33
import asyncio
4+
import contextlib
5+
import io
6+
import json
47
import unittest
58

69
from maf_starter.worker_boundary import WorkerBoundary, WorkerProfile
@@ -71,19 +74,25 @@ async def controlled_workflow():
7174

7275
async def test_status_records_error_on_exception(self) -> None:
7376
boundary = WorkerBoundary()
77+
stderr = io.StringIO()
7478

7579
async def failing_workflow():
7680
raise ValueError("something went wrong")
7781

78-
boundary.submit_async("run-3", failing_workflow)
79-
# Yield so the task can run and fail
80-
await asyncio.sleep(0)
82+
with contextlib.redirect_stderr(stderr):
83+
boundary.submit_async("run-3", failing_workflow)
84+
# Yield so the task can run and fail
85+
await asyncio.sleep(0)
8186

8287
status = boundary.get_status("run-3")
8388
assert status is not None
8489
self.assertTrue(status.startswith("error:"), f"Expected error: prefix, got: {status!r}")
8590
self.assertIn("something went wrong", status)
8691
self.assertTrue(boundary.is_done("run-3"))
92+
payload = json.loads(stderr.getvalue().strip())
93+
self.assertEqual(payload["event"], "worker_task_failed")
94+
self.assertEqual(payload["run_id"], "run-3")
95+
self.assertEqual(payload["error"], "something went wrong")
8796

8897
def test_get_status_returns_none_for_unknown_run(self) -> None:
8998
boundary = WorkerBoundary()

0 commit comments

Comments
 (0)