Observability
Configure request IDs, structured logs, protected metrics, event-loop sampling, and OpenTelemetry traces.
Request IDs and logs
Section titled “Request IDs and logs”Every HTTP request and WebSocket connection gets a locally generated request ID. It is returned as x-request-id, included in Flasgo events, bound to background-task logs, and correlated with active OpenTelemetry traces.
Configure only the flasgo logger hierarchy:
from flasgo import configure_logging
configure_logging(format="json", level="INFO")Or set application settings:
app = Flasgo( settings={ "LOG_FORMAT": "json", "LOG_LEVEL": "INFO", "LOG_SECURITY_EVENTS": True, })LOG_FORMAT accepts text or json. LOG_LEVEL names are case-insensitive, so "info" and "INFO" are equivalent. Log fields are bounded and sanitized to prevent control-character injection. Flasgo logs access outcomes, security denials, WebSocket lifecycle, lifespan failures, and background-task outcomes without logging request bodies or credentials.
Both formats include exception tracebacks and stack information when a record carries them: text output appends them on the lines after the event, and JSON output adds exception and stack fields. Calling configure_logging() again updates the existing Flasgo handler’s format and level, and passing stream= redirects it to the new stream instead of adding a second handler.
Incoming x-request-id is ignored by default. Enable TRUST_INCOMING_REQUEST_ID=True only when a trusted proxy overwrites the header; accepted IDs are limited to 128 ASCII letters, digits, ., _, and -.
Prometheus metrics
Section titled “Prometheus metrics”Install the optional dependency:
uv add flasgo --extra metricsEnable the protected endpoint:
import os
from flasgo import Flasgo
app = Flasgo( settings={ "METRICS_ENABLED": True, "METRICS_PATH": "/metrics", "METRICS_BEARER_TOKEN": os.environ[ "FLASGO_METRICS_TOKEN" ], })The token must contain at least 32 bearer-safe ASCII characters. Malformed or non-ASCII credentials fail closed with 401 Unauthorized. Scrape with:
curl \ -H "Authorization: Bearer $FLASGO_METRICS_TOKEN" \ https://api.example.com/metricsThe endpoint uses constant-time token comparison and excludes scrapes from ordinary HTTP and backend metrics. Authentication failures have a dedicated counter and are handled by the security-failure limiter. Metrics responses are explicitly no-store, and scrapes do not create session or CSRF cookies. Collection and encoding run in a worker thread after authentication succeeds.
HTTP metrics include completed request attempts, active requests, status-class request duration, successfully sent response-body size, and response-send failures. Counters retain exact status codes; duration and size distributions use bounded 1xx through 5xx classes. Known methods use a bounded method set, unknown methods use _OTHER, and labels use route templates such as /users/<int:user_id> rather than attacker-controlled raw paths. Pre-dispatch 431 rejections are counted under <unmatched>.
WebSocket metrics include active and completed connections, route and outcome labels, and duration buckets extending to 24 hours. A handler that returns without calling accept() is recorded as not_accepted rather than success. Background-task and lifespan outcomes are included alongside flasgo_info and standard Python, garbage-collector, platform, and process collectors.
Content negotiation supports both Prometheus text and OpenMetrics. When OpenTelemetry is also active and the scraper requests OpenMetrics, request counters, duration, and response-size observations carry trace_id and span_id exemplars for trace correlation.
The registry is per process. Scrape every worker as a distinct target or place a supported aggregation layer in front of them; a load-balanced metrics URL can return a different worker on each scrape.
Response, backend, and background measurements
Section titled “Response, backend, and background measurements”Since 0.9.1, Flasgo records response-start and first-body timing, incremental stream bytes and terminal outcomes, logical backend call durations and outcomes, framework rejection reasons, and internal failures. It separates responses in flight from active requests (which also include background work) and tracks pending, active, cancelled, and skipped background tasks.
Use the Metrics Reference for exact names, labels, buckets, and timing boundaries. A backend returning invalid credentials normally records successful execution plus an authentication rejection. A stream that sent 200 headers can still fail or exceed its cleanup deadline.
Event-loop sampling and custom metrics
Section titled “Event-loop sampling and custom metrics”With metrics enabled, event-loop lag sampling starts after successful lifespan startup and stops on lifespan exit. METRICS_EVENT_LOOP_ENABLED=True and METRICS_EVENT_LOOP_INTERVAL_SECONDS=0.1 are the defaults. The interval must be a finite number from 0.01 to 60 seconds; set the enabled flag to False to disable sampling. Check flasgo_event_loop_sampler_running alongside lag observations.
Register custom counters, gauges, and histograms through app.metrics_registry during setup. It returns None when metrics are disabled; see Application Metrics for an example and collector requirements.
OpenTelemetry tracing
Section titled “OpenTelemetry tracing”Tracing is a separate optional extra and exports spans over OTLP/HTTP using standard OpenTelemetry environment variables:
uv add flasgo --extra otelexport OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.example.com"export OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer%20${OTEL_TOKEN}"app = Flasgo( settings={ "OTEL_ENABLED": True, "OTEL_SERVICE_NAME": "billing-api", "OTEL_SERVICE_VERSION": "1.4.0", "OTEL_TRACE_SAMPLE_RATIO": 0.25, "OTEL_EXCLUDED_PATHS": {"/health"}, })Flasgo traces HTTP requests and WebSocket connections. Span names and http.route use bounded route templates. Known methods, including QUERY, appear in span names; unknown methods use the stable HTTP placeholder.
For HTTP spans, the real request path is retained while each non-empty query value is replaced with REDACTED. WebSocket URL attributes remain route-template-only. Invalid or ambiguous Host values are removed from the scope passed to instrumentation before a denial is traced.
Oversized HTTP and WebSocket request heads are rejected before tracing or session loading. These denials use a fresh local request ID even with TRUST_INCOMING_REQUEST_ID=True; HTTP 431 metrics still record the rejection under <unmatched>.
OTEL_EXCLUDED_PATHS accepts exact paths or route templates such as /reset/<token>; template exclusions also apply to method-rejection spans. The metrics path is always excluded.
Flasgo uses a parent-based ratio sampler and a batch span processor. A provider it builds is process-global by default so other instrumentations can share its sampler and exporter; set OTEL_SET_GLOBAL_PROVIDER=False to keep it private. You may instead pass tracer_provider= to Flasgo(...) for tests or application-owned lifecycle management; this enables tracing even when OTEL_ENABLED is False.
At lifespan shutdown, Flasgo flushes a provider it created but does not shut it down, so a later lifespan on the same app, such as a second test client context, keeps exporting spans. The OpenTelemetry SDK closes it at interpreter exit. Flasgo never flushes or closes a provider passed as tracer_provider=.
Active trace and span IDs are added to framework event logs automatically.
Dependency, streaming, and storage failures
Section titled “Dependency, streaming, and storage failures”Flasgo reports dependency cleanup, streaming, and shared-storage failures through structured events. Monitor dependency-cleanup-failed, response-prepare-failed, response-send-failed, and shared-storage-unavailable events. A session save failure can be reported during response preparation; StoreUnavailable is the exception type, not an event name.
A client disconnecting from a streaming or SSE response is routine, so Flasgo logs it as an INFO http-client-disconnected access event instead of an ERROR response-send-failed event. The stream outcome is client_disconnect, and the attempt still counts in flasgo_http_response_send_failures_total. Other send failures keep the response-send-failed event and an http-response-send-failed access event.
An HTTP stream remains active until sending completes or fails. Its completion log and duration cover the connection lifetime; a response whose headers were already sent can retain a 200 status while recording a send failure. Use the response-send outcome as well as status when monitoring streams. Request dependency cleanup failures are logged after delivery and cannot retract the response.
Never add session identifiers, storage connection strings, rejected output values, or credentials to custom log fields. See Streaming Responses and Shared Quotas and Sessions for their failure boundaries.
Operational boundaries
Section titled “Operational boundaries”- Keep metrics behind network controls in addition to bearer authentication.
- Send JSON logs to a bounded central sink and define retention.
- Keep trace sampling and export queues deliberate for the service’s traffic volume.
- Alert on sustained
5xx, security denials, WebSocket failures, lifespan failures, and exporter health. - Avoid high-cardinality labels or attributes such as user IDs, raw tokens, or arbitrary exception messages.
Sources: