face-intel / utils /logging.py
Marwan
Restructure + add reverse face search (PimEyes-style)
f5eeb1c
Raw
History Blame Contribute Delete
3.8 kB
"""
Structured logging with execution context.
Every execution (a job, a provider invocation, a retry attempt) is
given an `ExecutionContext` that binds the following fields into every
log line produced inside that context:
execution_id — UUID for the top-level job
provider_id — name of the provider being invoked (or "-")
retry_count — how many retries have occurred for this attempt
status — "started" | "success" | "failed" | "retried"
Implementation: loguru's `contextualize()` binds dict keys into the
`extra` dict of every record created within the context. We layer a
small wrapper on top so callers don't have to remember the keys.
"""
from __future__ import annotations
import sys
import uuid
from contextlib import contextmanager
from contextvars import ContextVar
from typing import Any, Iterator, Optional
from loguru import logger
from config.settings import Settings
# Context vars for in-process propagation (e.g. into async tasks)
_execution_id: ContextVar[str] = ContextVar("execution_id", default="-")
_provider_id: ContextVar[str] = ContextVar("provider_id", default="-")
_retry_count: ContextVar[int] = ContextVar("retry_count", default=0)
_status: ContextVar[str] = ContextVar("status", default="started")
def setup_logging(settings: Settings) -> None:
"""Configure loguru sinks. Call once at app startup."""
logger.remove()
if settings.log_json:
fmt = (
'{{"timestamp":"{time:YYYY-MM-DDTHH:mm:ss.SSSZ}",'
'"level":"{level}",'
'"execution_id":"{extra[execution_id]}",'
'"provider_id":"{extra[provider_id]}",'
'"retry_count":{extra[retry_count]},'
'"status":"{extra[status]}",'
'"message":"{message}"}}'
)
else:
fmt = (
"{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | "
"eid={extra[execution_id]} | pid={extra[provider_id]} | "
"retry={extra[retry_count]} | status={extra[status]} | "
"{message}"
)
logger.configure(
extra={
"execution_id": "-",
"provider_id": "-",
"retry_count": 0,
"status": "started",
}
)
logger.add(sys.stderr, format=fmt, level=settings.log_level, backtrace=True, diagnose=False)
def new_execution_id() -> str:
"""Generate a fresh execution id."""
return uuid.uuid4().hex[:12]
@contextmanager
def execution_context(
execution_id: Optional[str] = None,
provider_id: str = "-",
retry_count: int = 0,
status: str = "started",
) -> Iterator[dict]:
"""Bind execution context to every log line produced inside the block.
Usage:
with execution_context(provider_id="haar") as ctx:
ctx["status"] = "running"
logger.info("detecting faces")
ctx["status"] = "success"
"""
eid = execution_id or new_execution_id()
ctx = {
"execution_id": eid,
"provider_id": provider_id,
"retry_count": retry_count,
"status": status,
}
eid_token = _execution_id.set(eid)
pid_token = _provider_id.set(provider_id)
rc_token = _retry_count.set(retry_count)
st_token = _status.set(status)
with logger.contextualize(**ctx):
try:
yield ctx
finally:
_execution_id.reset(eid_token)
_provider_id.reset(pid_token)
_retry_count.reset(rc_token)
_status.reset(st_token)
def current_execution_id() -> str:
return _execution_id.get()
def current_provider_id() -> str:
return _provider_id.get()
__all__ = [
"setup_logging",
"new_execution_id",
"execution_context",
"current_execution_id",
"current_provider_id",
"logger",
]