use metrics_process::Collector; use std::{fmt, sync::Arc}; /// The simple alias for function types that are `'static`, `Send`, and `Sync`. pub trait Hook: Fn() + Send + Sync + 'static {} impl Hook for T {} /// A builder-like type to create a new [`Hooks`] instance. pub struct HooksBuilder { hooks: Vec>>, } impl HooksBuilder { /// Registers a [`Hook`]. pub fn with_hook(self, hook: impl Hook) -> Self { self.with_boxed_hook(Box::new(hook)) } /// Registers a [`Hook`] by calling the provided closure. pub fn install_hook(self, f: F) -> Self where F: FnOnce() -> H, H: Hook, { self.with_hook(f()) } /// Registers a [`Hook`]. #[inline] pub fn with_boxed_hook(mut self, hook: Box>) -> Self { self.hooks.push(hook); self } /// Builds the [`Hooks`] collection from the registered hooks. pub fn build(self) -> Hooks { Hooks { inner: Arc::new(self.hooks) } } } impl Default for HooksBuilder { fn default() -> Self { Self { hooks: vec![ Box::new(|| Collector::default().collect()), Box::new(collect_memory_stats), Box::new(collect_io_stats), ], } } } impl std::fmt::Debug for HooksBuilder { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("HooksBuilder") .field("hooks", &format_args!("Vec>, len: {}", self.hooks.len())) .finish() } } /// Helper type for managing hooks #[derive(Clone)] pub struct Hooks { inner: Arc>>>, } impl Hooks { /// Creates a new [`HooksBuilder`] instance. #[inline] pub fn builder() -> HooksBuilder { HooksBuilder::default() } pub(crate) fn iter(&self) -> impl Iterator>> { self.inner.iter() } } impl fmt::Debug for Hooks { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let hooks_len = self.inner.len(); f.debug_struct("Hooks") .field("inner", &format_args!("Arc>>, len: {}", hooks_len)) .finish() } } #[cfg(all(feature = "jemalloc", unix))] fn collect_memory_stats() { use metrics::gauge; use tikv_jemalloc_ctl::{epoch, stats}; use tracing::error; if epoch::advance().map_err(|error| error!(%error, "Failed to advance jemalloc epoch")).is_err() { return } if let Ok(value) = stats::active::read() .map_err(|error| error!(%error, "Failed to read jemalloc.stats.active")) { gauge!("jemalloc.active").set(value as f64); } if let Ok(value) = stats::allocated::read() .map_err(|error| error!(%error, "Failed to read jemalloc.stats.allocated")) { gauge!("jemalloc.allocated").set(value as f64); } if let Ok(value) = stats::mapped::read() .map_err(|error| error!(%error, "Failed to read jemalloc.stats.mapped")) { gauge!("jemalloc.mapped").set(value as f64); } if let Ok(value) = stats::metadata::read() .map_err(|error| error!(%error, "Failed to read jemalloc.stats.metadata")) { gauge!("jemalloc.metadata").set(value as f64); } if let Ok(value) = stats::resident::read() .map_err(|error| error!(%error, "Failed to read jemalloc.stats.resident")) { gauge!("jemalloc.resident").set(value as f64); } if let Ok(value) = stats::retained::read() .map_err(|error| error!(%error, "Failed to read jemalloc.stats.retained")) { gauge!("jemalloc.retained").set(value as f64); } } #[cfg(not(all(feature = "jemalloc", unix)))] const fn collect_memory_stats() {} #[cfg(target_os = "linux")] fn collect_io_stats() { use metrics::counter; use tracing::error; let Ok(process) = procfs::process::Process::myself() .map_err(|error| error!(%error, "Failed to get currently running process")) else { return }; let Ok(io) = process.io().map_err( |error| error!(%error, "Failed to get IO stats for the currently running process"), ) else { return }; counter!("io.rchar").absolute(io.rchar); counter!("io.wchar").absolute(io.wchar); counter!("io.syscr").absolute(io.syscr); counter!("io.syscw").absolute(io.syscw); counter!("io.read_bytes").absolute(io.read_bytes); counter!("io.write_bytes").absolute(io.write_bytes); counter!("io.cancelled_write_bytes").absolute(io.cancelled_write_bytes); } #[cfg(not(target_os = "linux"))] const fn collect_io_stats() {}