from __future__ import annotations import json import threading from datetime import datetime, timezone from pathlib import Path from typing import Any def utc_now_iso() -> str: return datetime.now(timezone.utc).isoformat() def resolve_audit_root(root: Path, *, service: str, env_var: str | None = None) -> Path: import os configured = os.environ.get(env_var or "", "").strip() if env_var else "" if configured: return Path(configured) return root / "reports" / "observability" / service class JsonlAuditStore: def __init__(self, root: Path, *, service: str) -> None: self.root = root self.service = service self.root.mkdir(parents=True, exist_ok=True) self._lock = threading.Lock() def _path_for(self, event_type: str) -> Path: stamp = datetime.now(timezone.utc).strftime("%Y%m%d") return self.root / event_type / f"{stamp}.jsonl" def write_event(self, event_type: str, payload: dict[str, Any]) -> dict[str, Any]: record = { "event_type": event_type, "service": self.service, "logged_at": utc_now_iso(), **payload, } path = self._path_for(event_type) path.parent.mkdir(parents=True, exist_ok=True) line = json.dumps(record, ensure_ascii=False, separators=(",", ":")) + "\n" with self._lock: with path.open("a", encoding="utf-8") as handle: handle.write(line) return record