Initial project import
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"""Summarize privacy-safe adapter JSONL telemetry.
|
||||
|
||||
Run inside the REST container or copy /data/adapter-audit.jsonl from it.
|
||||
No BSL text, SQL payload, or credentials are expected in the source log.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from collections import Counter, defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--log", default="/data/adapter-audit.jsonl")
|
||||
parser.add_argument("--slow-ms", type=int, default=5_000)
|
||||
parser.add_argument("--limit", type=int, default=20)
|
||||
args = parser.parse_args()
|
||||
path = Path(args.log)
|
||||
rows: list[dict] = []
|
||||
malformed_rows = 0
|
||||
if not path.exists():
|
||||
print(json.dumps({"schema": "onec_adapter_audit_summary.v1", "status": "log_not_found", "log": str(path)}, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
|
||||
try:
|
||||
item = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
malformed_rows += 1
|
||||
continue
|
||||
if item.get("event") == "adapter_rpc":
|
||||
rows.append(item)
|
||||
by_base = Counter(str((row.get("request") or {}).get("base_id") or "<none>") for row in rows)
|
||||
exceptions = [row for row in rows if str(row.get("status") or "") == "exception" or row.get("error") == "request_exception"]
|
||||
rejected = [row for row in rows if str(row.get("status") or "") in {"blocked", "unsupported", "invalid_argument"}]
|
||||
slow = sorted((row for row in rows if int(row.get("duration_ms") or 0) >= args.slow_ms), key=lambda row: int(row.get("duration_ms") or 0), reverse=True)
|
||||
methods = Counter(str(row.get("method") or "<none>") for row in exceptions)
|
||||
print(json.dumps({
|
||||
"schema": "onec_adapter_audit_summary.v1",
|
||||
"events": len(rows),
|
||||
"malformed_rows": malformed_rows,
|
||||
"time_range": {"from": rows[0].get("time") if rows else None, "to": rows[-1].get("time") if rows else None},
|
||||
"bases": dict(by_base),
|
||||
"adapter_exceptions": len(exceptions),
|
||||
"expected_rejections": len(rejected),
|
||||
"exception_methods": dict(methods.most_common(args.limit)),
|
||||
"slow_threshold_ms": args.slow_ms,
|
||||
"slow": [
|
||||
{"time": row.get("time"), "base_id": (row.get("request") or {}).get("base_id"), "method": row.get("method"), "status": row.get("status"), "error": row.get("error"), "duration_ms": row.get("duration_ms"), "request_id": row.get("request_id")}
|
||||
for row in slow[:args.limit]
|
||||
],
|
||||
"recent_exceptions": [
|
||||
{"time": row.get("time"), "base_id": (row.get("request") or {}).get("base_id"), "method": row.get("method"), "error": row.get("error"), "exception_type": row.get("exception_type"), "duration_ms": row.get("duration_ms"), "request_id": row.get("request_id")}
|
||||
for row in exceptions[-args.limit:]
|
||||
],
|
||||
"findings": [
|
||||
*([{"priority": "P1", "kind": "adapter_exception", "count": len(exceptions), "next_action": "Inspect the matching REST request_id and exception_type; reproduce only on upo_test before changing code."}] if exceptions else []),
|
||||
*([{"priority": "P2", "kind": "slow_calls", "count": len(slow), "next_action": "Inspect timings_ms for the listed methods; optimise only after a repeated pattern is confirmed."}] if slow else []),
|
||||
*([{"priority": "P2", "kind": "malformed_audit_rows", "count": malformed_rows, "next_action": "Inspect log rotation and container shutdown events."}] if malformed_rows else []),
|
||||
],
|
||||
}, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user