Observability¶
The client exposes two hook families. Log hooks (log_hooks) receive
structured per-provider lifecycle events — start, retry, success,
error — the same events the CLI persists to the provider_logs table (see
Database Design). Metrics hooks
(metrics_hooks) receive a typed MetricEvent for every request attempt,
retry, HTTP cache lookup, and quota consumption.
Neither needs an extra dependency, and a hook that raises is logged and never breaks the forecast.
Both event types are frozen, keyword-only dataclasses. Their timestamp is
always an offset-aware UTC datetime: a naive value is assumed to be UTC, and
an aware value in another zone is converted, preserving the instant.
Metrics hooks¶
Register any callable that accepts a MetricEvent:
from omni_weather_forecast_apis import MetricEvent, MetricKind, create_omni_weather
def record(event: MetricEvent) -> None:
if event.kind is MetricKind.REQUEST_END:
print(event.provider, event.latency_ms, event.error_code)
async with await create_omni_weather(config, metrics_hooks=[record]) as client:
...
MetricKind has seven members:
| Kind | Emitted when |
|---|---|
request_start |
A provider fetch attempt begins |
request_end |
A provider fetch attempt finishes, successfully or not |
retry_scheduled |
A transient failure is about to be retried after backoff |
cache_hit |
The shared HTTP cache served a response |
cache_miss |
The shared HTTP cache had nothing usable |
quota_consumed |
A request was charged against a provider's daily quota |
quota_exhausted |
A fetch was refused because the daily quota was spent |
MetricEvent carries kind, provider, timestamp, attempt,
latency_ms, error_code, http_status, url, and an extra mapping.
Cache events set provider to None and carry the request url instead —
the HTTP cache lives at the shared transport, below per-provider attribution.
For a per-call rollup without a hook, response.summary.retries reports how
many retries a single forecast() needed.
OpenTelemetry¶
A prebuilt bridge ships as the otel extra:
pip install "omni-weather-forecast-apis[otel]"
from omni_weather_forecast_apis.otel import create_otel_metrics_hook
async with await create_omni_weather(
config,
metrics_hooks=[create_otel_metrics_hook()],
) as client:
...
The bridge records counters for requests, retries, cache outcomes, and quota
consumption, plus an omni_weather.request.duration_ms histogram. It uses the
globally configured OpenTelemetry meter provider, so configure your exporter
as usual before creating the client.
Debug logging¶
The CLI's --debug flag turns on verbose logging to stderr and writes a .log
file next to the SQLite database (or ./omni-weather.log when --sqlite is
omitted). See CLI.