study-buddy / app /observability /local_store.py
GitHub Actions
deploy d092bea3608b7a29952f16357fda39b7a29e399b
2e818da
Raw
History Blame Contribute Delete
3.12 kB
"""Compact JSONL storage for completed operation observations.
Per the signal-responsibility table, the local observation exists to "Feed
Control Room and preserve compact experiment evidence: latest stage durations,
counts, statuses, and trace ID". This is deliberately the *smallest* useful
persistence: one JSON line per completed operation, append-only, no index, no
rotation policy (a later Control Room task owns summaries/aggregation).
The rows are already-sanitized ``OperationObservation`` values; this module
does not re-sanitize -- redaction happens once, in the operation boundary,
before an observation is handed here.
"""
from __future__ import annotations
import json
import threading
from pathlib import Path
from typing import Any
from app.observability.contracts import OperationObservation
_FILENAME = "observations.jsonl"
class LocalObservationStore:
"""Append-only JSONL sink for :class:`OperationObservation` rows.
Construction ensures the target directory exists. ``record`` may raise
(disk full, permissions) -- the operation boundary is responsible for
suppressing and counting such failures so a telemetry-write problem never
fails a product request.
"""
def __init__(self, root: Path, filename: str = _FILENAME) -> None:
self._root = Path(root)
self._root.mkdir(parents=True, exist_ok=True)
self._path = self._root / filename
self._lock = threading.Lock()
@property
def path(self) -> Path:
return self._path
def record(self, observation: OperationObservation) -> None:
"""Append one observation as a single JSON line."""
line = json.dumps(observation.model_dump(mode="json"), ensure_ascii=False)
with self._lock:
with self._path.open("a", encoding="utf-8") as handle:
handle.write(line + "\n")
def latest(self) -> dict[str, Any]:
"""Return the most recently recorded row as a dict.
Raises ``ValueError`` if the store file exists but has no recorded
rows yet (an empty or all-blank-lines file). Note this does not
guard against a missing file at all -- opening a store whose file has
never been created raises the underlying ``FileNotFoundError`` from
``Path.open`` instead, since construction only ensures the *directory*
exists, not the file itself.
"""
last: str | None = None
with self._path.open("r", encoding="utf-8") as handle:
for line in handle:
if line.strip():
last = line
if last is None:
raise ValueError("LocalObservationStore is empty")
return json.loads(last)
def all(self) -> list[dict[str, Any]]:
"""Return every recorded row (small-scale evidence reads only)."""
if not self._path.exists():
return []
rows: list[dict[str, Any]] = []
with self._path.open("r", encoding="utf-8") as handle:
for line in handle:
if line.strip():
rows.append(json.loads(line))
return rows