Dhrumil Parikh
deploy GeminiRAG
cdc55f4
Raw
History Blame Contribute Delete
2.11 kB
"""
Structured logging and LLM call tracking.
configure_logging() — call once at startup to set up structlog with ISO timestamps
and JSON output.
get_logger() — returns a structlog BoundLogger; bind job_id / user_id as needed.
log_llm_call() — writes a UsageLog row to PostgreSQL AND emits a structlog
'llm_call' event, so every Groq/Gemini/Whisper/embed API
call is observable both in the DB and in the JSON log stream.
"""
import uuid
from datetime import datetime
from typing import Optional
import structlog
def configure_logging() -> None:
structlog.configure(
processors=[
structlog.stdlib.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.JSONRenderer(),
],
wrapper_class=structlog.BoundLogger,
context_class=dict,
logger_factory=structlog.PrintLoggerFactory(),
)
def get_logger():
return structlog.get_logger()
def log_llm_call(
*,
user_id,
job_id=None,
endpoint: str,
model: str,
prompt_tokens: int,
completion_tokens: int,
latency_ms: int,
query_text: Optional[str] = None,
llm_response_preview: Optional[str] = None,
db,
) -> None:
from app.models.db import UsageLog
log_entry = UsageLog(
user_id=user_id,
job_id=job_id,
endpoint=endpoint,
model=model,
prompt_tokens=prompt_tokens,
completion_tokens=completion_tokens,
total_tokens=prompt_tokens + completion_tokens,
latency_ms=latency_ms,
query_text=query_text,
llm_response_preview=llm_response_preview,
created_at=datetime.utcnow(),
)
db.add(log_entry)
db.commit()
get_logger().info(
"llm_call",
endpoint=endpoint,
model=model,
prompt_tokens=prompt_tokens,
completion_tokens=completion_tokens,
latency_ms=latency_ms,
job_id=str(job_id) if job_id else None,
)