🔎 Prometheus metrics middleware for Axum

Overview

Axum-Prometheus

A Prometheus middleware to collect HTTP metrics for Axum applications.

axum-prometheus relies on metrics_exporter_prometheus as a backed to interact with Prometheus.

Metrics

By default three HTTP metrics are tracked

  • axum_http_requests_total (labels: endpoint, method): the total number of HTTP requests handled (counter)
  • axum_http_requests_duration_seconds (labels: endpoint, method, status): the request duration for all HTTP requests handled (histogram)
  • axum_http_requests_pending (labels: endpoint, method): the number of currently in-flight requests (gauge)

Note that in the future request size metric is also planned to be implemented.

Usage

Add axum-prometheus to your Cargo.toml.

[dependencies]
axum-prometheus = "0.1.0"

Then you instantiate the prometheus middleware:

use std::{net::SocketAddr, time::Duration};
use axum::{routing::get, Router};
use axum_prometheus::PrometheusMetricLayer;

#[tokio::main]
async fn main() {
    let (prometheus_layer, metric_handle) = PrometheusMetricLayer::pair();
    let app = Router::new()
        .route("/fast", get(|| async {}))
        .route(
            "/slow",
            get(|| async {
                tokio::time::sleep(Duration::from_secs(1)).await;
            }),
        )
        .route("/metrics", get(|| async move { metric_handle.render() }))
        .layer(prometheus_layer);

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

Note that the /metrics endpoint is not automatically exposed, so you need to add that as a route manually. Calling the /metrics endpoint will expose your metrics:

axum_http_requests_total{method="GET",endpoint="/metrics"} 5
axum_http_requests_pending{method="GET",endpoint="/metrics"} 1
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="0.005"} 4
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="0.01"} 4
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="0.025"} 4
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="0.05"} 4
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="0.1"} 4
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="0.25"} 4
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="0.5"} 4
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="1"} 4
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="2.5"} 4
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="5"} 4
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="10"} 4
axum_http_requests_duration_seconds_bucket{method="GET",status="200",endpoint="/metrics",le="+Inf"} 4
axum_http_requests_duration_seconds_sum{method="GET",status="200",endpoint="/metrics"} 0.001997171
axum_http_requests_duration_seconds_count{method="GET",status="200",endpoint="/metrics"} 4

This crate is similar to (and takes inspiration from) actix-web-prom and rocket_prometheus, and also builds on top of davidpdrsn's earlier work with LifeCycleHooks in tower-http.

You might also like...
Rust Axum+SQLx Sample

rust-axum-sqlx-sample Install git clone https://github.com/web3ten0/rust-axum-sqlx-1.git cd rust-axum-sqlx-1/local docker-compose up -d sh scripts/exe

🪪 Session-based user authentication for Axum.

axum-login 🪪 Session-based user authentication for Axum. 🎨 Overview axum-login is a Tower middleware providing session-based user authentication for

Layers, extractors and template engine wrappers for axum based Web MVC applications

axum-template Layers, extractors and template engine wrappers for axum based Web MVC applications Getting started Cargo.toml [dependencies] axum-templ

Provides json/csv/protobuf streaming support for axum

axum streams for Rust Library provides HTTP response streaming support for axum web framework: JSON array stream format JSON lines stream format CSV s

Simple example of axum, sqlx with sqlite and utoipa (swagger) - without auth

axum_crud_api Simple example to learn creating CRUD rest apis in Rust with axum, sqlx with sqlite and utoipa (swagger) - without auth Also shows how t

Heavy Metal Leptos Stack with Tailwind, Axum, Sqlite, and Cargo Leptos

Heavy Metal Stack Leptos stack with Axum, TailwindCSS, and Sqlite This example creates a basic todo app with an Axum backend that uses Leptos' server

A crate built on top of `axum-sessions`, implementing the CSRF Synchronizer Token Pattern

Axum Synchronizer Token Pattern CSRF prevention This crate provides a Cross-Site Request Forgery protection layer and middleware for use with the axum

An adapter to easily allow an Axum server to be run within a Cloudflare worker.

axum-cloudflare-adapter An adapter to easily allow an Axum server to be run within a Cloudflare worker. Usage use worker::*; use axum::{ response

Starter template for use with the Leptos web framework and Axum.

Leptos Axum Starter Template This is a template for use with the Leptos web framework and the cargo-leptos tool using Axum. Creating your template rep

Comments
  • allow the metric names to be changed with compile-time env vars

    allow the metric names to be changed with compile-time env vars

    To match local conventions I needed to adjust the metric names and I'm not able to modify the scrape config. Thus, I made this patch which allows these AXUM_HTTP_* constants to be changed via envs.

    It is common for applications metrics to have an application-specific prefix. Metrics can be renamed at scrape time of course, but in some environments an application is expected to merely expose metrics on a given port and no other configuration is allowed.

    This commit allows the metric names to be changed at compile time by setting the following environment variables:

    • AXUM_HTTP_REQUESTS_TOTAL
    • AXUM_HTTP_REQUESTS_DURATION_SECONDS
    • AXUM_HTTP_REQUESTS_PENDING

    In the absence of these environment variables, the metric names are unchanged.

    This commit also documents the usage of these envs and suggests setting them in .cargo/config.toml

    opened by dylanwh 4
  • Custom route patterns: groupings and ignore

    Custom route patterns: groupings and ignore

    Trying to address #4.

    Added options to skip reporting route patterns to Prometheus, and also an option to group routes into one endpoint. Added a builder struct to be able to easily customize these. For more info, see the docs in the PR.

    This is a draft, because I'm not sure whether this is a viable solution.

    enhancement suggestion 
    opened by Ptrskay3 0
  • axum_http_requests_pending metric leak

    axum_http_requests_pending metric leak

    Hi,

    I noticed that the metric axum_http_requests_pending does not get properly decremented when the client connection is aborted, so its value may increase over time.

    I can reproduce with this test:

    use axum::{routing::get, Router};
    use axum_prometheus::PrometheusMetricLayer;
    use tokio::{
        select,
        time::{sleep, Duration},
    };
    
    #[tokio::test]
    async fn metrics_socket() {
        let (metrics, prom) = PrometheusMetricLayer::pair();
        let app: Router = Router::new()
            .route(
                "/test",
                get(|| async { sleep(Duration::from_millis(10)).await }),
            )
            .layer(metrics);
    
        assert_eq!(prom.render(), "");
    
        let (tx, rx) = tokio::sync::oneshot::channel();
        let server = tokio::spawn(async {
            axum::Server::bind(&([127, 0, 0, 1], 3000).into())
                .serve(app.into_make_service())
                .with_graceful_shutdown(async {
                    rx.await.unwrap();
                })
                .await
                .unwrap();
        });
    
        select! {
            r = reqwest::get("http://localhost:3000/test") => {r.unwrap();},
            _ = sleep(Duration::from_millis(10)) => (),
        }
    
        sleep(Duration::from_millis(10)).await;
        assert_eq!(
            prom.render()
                .lines()
                .filter(|l| l.starts_with("axum_http_requests_pending"))
                .map(|l| l.split(" ").last().unwrap())
                .collect::<String>(),
            "0"
        );
        tx.send(()).unwrap();
        server.await.unwrap();
    }
    
    

    or by stress-testing with autocannon and observing the metric value is not 0 after it ends

    (also the axum_http_requests_duration_seconds histogram is not updated, but i'm unsure which should be the correct behaviour for aborted requests here)

    opened by m-ronchi 0
  • label metrics by route

    label metrics by route

    Hi, is it possible to label the metrics by configured route instead of endpoint?

    e.g. if I have a route with path parameters (e.g. /foo/:bar/:baz) I want to get one set of metrics (or optionally one per allowed value of :bar and :baz by adding additional labels) instead of one per actual endpoint hit by anyone

    currently there is a possible problem with exploding metrics if path params have high cardinality, either intentionally or someone trying to break our APIs

    enhancement help wanted 
    opened by m-ronchi 8
Releases(0.2.0)
Owner
Péter Leéh
A fan of making things fast, secure and robust.
Péter Leéh
🥠 Sessions as a `tower` and `axum` middleware.

tower-sessions ?? Sessions as a `tower` and `axum` middleware. ?? Overview This crate provides sessions, key-value pairs associated with a site visito

Max Countryman 48 Oct 11, 2023
Axum + JWT authentication Middleware that allows you to start building your application fast

axum_jwt_ware Integration Guide Simple Axum + JWT authentication middleware with implemented Login and refresh token. Goal I aim to simplify the proce

Eze Sunday 3 Dec 2, 2023
axum-serde is a library that provides multiple serde-based extractors and responders for the Axum web framework.

axum-serde ?? Overview axum-serde is a library that provides multiple serde-based extractors / responses for the Axum web framework. It also offers a

GengTeng 3 Dec 12, 2023
A cookie manager middleware built on top of tower.

tower-cookies A cookie manager middleware built on top of tower. Example With axum: use axum::{handler::get, Router}; use std::net::SocketAddr; use to

Imbolc 47 Dec 9, 2022
Rate limit middleware for Poem framework

Rate limit middleware for Poem framework Usage Check examples, poem-ratelimit is available on crates.io. A yaml configuration file is used to set limi

Devs Day 5 Sep 22, 2022
Write Rack middleware in Rust.

RacksOnRacks Write Rack middleware in Rust. This repo is a proof of concept and should be used as an example. The best way to use this at the moment w

Odin 5 Jul 11, 2023
Experiments with Rust CRDTs using Tokio web application framework Axum.

crdt-genome Synopsis Experiments with Rust CRDTs using Tokio web application framework Axum. Background Exploring some ideas of Martin Kleppmann, part

dougfort 3 Mar 18, 2022
Axum web framework tutorial for beginners.

Axum Tutorial For Beginners Hello web developers! This tutorial will cover how to write simple web applications in rust with axum framework. If you ar

Eray Karatay 46 Jan 5, 2023
Yew + Axum + blog = Yab

Yew + Axum + blog = Yab

STUDIO RSBM 13 Dec 5, 2022
Demo of Rust and axum web framework

Demo of Rust and axum web framework Demonstration of: Rust: programming language that focuses on reliability and stability. axum: web framework that f

Joel Parker Henderson 115 Dec 29, 2022