#prometheus-metrics #grpc-server #prometheus #metrics #tonic #layer #middleware

tonic_prometheus_layer

Tonic-compatible Tower Layer for Prometheus Metrics

11 releases

0.1.10 Nov 1, 2024
0.1.9 Sep 26, 2024
0.1.4 Jun 17, 2024

#345 in Profiling

Download history 39/week @ 2024-08-22 104/week @ 2024-08-29 100/week @ 2024-09-05 48/week @ 2024-09-12 659/week @ 2024-09-19 337/week @ 2024-09-26 120/week @ 2024-10-03 113/week @ 2024-10-10 85/week @ 2024-10-17 96/week @ 2024-10-24 277/week @ 2024-10-31 65/week @ 2024-11-07 103/week @ 2024-11-14 111/week @ 2024-11-21 84/week @ 2024-11-28

385 downloads per month
Used in comprehensive_grpc

MIT license

26KB
411 lines

Tonic Prometheus Layer

A lightweight Prometheus metrics layer for Tonic gRPC client and server

It provides the following metrics:

  • grpc_server_handled_total: a Counter for tracking the total number of completed gRPC server calls.
  • grpc_server_started_total: a Counter for tracking the total number of gRPC server calls started. The difference between this and grpc_server_handled_total equals the number of ongoing server requests.
  • grpc_server_handling_seconds: a Histogram for tracking gRPC server call duration.
  • grpc_client_handled_total: a Counter for tracking the total number of completed gRPC client calls.
  • grpc_client_started_total: a Counter for tracking the total number of gRPC client calls started. The difference between this and grpc_client_handled_total equals the number of ongoing client requests.
  • grpc_client_handling_seconds: a Histogram for tracking gRPC client call duration.

Usage

Add tonic_prometheus_layer to your Cargo.toml.

[dependencies]
tonic_prometheus_layer = "0.1.10"

Server Instrumentation

Add a new layer to your tonic instance:

use std::net::SocketAddr;
use std::str::FromStr;

use rocket::{get, routes};
use rocket::http::Status;
use rocket::response::content::RawText;
use rocket::config::Shutdown;
use rocket::response::status::Custom;
use tonic_prometheus_layer::metrics::GlobalSettings;

use crate::api::server;
use crate::proto::service_server::ServiceServer;

mod api;
mod proto;

#[tokio::main]
async fn main() {
    let addr: SocketAddr = "127.0.0.1:9090".parse().unwrap();

    let service = server::Server {};

    tonic_prometheus_layer::metrics::try_init_settings(GlobalSettings {
        histogram_buckets: vec![0.01, 0.05, 0.1, 0.5, 1.0, 2.5, 5.0, 10.0],
        ..Default::default()
    }).unwrap();

    let metrics_layer = tonic_prometheus_layer::MetricsLayer::new();

    tokio::spawn(async {
        run_http_server("127.0.0.1:8090").await
    });

    tonic::transport::Server::builder()
        .layer(metrics_layer)
        .add_service(ServiceServer::new(service))
        .serve(addr.into())
        .await
        .unwrap();
}

#[get("/metrics")]
async fn metrics() -> Custom<RawText<String>> {
    let body = tonic_prometheus_layer::metrics::encode_to_string().unwrap();

    Custom(Status::Ok, RawText(body))
}

pub async fn run_http_server(addr: &str) {
    let addr = SocketAddr::from_str(addr).unwrap();

    let config = rocket::config::Config {
        address: addr.ip(),
        port: addr.port(),
        shutdown: Shutdown {
            ctrlc: false,
            ..Default::default()
        },
        ..rocket::config::Config::release_default()
    };

    rocket::custom(config)
        .mount("/", routes![metrics])
        .launch()
        .await
        .unwrap();
}

Client Instrumentation

Wrap each individual tonic client Channel object:

#[tokio::main]
async fn main() {
    let channel = tonic::transport::Channel::from_static("http://localhost")
        .connect()
        .await
        .unwrap();
    let channel = tonic_prometheus_layer::MetricsChannel::new(channel);
    let mut client = tonic_health::pb::health_client::HealthClient::new(channel);
}

Dependencies

~7–14MB
~168K SLoC