deepslate 0.3.1

A high-performance Minecraft server proxy written in Rust.
Documentation
//! Metrics instrumentation for the Deepslate proxy.
//!
//! When the `metrics` Cargo feature is enabled, a Prometheus HTTP exporter is
//! started and the wrapper macros in this module delegate to the [`metrics`]
//! crate. When the feature is disabled, the macros expand to nothing and have
//! zero runtime cost.
//!
//! # Recorded metrics
//!
//! | Name | Type | Description |
//! |------|------|-------------|
//! | `connections_accepted_total` | counter | TCP connections accepted by the listener |
//! | `connections_active` | gauge | Currently open client connections |
//! | `connections_rejected_total` | counter | Connections rejected by rate limiting (label: `reason`) |
//! | `backend_connect_duration_seconds` | histogram | Time to establish a backend TCP connection (label: `server`) |
//! | `backend_connect_failures_total` | counter | Failed backend connection attempts (label: `server`) |
//! | `auth_mojang_duration_seconds` | histogram | Mojang `hasJoined` request latency |
//! | `relay_packets_total` | counter | Packets relayed (label: `direction`) |
//! | `relay_bytes_total` | counter | Bytes relayed (label: `direction`) |

// ── Wrapper macros ──────────────────────────────────────────────────────────
//
// These allow call sites to record metrics without `#[cfg]` guards. When the
// feature is disabled the macro body is empty and the compiler eliminates the
// surrounding code if it has no other side effects.

/// Increment a counter. Accepts the same arguments as [`metrics::counter!`].
macro_rules! counter {
    ($($args:tt)*) => {{
        #[cfg(feature = "metrics")]
        {
            ::metrics::counter!($($args)*).increment(1);
        }
    }};
}

/// Adjust a gauge. Use `.increment(n)` / `.decrement(n)` on the returned handle.
///
/// When the feature is disabled this expands to nothing, so the caller must
/// chain the method call inside the macro invocation:
///
/// ```ignore
/// metrics::gauge_increment!("connections_active", 1.0);
/// ```
macro_rules! gauge_increment {
    ($name:expr, $val:expr $(, $($labels:tt)*)?) => {{
        #[cfg(feature = "metrics")]
        {
            ::metrics::gauge!($name $(, $($labels)*)?).increment($val);
        }
    }};
}

/// Decrement a gauge.
macro_rules! gauge_decrement {
    ($name:expr, $val:expr $(, $($labels:tt)*)?) => {{
        #[cfg(feature = "metrics")]
        {
            ::metrics::gauge!($name $(, $($labels)*)?).decrement($val);
        }
    }};
}

/// Record a histogram observation. Accepts the same arguments as
/// [`metrics::histogram!`].
macro_rules! histogram {
    ($name:expr, $val:expr $(, $($labels:tt)*)?) => {{
        #[cfg(feature = "metrics")]
        {
            ::metrics::histogram!($name $(, $($labels)*)?).record($val);
        }
    }};
}

/// Increment a counter by an arbitrary amount.
macro_rules! counter_add {
    ($name:expr, $val:expr $(, $($labels:tt)*)?) => {{
        #[cfg(feature = "metrics")]
        {
            ::metrics::counter!($name $(, $($labels)*)?).increment($val);
        }
    }};
}

pub(crate) use {counter, counter_add, gauge_decrement, gauge_increment, histogram};

// ── Prometheus exporter ─────────────────────────────────────────────────────

/// Standard Prometheus histogram bucket boundaries (1 ms to 10 s).
///
/// These cover the expected range for both backend TCP connects (sub-millisecond
/// locally, up to seconds over the internet) and Mojang auth HTTP requests
/// (typically 50-500 ms).
#[cfg(feature = "metrics")]
const HISTOGRAM_BUCKETS: &[f64] = &[
    0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
];

/// Install the Prometheus HTTP exporter and bind it to `addr`.
///
/// This must be called once, before any metrics are recorded, to install the
/// global recorder. Histograms are emitted as classic Prometheus histograms
/// (with `_bucket` / `_count` / `_sum` suffixes) rather than summaries, so
/// they can be aggregated with `histogram_quantile()` in `PromQL`.
///
/// # Panics
///
/// Panics if a metrics recorder is already installed.
#[cfg(feature = "metrics")]
pub fn init_metrics(addr: std::net::SocketAddr) {
    let builder = metrics_exporter_prometheus::PrometheusBuilder::new();
    builder
        .set_buckets(HISTOGRAM_BUCKETS)
        .expect("non-empty bucket list")
        .with_http_listener(addr)
        .install()
        .expect("failed to install Prometheus metrics recorder");
}