from __future__ import annotations import json import logging from datetime import datetime from typing import Any from app.hub import HubClient from app.naming import audit_log_path, stamp_iso, utc_now log = logging.getLogger(__name__) class AuditLogger: def __init__(self, hub: HubClient): self._hub = hub def write( self, *, agent_id: str | None, route: str, via: str | None, source: str | None, target_path: str | None, bytes_count: int, status_code: int, caller_ip: str | None = None, user_agent: str | None = None, extra: dict[str, Any] | None = None, ) -> None: now = utc_now() record: dict[str, Any] = { "ts": stamp_iso(now), "agent_id": agent_id, "route": route, "via": via, "source": source, "target_path": target_path, "bytes": bytes_count, "status_code": status_code, } if caller_ip is not None: record["caller_ip"] = caller_ip if user_agent is not None: record["user_agent"] = user_agent if extra: record.update(extra) line = json.dumps(record, separators=(",", ":"), ensure_ascii=False) path = audit_log_path(now) try: self._hub.append_jsonl_audit(path, line) except Exception: log.exception("audit append failed; record=%s", record)