Metrics Reference
Prometheus metric names, labels, timing boundaries, stream outcomes, and application-owned collectors in Flasgo 0.11.0.
Metrics are optional and disabled by default. Follow Observability to install the metrics extra and configure bearer authentication. The measurements and extension API below describe Flasgo 0.11.0; check your installed version.
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
Section titled “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
Section titled “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. 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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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:
registry = app.metrics_registrycompleted = Noneif 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.
Sources: