contextquery-api / app /services /observability.py
RamshaAnwar's picture
feat(obs): add /metrics endpoint and structured ai_call logging (#17)
0358051 unverified
Raw History Blame Contribute Delete
1.48 kB
import time
import json
import logging
from uuid import UUID
from typing import Callable, Any
from app.services.groq_service import AiUnavailable
logger = logging.getLogger("uvicorn.error")
def log_ai_call(operation: str, user_id: UUID | str, hub_id: UUID | str | None, fn: Callable, *args, **kwargs) -> Any:
"""Wraps an AI call, logs the duration/tokens/status, and re-raises exceptions."""
start = time.monotonic()
status = "success"
input_tokens = 0
output_tokens = 0
try:
result = fn(*args, **kwargs)
# If the wrapped function returns (data, in_tok, out_tok), unpack it
if isinstance(result, tuple) and len(result) == 3 and isinstance(result[1], int) and isinstance(result[2], int):
data, input_tokens, output_tokens = result
return data
return result
except Exception as e:
status = getattr(e, "kind", "error")
raise e
finally:
duration_ms = int((time.monotonic() - start) * 1000)
log_data = {
"event": "ai_call",
"operation": operation,
"user_id": str(user_id),
"hub_id": str(hub_id) if hub_id else None,
"duration_ms": duration_ms,
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"status": status,
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
}
logger.info(json.dumps(log_data))