Attribute Macro foundations::telemetry::metrics::metrics

source ·
#[metrics]
Available on (crate features logging or metrics or telemetry or tracing) and crate feature metrics only.
Expand description

A macro that allows to define Prometheus metrics.

The macro is a proc macro attribute that should be put on a module containing bodyless functions. Each bodyless function corresponds to a single metric, whose name becomes <global prefix>_<module name>_<bodyless function name>and function’s Rust doc comment is reported as metric description to Prometheus.

§Labels

Arguments of the bodyless functions become labels for that metric.

The metric types must implement prometheus_client::metrics::MetricType, they are reexported from this module for convenience:

The metrics associated with the functions are automatically registered in a global registry, and they can be collected with the collect function.

§Metric attributes

Example below shows how to use all the attributes listed here.

§#[ctor]

#[ctor] attribute allows specifying how the metric should be built (e.g. HistogramBuilder). Constructor should implement the MetricConstructor<MetricType> trait.

§#[optional]

Metrics marked with #[optional] are collected in a separate registry and reported only if collect_optional argument of collect is set to true, or, in case the telemetry server is used, if MetricsSettings::report_optional is set to true.

Can be used for heavy-weight metrics (e.g. with high cardinality) that don’t need to be reported on a regular basis.

§Example

use foundations::telemetry::metrics::{metrics, Counter, Gauge, HistogramBuilder, TimeHistogram};
use serde_with::DisplayFromStr;
use std::net::IpAddr;
use std::io;
use std::sync::Arc;

mod labels {
    use serde::Serialize;

    #[derive(Clone, Eq, Hash, PartialEq, Serialize)]
    #[serde(rename_all = "lowercase")]
    pub enum IpVersion {
        V4,
        V6,
    }

    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize)]
    #[serde(rename_all = "lowercase")]
    pub enum L4Protocol {
        Tcp,
        Udp,
        Quic,
        Unknown,
    }

    #[derive(Clone, Eq, Hash, PartialEq, Serialize)]
    #[serde(rename_all = "lowercase")]
    pub enum ProxiedProtocol {
        Ip,
        Tcp,
        Udp,
        Quic,
        Unknown,
    }

    impl From<L4Protocol> for ProxiedProtocol {
        fn from(l4: L4Protocol) -> Self {
            match l4 {
                L4Protocol::Tcp => Self::Tcp,
                L4Protocol::Udp => Self::Udp,
                L4Protocol::Quic => Self::Quic,
                L4Protocol::Unknown => Self::Unknown,
            }
        }
    }
}

// The generated module contains an implicit `use super::*;` statement.
#[metrics]
pub mod my_app_metrics {
    /// Number of active client connections
    pub fn client_connections_active(
        // Labels with an anonymous reference type will get cloned.
        endpoint: &Arc<String>,
        protocol: labels::L4Protocol,
        ip_version: labels::IpVersion,
        ingress_ip: IpAddr,
    ) -> Gauge;

    /// Histogram of task schedule delays
    #[ctor = HistogramBuilder {
        // 100 us to 1 second
        buckets: &[1E-4, 2E-4, 3E-4, 4E-4, 5E-4, 6E-4, 7E-4, 8E-4, 9E-4, 1E-3, 1E-2, 2E-2, 4E-2, 8E-2, 1E-1, 1.0],
    }]
    pub fn tokio_runtime_task_schedule_delay_histogram(
        task: &Arc<str>,
    ) -> TimeHistogram;

    /// Number of client connections
    pub fn client_connections_total(
        endpoint: &Arc<String>,
        // Labels with type `impl Into<T>` will invoke `std::convert::Into<T>`.
        protocol: impl Into<labels::ProxiedProtocol>,
        ingress_ip: IpAddr,
    ) -> Counter;

    /// Tunnel transmit error count
    pub fn tunnel_transmit_errors_total(
        endpoint: &Arc<String>,
        protocol: labels::L4Protocol,
        ingress_ip: IpAddr,
        // `serde_as` attribute is allowed without decorating the metric with `serde_with::serde_as`.
        #[serde_as(as = "DisplayFromStr")]
        kind: io::ErrorKind,
        raw_os_error: i32,
    ) -> Counter;

    /// Number of stalled futures
    #[optional]
    pub fn debug_stalled_future_count(
        // Labels with a `'static` lifetime are used as is, without cloning.
        name: &'static str,
    ) -> Counter;

    /// Number of Proxy-Status serialization errors
    // Metrics with no labels are also obviously supported.
    pub fn proxy_status_serialization_error_count() -> Counter;
}

fn usage() {
    let endpoint = Arc::new("http-over-tcp".to_owned());
    let l4_protocol = labels::L4Protocol::Tcp;
    let ingress_ip = "127.0.0.1".parse::<IpAddr>().unwrap();
     
    my_app_metrics::client_connections_total(
        &endpoint,
        l4_protocol,
        ingress_ip,
    ).inc();
     
    let client_connections_active = my_app_metrics::client_connections_active(
        &endpoint,
        l4_protocol,
        labels::IpVersion::V4,
        ingress_ip,
    );
     
    client_connections_active.inc();
     
    my_app_metrics::proxy_status_serialization_error_count().inc();

    client_connections_active.dec();
}

§Renamed or reexported crate

The macro will fail to compile if foundations crate is reexported. However, the crate path can be explicitly specified for the macro to workaround that:

mod reexport {
    pub use foundations::*;
}

use self::reexport::telemetry::metrics::Counter;

#[reexport::telemetry::metrics::metrics(crate_path = "reexport")]
mod my_app_metrics {
    /// Total number of tasks workers stole from each other.
    fn tokio_runtime_total_task_steal_count() -> Counter;
}