8000 GitHub - luzzodev/fastapi-prometheus-lite: πŸš€ A fast, minimal Prometheus middleware for FastAPI β€” no multiprocessing, no env config, no body reads. Just clean, efficient metrics.
[go: up one dir, main page]

Skip to content

πŸš€ A fast, minimal Prometheus middleware for FastAPI β€” no multiprocessing, no env config, no body reads. Just clean, efficient metrics.

License

Notifications You must be signed in to change notification settings

luzzodev/fastapi-prometheus-lite

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

36 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

FastAPI Prometheus Lite

Status

A fast, minimal Prometheus middleware for FastAPI β€” no multiprocessing, no env config, no body reads. Just clean, efficient metrics.


πŸ“‹ Table of Contents


πŸ“– Description

FastAPI Prometheus Lite is a lightweight and high-performance Prometheus middleware for FastAPI applications. It provides essential request-level metrics such as request counts and durations, while avoiding the overhead of more feature-rich instrumentors. This library is ideal for developers who want precise, clean metrics without pulling in unnecessary complexity.

⚠️ Limitations & Caveats: This project was built for personal use and has certain constraints that are acceptable for my setup:

  • Does not support multi-worker (prefork) deployments. If you run multiple Gunicorn/Uvicorn workers in the same process, metrics will not aggregate correctly.
  • Works correctly when you deploy multiple replicas (separate processes or containers) behind a load‑balancer, since each replica maintains its own registry.
  • Live collectors cannot access the route template (matched_path_template) in the ASGI scope; only post-request collectors can utilize it for labeling.
  • Instrumentation currently supports only base HTTP and WebSocket routes patched via Starlette.

✨ Why Lite?

This project was created with a clear goal in mind: provide just enough instrumentation for FastAPI performance and visibility β€” and nothing more.

Compared to full-fledged solutions, this package intentionally avoids:

  • ❌ Multiprocessing support (unnecessary for most simple deployments)
  • ❌ Environment-based configuration (explicit is better than implicit)
  • ❌ Automatic registration of default or system-level metrics
  • ❌ Double routing (avoiding processing the same request twice through middleware)
  • ❌ Request and response body parsing (to keep things fast and safe)

Instead, it focuses on:

  • βœ… Precise, labeled Prometheus metrics (http_requests_total, http_request_duration_seconds)
  • βœ… Zero-config setup with FastAPI’s native middleware interface
  • βœ… Minimal dependency tree (just fastapi and prometheus-client)
  • βœ… ASGI-native implementation with optional Starlette type support

It’s perfect for lean microservices, observability-conscious APIs, and production-like setups where performance and control matter.

⚠️ Note: This project was developed for personal use and as a showcase of FastAPI middleware design. It is not intended to be a production-ready replacement for full-featured solutions. Feel free to use it as inspiration, reference, or a learning tool β€” not as a drop-in replacement unless you've reviewed and tailored it to your own needs.

πŸš€ Quickstart

from fastapi import FastAPI
from fastapi_prometheus_lite import Instrumentor
from fastapi_prometheus_lite.metrics.post_metrics import TotalRequests
from fastapi_prometheus_lite.metrics.live_metrics import GlobalActiveRequests

app = FastAPI()

instrumentor = Instrumentor(
    metrics_collectors=[TotalRequests()],
    live_metrics_collectors=[GlobalActiveRequests()],
    excluded_paths=["^/docs"]
).instrument(app).expose(app)

βš™οΈ Instrumentor Usage

The Instrumentor (alias for FastApiPrometheusLite) provides a simple, fluent API for integrating Prometheus metrics into your FastAPI application.

Initialize

Instantiate the instrumentor with optional configuration:

instrumentor = Instrumentor(
    registry=None,                 # optional CollectorRegistry, defaults to global
    metrics_collectors=[],         # post-request collectors (list of CollectorBase; e.g., TotalRequests())
    live_metrics_collectors=[],    # in-request collectors (list of LiveCollectorBase; e.g., GlobalActiveRequests())
    excluded_paths=["^/health$"], # regex patterns of paths to skip
    static_labels  = None, # a dictionary[str, str] containing static labels
)
  • registry: Prometheus CollectorRegistry (uses global registry if None).
  • metrics_collectors: list of CollectorBase instances executed after each request (counters, histograms, etc.).
  • live_metrics_collectors: list of LiveCollectorBase instances wrapping each request (during execution, e.g., in-flight gauges, timers).
  • excluded_paths: regex patterns matching request paths to skip instrumentation.
  • static_labels: a dictionary[str, str] containing static labels.

Instrument the App

Attach the Prometheus middleware to your FastAPI application:

instrumentor.instrument(app)

This call adds the ASGI middleware that records metrics on every HTTP request (except excluded paths).

Expose Metrics Endpoint

Register a scrape endpoint that serves collected metrics:

instrumentor.expose(
    app,
    endpoint="/metrics",      # URL path (default: "/metrics")
    include_in_schema=True,     # include in OpenAPI schema
    tags=["Metrics"],          # optional OpenAPI tags
)
  • endpoint: path at which metrics are exposed.
  • include_in_schema: toggle inclusion in OpenAPI docs.
  • tags: list of tags for documentation grouping.
  • **kwargs: additional parameters forwarded to app.get().

Fluent Chaining

All methods return the instrumentor instance, allowing chaining:

Instrumentor(...)
    .instrument(app)
    .expose(app)

Static Labels

From 0.4.0 static labels are supported. This feature is intended to support those use-cases where you want to collect data with a fine level of detail and you do not want to deal with aggregation at this level. Ex pod, replica, etc. Keep in mind anyway that this could affect cardinality and should be used only for specific well known cases.

Note: In the current implementation the static labels are supported only under the usage of generate_latest offered by the current lib.

from fastapi_prometheus_lite.registry.utils import generate_latest

Now this is the default function used in the /metric endpoint. Static Labels are attached to all metrics only at generation time and are not part of any collector in any way.

Building Custom Collectors

For more advanced collectors, you can extend the provided typed-base abstractions:

  • CounterCollectorBase, GaugeCollectorBase, HistogramCollectorBase, SummaryCollectorBase for post-request collectors.
  • LiveCounterCollectorBase, LiveGaugeCollectorBase, LiveHistogramCollectorBase, LiveSummaryCollectorBase for in-request (live) collectors.

Example: Custom Counter

from fastapi_prometheus_lite.collectors.typed_collector_bases import CounterCollectorBase
from fastapi_prometheus_lite.collectors.base import MetricsContext


class MyRequestCounter(CounterCollectorBase):
    def __call__(self, ctx: MetricsContext):
        matched, path_format = ctx.matched_path_template
        labels = {
            "method": ctx.request_method,
            "path": path_format,
            "status": str(ctx.response.status_code),
        }
        # Increment the counter with custom labels
        self.metric.labels(**labels).inc()

Example: Custom Live Gauge Collector

from fastapi_prometheus_lite.collectors.typed_live_collector_bases import LiveGaugeCollectorBase


class InFlightRequestsGauge(LiveGaugeCollectorBase):
    def __enter__(self):
        labels = {"method": self._scope["method"], "path": self._scope["path"]}
        self.metric.labels(**labels).inc()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        labels = {"method": self._scope["method"], "path": self._scope["path"]}
        self.metric.labels(**labels).dec()

Use these base classes when initializing your instrumentor:

instrumentor = Instrumentor(
    metrics_collectors=[MyRequestCounter()],
    live_metrics_collectors=[InFlightRequestsGauge()],
    excluded_paths=["^/docs$"],
).instrument(app).expose(app)

Credits

This project is heavily inspired by prometheus-fastapi-instrumentator by @trallnag.

It borrows the overall design philosophy of exposing route-aware, labeled Prometheus metrics for FastAPI applications.

About

πŸš€ A fast, minimal Prometheus middleware for FastAPI β€” no multiprocessing, no env config, no body reads. Just clean, efficient metrics.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

0