48 lines
1.5 KiB
Python
48 lines
1.5 KiB
Python
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
|