Skip to content
Reference

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.

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.

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.

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.

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.

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.

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.

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

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_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.

Sources: