refactor: add rule engine, risk service, quote state machine, and replay tests
This commit is contained in:
89
utils/observability.py
Normal file
89
utils/observability.py
Normal file
@@ -0,0 +1,89 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
|
||||
def build_trace_id(*parts: str) -> str:
|
||||
raw = "|".join(str(p or "") for p in parts)
|
||||
digest = hashlib.md5(raw.encode("utf-8")).hexdigest()
|
||||
return digest[:16]
|
||||
|
||||
|
||||
def emit_activity(
|
||||
logger: logging.Logger,
|
||||
*,
|
||||
event: str,
|
||||
trace_id: str = "",
|
||||
customer_id: str = "",
|
||||
result: str = "ok",
|
||||
latency_ms: Optional[int] = None,
|
||||
**fields: Any,
|
||||
) -> None:
|
||||
payload: Dict[str, Any] = {
|
||||
"trace_id": trace_id or "-",
|
||||
"customer_id": customer_id or "-",
|
||||
"event": event,
|
||||
"result": result,
|
||||
}
|
||||
if latency_ms is not None:
|
||||
payload["latency_ms"] = int(max(0, latency_ms))
|
||||
for k, v in (fields or {}).items():
|
||||
if isinstance(v, str):
|
||||
payload[k] = v[:400]
|
||||
else:
|
||||
payload[k] = v
|
||||
try:
|
||||
logger.info(f"[ACTIVITY] {json.dumps(payload, ensure_ascii=False)}")
|
||||
except Exception:
|
||||
logger.info(f"[ACTIVITY] {payload}")
|
||||
|
||||
|
||||
class ActivityTimer:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
logger: logging.Logger,
|
||||
event: str,
|
||||
trace_id: str = "",
|
||||
customer_id: str = "",
|
||||
**fields: Any,
|
||||
):
|
||||
self.logger = logger
|
||||
self.event = event
|
||||
self.trace_id = trace_id
|
||||
self.customer_id = customer_id
|
||||
self.fields = fields
|
||||
self.start = time.monotonic()
|
||||
|
||||
def ok(self, **fields: Any) -> None:
|
||||
elapsed_ms = int((time.monotonic() - self.start) * 1000)
|
||||
merged = dict(self.fields)
|
||||
merged.update(fields)
|
||||
emit_activity(
|
||||
self.logger,
|
||||
event=self.event,
|
||||
trace_id=self.trace_id,
|
||||
customer_id=self.customer_id,
|
||||
result="ok",
|
||||
latency_ms=elapsed_ms,
|
||||
**merged,
|
||||
)
|
||||
|
||||
def fail(self, error: str, **fields: Any) -> None:
|
||||
elapsed_ms = int((time.monotonic() - self.start) * 1000)
|
||||
merged = dict(self.fields)
|
||||
merged.update(fields)
|
||||
emit_activity(
|
||||
self.logger,
|
||||
event=self.event,
|
||||
trace_id=self.trace_id,
|
||||
customer_id=self.customer_id,
|
||||
result="error",
|
||||
latency_ms=elapsed_ms,
|
||||
error=error,
|
||||
**merged,
|
||||
)
|
||||
Reference in New Issue
Block a user