# Metrics Reference

Prometheus metric names, labels, timing boundaries, stream outcomes, and application-owned collectors in Flasgo 0.11.0.

Source: https://flasgo.dev/reference/metrics/

Metrics are optional and disabled by default. Follow [Observability](/guides/observability/#prometheus-metrics) to install the `metrics` extra and configure bearer authentication. The measurements and extension API below describe Flasgo 0.11.0; check your [installed version](/guides/getting-started/#version-checks-and-upgrades).

Each application has its own per-process registry. Scrape each worker separately or use an appropriate aggregation layer. A load-balanced endpoint can return a different worker's counters on each scrape.

## Names, labels, and units

Tables use full metric names. Histograms expose `_bucket`, `_sum`, and `_count` series. Durations are seconds; byte measurements count payload accepted by ASGI, excluding headers and transport framing.

`route` is a registered template or `<unmatched>`, never an arbitrary request path. HTTP methods use a fixed known set and `_OTHER` for unknown methods. Counters retain exact `status` codes; HTTP duration and response-size histograms use `status_class` (`1xx` through `5xx`). These labels do not contain user identities, session identifiers, tokens, backend addresses, or exception messages.

## HTTP and streaming

| Metric | Type | Labels | Meaning |
| --- | --- | --- | --- |
| `flasgo_http_requests_total` | Counter | `method`, `route`, `status` | Completed request attempts, including response-send failures |
| `flasgo_http_request_duration_seconds` | Histogram | `method`, `route`, `status_class` | Request entry through response sending and request-dependency cleanup |
| `flasgo_http_response_body_size_bytes` | Histogram | `method`, `route`, `status_class` | Body size when sending completed successfully; failed sends have no sample |
| `flasgo_http_response_send_failures_total` | Counter | `method`, `route`, `status` | Responses whose send raised an exception, including streams the client disconnected from |
| `flasgo_http_requests_active` | Gauge | None | Active requests, including subsequent background work |
| `flasgo_http_responses_in_flight` | Gauge | None | Request entry through response sending, excluding subsequent request-dependency cleanup and background work |
| `flasgo_http_response_start_duration_seconds` | Histogram | `method`, `route` | Request entry to successful ASGI response-start send |
| `flasgo_http_response_first_body_duration_seconds` | Histogram | `method`, `route` | Request entry to the first successfully sent non-empty body message |
| `flasgo_http_streams_active` | Gauge | `route` | Streaming send attempts, including iterator cleanup |
| `flasgo_http_streams_total` | Counter | `route`, `outcome` | One terminal outcome per streaming send attempt |
| `flasgo_http_stream_body_bytes_total` | Counter | `route` | Incremental bytes successfully sent, including partial streams |

Stream outcomes are `completed`, `client_disconnect`, `idle_timeout`, `send_timeout`, `max_duration`, `producer_failure`, `send_failure`, `cancelled`, `cleanup_timeout`, and `cleanup_capacity`. The last outcome reports cleanup queue overflow. A producer's own exception is a producer failure; framework timeout contexts distinguish the configured deadlines. An ASGI send `OSError` indicates a closed connection; other send exceptions use `send_failure`.

A stream can send `200` headers and later fail. Check its outcome as well as its status. A cleanup deadline can record `cleanup_timeout` after successful delivery; queued cleanup at capacity uses that outcome too. See [Cleanup Deadline](/guides/streaming-responses/#cleanup-deadline). Existing completed-request counters retain their cancellation behavior; use stream outcomes for explicit stream cancellation accounting.

SSE heartbeats count as body data and bytes. Empty and `HEAD` responses have no first-body sample. ASGI acceptance does not establish when a browser received or rendered content; add an application metric if you need time to the first useful event. Streaming metrics update without buffering the response.

Pre-routing HTTP head-limit denials are recorded as `431` under `<unmatched>`, before OpenTelemetry runs. Metrics scrapes do not contribute ordinary HTTP metrics.

## Backend calls

| Metric | Type | Labels |
| --- | --- | --- |
| `flasgo_backend_operations_total` | Counter | `component`, `operation`, `outcome` |
| `flasgo_backend_operation_duration_seconds` | Histogram | `component`, `operation` |

The fixed component/operation pairs are `session/load`, `session/save`, `authentication/authenticate`, and `rate_limit/check_batch`. They measure logical framework calls across HTTP, protected docs, and WebSocket authorization. Lower storage adapters are not counted again, and metrics scrapes contribute no backend operations.

Session calls include serialization and storage work. Loading an empty session still counts as a successful logical call even if no storage read was necessary; saves count only when persistence is needed. Rate limits count each applicable batch or phase once.

Outcomes are `success`, `failure`, `timeout`, `cancelled`, and `conflict` (an HTTP `409` raised by the operation). An immediate `TimeoutError` cause wrapped by a backend is also recognized. Returning invalid credentials, no session, or a quota denial normally counts as successful operation execution. Use rejection counters to distinguish these expected decisions from infrastructure failures.

## Rejections and internal failures

| Metric | Type | Labels |
| --- | --- | --- |
| `flasgo_http_rejections_total` | Counter | `route`, `reason` |
| `flasgo_internal_errors_total` | Counter | `phase`, `reason` |
| `flasgo_metrics_auth_failures_total` | Counter | `reason` |

HTTP rejection reasons are `host`, `cors`, `csrf`, `authentication`, `permission`, `security_rate_limit`, `rate_limit`, `capacity`, `validation`, `request_head_limit`, `request_body_limit`, `request_read_timeout`, `multipart_limit`, and `form_limit`. Each reason is counted at most once per request. Pre-routing decisions use `<unmatched>`. These counters exclude WebSocket upgrades and metrics scrapes.

Flasgo records framework validation and limit failures at their source, even when an error handler changes the eventual status. It does not infer rejection reasons from application-generated status codes. Capacity exhaustion is distinct from normal quota exhaustion, and backend exceptions are distinct from invalid credentials.

Internal phase/reason pairs include `dispatch/unhandled_exception`, `response_prepare/failure`, `dependency_cleanup/failure`, `error_handler/failure`, `backend/unavailable`, `authentication/backend_error`, `authentication/backend_missing`, and `authorization/permission_error`. Internal authorization failures can also be recorded during WebSocket upgrades.

Metrics authentication has a dedicated counter with `invalid_credentials` and `throttled` reasons. These diagnostic counters update even with `LOG_SECURITY_EVENTS=False`. They overlap request and backend counters, so do not add them together as a request total. Logs remain necessary for investigation and audit records.

## WebSockets, background work, and lifespan

| Metric | Type | Labels |
| --- | --- | --- |
| `flasgo_websocket_connections_total` | Counter | `route`, `outcome` |
| `flasgo_websocket_connection_duration_seconds` | Histogram | `route`, `outcome` |
| `flasgo_websocket_connections_active` | Gauge | `route` |
| `flasgo_background_tasks_total` | Counter | `outcome` |
| `flasgo_background_tasks_active` | Gauge | None |
| `flasgo_background_tasks_pending` | Gauge | None |
| `flasgo_background_task_duration_seconds` | Histogram | None |
| `flasgo_lifespan_events_total` | Counter | `phase`, `outcome` |

WebSocket counters include completed connections and denials. Outcomes include `success`, `closed`, `not_accepted` (the handler returned without accepting and the upgrade was denied with `403`), `handler_error`, `dispatch_error`, `request_head_limit`, `not_found`, `invalid_host`, `invalid_origin`, `rate_limited`, and `denied_<status>` for authorization denials such as `denied_401`, `denied_403`, `denied_429` (security-failure throttle), or `denied_500` (missing or failing backend). The active gauge measures authorized handler calls. Lifespan observations use `startup` or `shutdown` with `success` or `failure`.

Pending background tracking begins when Flasgo sees a response with attached tasks, including tasks added while tracking is active. Constructing an unattached `BackgroundTasks` object does not count. Replaced or undeliverable responses release remaining tasks as `skipped`; cancellation also skips tasks that have not started. Running tasks report `success`, `failure`, or `cancelled`. Skipped tasks have no duration sample.

Tasks remain sequential and best effort. Duration includes the awaited call and thread dispatch for synchronous functions. Cancelling that await cannot stop a Python function already running in a worker thread; the active gauge describes the framework call. Metrics do not add durable delivery or retries.

## Event-loop responsiveness

`flasgo_event_loop_lag_seconds` measures callback delay beyond its scheduled time using the event loop's monotonic clock. A delayed callback creates one observation; missed intervals are not replayed.

The sampler starts after successful lifespan startup when both `METRICS_ENABLED` and `METRICS_EVENT_LOOP_ENABLED` are true. `METRICS_EVENT_LOOP_ENABLED` defaults to `True`; `METRICS_EVENT_LOOP_INTERVAL_SECONDS` defaults to `0.1` and accepts finite numeric values from `0.01` to `60`, excluding booleans.

`flasgo_event_loop_sampler_running` is `1` while scheduled and `0` otherwise. Lifespan exit stops sampling, including exceptional exits. Without startup the lag histogram is absent; after shutdown it retains observations. Check the running gauge and sample count before interpreting lag. A fully blocked loop cannot serve a scrape until it recovers.

## Histogram buckets

All histograms also include an infinity bucket.

| Measurements | Finite bucket boundaries |
| --- | --- |
| HTTP duration, response start/first body, backend duration (seconds) | `0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10` |
| Response body size (bytes) | `64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304` |
| WebSocket duration (seconds) | `1, 5, 15, 30, 60, 300, 900, 1800, 3600, 7200, 21600, 43200, 86400` |
| Background duration (seconds) | HTTP duration buckets plus `30, 60, 300, 900, 3600` |
| Event-loop lag (seconds) | `0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10` |

## Application metrics

`app.metrics_registry` is a read-only property exposing the application's `CollectorRegistry`, or `None` when metrics are disabled. Reading it in disabled mode does not import the optional Prometheus dependency. Register application metrics during setup:

```python
registry = app.metrics_registry
completed = None
if registry is not None:
    from prometheus_client import Counter

    completed = Counter(
        "application_reports_completed_total",
        "Reports completed successfully.",
        registry=registry,
    )

# After successful application work:
if completed is not None:
    completed.inc()
```

`Gauge` and `Histogram` accept the same registry argument. Duplicate names raise `ValueError`. Keep the `None` guard; passing `registry=None` to a client constructor is not a substitute. Use a distinct application prefix and small, fixed label sets.

Collection and encoding run in a worker thread after scrape authentication succeeds. Custom collectors use `registry.register(collector)` and `registry.unregister(collector)`. Implement `describe()` so duplicate names can be checked without collecting. Collectors are trusted application code: keep them fast and synchronous, without network calls, large store scans, state mutations, or sensitive output. Cancelling a scrape cannot forcibly stop an already running collector thread.

Unlabelled gauges, background/metrics-auth/backend outcome counters, and known internal-error pairs start at zero. Other labelled series appear when observed. An absent series does not necessarily mean metrics are disabled. The registry also includes `flasgo_info` and standard Python, garbage-collector, platform, and process collectors. Prometheus text and OpenMetrics negotiation, including supported trace exemplars, are described in [Observability](/guides/observability/#prometheus-metrics).

Sources:

- [flasgo/metrics.py](https://github.com/L1ghtn1ng/flasgo/blob/v0.11.0/flasgo/metrics.py)
- [flasgo/app.py](https://github.com/L1ghtn1ng/flasgo/blob/v0.11.0/flasgo/app.py)
- [flasgo/streaming.py](https://github.com/L1ghtn1ng/flasgo/blob/v0.11.0/flasgo/streaming.py)
- [Metrics regression tests](https://github.com/L1ghtn1ng/flasgo/blob/v0.11.0/tests/test_metrics_observability.py)
- [Security regression tests](https://github.com/L1ghtn1ng/flasgo/blob/v0.11.0/tests/test_security_regressions.py)
