Source code for qfa.utils
"""Utility functions for the feedback analysis backend."""
import logging
import time
from collections.abc import Iterator
from contextlib import contextmanager
from dataclasses import dataclass
from qfa.settings import LogSettings
[docs]
@dataclass
class Stopwatch:
"""Holds the measured wall-clock duration of a :func:`timed` block."""
elapsed_seconds: float = 0.0
[docs]
@contextmanager
def timed() -> Iterator[Stopwatch]:
"""Measure the wall-clock duration of a block, without logging anything.
Yields a :class:`Stopwatch` whose ``elapsed_seconds`` is populated when the
block exits — including when it raises, so a phase that fails still reports
how long it ran. Measurement is deliberately decoupled from logging: the
caller owns the log message, so it controls the level and guarantees only
content-free values (counts, latencies) reach the log, per the hard
prohibitions in ``docs/operations/observability.md``.
Uses :func:`time.perf_counter` (monotonic) so it is unaffected by wall-clock
adjustments such as NTP steps.
Examples
--------
Time a phase, then log a content-free summary yourself::
with timed() as sw:
vectors = embedder.embed(texts)
logger.info("embedded %d record(s) in %.2fs", len(texts), sw.elapsed_seconds)
"""
stopwatch = Stopwatch()
start = time.perf_counter()
try:
yield stopwatch
finally:
stopwatch.elapsed_seconds = time.perf_counter() - start
[docs]
def setup_logging(log_settings: LogSettings | None = None) -> None:
"""Set up the logging system.
Parameters
----------
log_settings : LogSettings | None
Optional logging configuration. When ``None`` a default
``LogSettings`` instance is created.
"""
log_config = log_settings or LogSettings()
# force=True: qfa.main calls configure_azure_monitor() before this runs
# whenever APPLICATIONINSIGHTS_CONNECTION_STRING is set (every deployed
# environment). That call attaches its own handler to the root logger,
# which makes a plain basicConfig() a silent no-op — every log call in the
# app would still "succeed" but never reach stdout/stderr, only the
# separately-configured OTel export.
logging.basicConfig(
level=log_config.loglevel_3rdparty, force=True, **log_config.basicConfig
)
our_loglevel = log_config.loglevel
for package in log_config.our_packages:
logging.getLogger(package).setLevel(our_loglevel)