Repository: firecracker-microvm/firecracker
File: src/vmm/src/logger/metrics.rs (Rust)

Code before:
use crate::devices::virtio::balloon::metrics as balloon_metrics;
use crate::devices::virtio::net::metrics as net_metrics;
use crate::devices::virtio::rng::metrics as entropy_metrics;
use crate::devices::virtio::vhost_user_metrics;
use crate::devices::virtio::virtio_block::metrics as block_metrics;
use crate::devices::virtio::vsock::metrics as vsock_metrics;

#[macro_export]
/// records aggregate (min/max/sum) for the given $metrics
/// This macro captures delta between start_time and current time
/// and updates min/max/sum metrics so it expects that start_time
/// is captured at the appropriate location.
macro_rules! record_latency_summary {
    ($metrics:expr, $start_time:ident) => {
        let delta_us = utils::time::get_time_us(utils::time::ClockType::Monotonic) - $start_time;
        $metrics.sum_us.add(delta_us);
        let min_us = $metrics.min_us.fetch();
        let max_us = $metrics.max_us.fetch();
        if (0 == min_us) || (min_us > delta_us) {
            $metrics.min_us.store(delta_us);
        }
        if (0 == max_us) || (max_us < delta_us) {
            $metrics.max_us.store(delta_us);
        }
    };
}

/// Static instance used for handling metrics.
pub static METRICS: Metrics<FirecrackerMetrics, FcLineWriter> =
    Metrics::<FirecrackerMetrics, FcLineWriter>::new(FirecrackerMetrics::new());

/// Metrics system.
// All member fields have types which are Sync, and exhibit interior mutability, so
// we can call operations on metrics using a non-mut static global variable.
#[derive(Debug)]
pub struct Metrics<T: Serialize, M: Write + Send> {
    // Metrics will get flushed here.
    metrics_buf: OnceLock<Mutex<M>>,
    pub app_metrics: T,
}

impl<T: Serialize + Debug, M: Write + Send + Debug> Metrics<T, M> {
    /// Creates a new instance of the current metrics.
    // TODO: We need a better name than app_metrics (something that says that these are the actual
    // values that we are writing to the metrics_buf).
    pub const fn new(app_metrics: T) -> Metrics<T, M> {
        Metrics {
            metrics_buf: OnceLock::new(),
            app_metrics,
        }
    }

Review comment (performance):
Could this be
```suggestion
impl LatencyAggregateMetrics {
    pub fn record_latency_summary(&mut self, start_time: u64) {
        let delta_us = utils::time::get_time_us(utils::time::ClockType::Monotonic) - start_time;
        self.sum_us.add(delta_us);
        let min_us = self.min_us.fetch();
        let max_us = self.max_us.fetch();
        if (0 == min_us) || (min_us > delta_us) {
            self.min_us.store(delta_us);
        }
        if (0 == max_us) || (max_us < delta_us) {
            self.max_us.store(delta_us);
        }
    }
}
```

Code after:
use crate::devices::virtio::balloon::metrics as balloon_metrics;
use crate::devices::virtio::net::metrics as net_metrics;
use crate::devices::virtio::rng::metrics as entropy_metrics;
use crate::devices::virtio::vhost_user_metrics;
use crate::devices::virtio::virtio_block::metrics as block_metrics;
use crate::devices::virtio::vsock::metrics as vsock_metrics;

/// Static instance used for handling metrics.
pub static METRICS: Metrics<FirecrackerMetrics, FcLineWriter> =
    Metrics::<FirecrackerMetrics, FcLineWriter>::new(FirecrackerMetrics::new());

/// Metrics system.
// All member fields have types which are Sync, and exhibit interior mutability, so
// we can call operations on metrics using a non-mut static global variable.
#[derive(Debug)]
pub struct Metrics<T: Serialize, M: Write + Send> {
    // Metrics will get flushed here.
    metrics_buf: OnceLock<Mutex<M>>,
    pub app_metrics: T,
}

impl<T: Serialize + Debug, M: Write + Send + Debug> Metrics<T, M> {
    /// Creates a new instance of the current metrics.
    // TODO: We need a better name than app_metrics (something that says that these are the actual
    // values that we are writing to the metrics_buf).
    pub const fn new(app_metrics: T) -> Metrics<T, M> {
        Metrics {
            metrics_buf: OnceLock::new(),
            app_metrics,
        }
    }

    /// Initialize metrics system (once and only once).
    /// Every call made after the first will have no effect besides returning `Ok` or `Err`.
    ///
    /// This function is supposed to be called only from a single thread, once.
    /// It is not thread-safe and is not meant to be used in a multithreaded
    /// scenario. The reason `is_initialized` is an `AtomicBool` instead of
    /// just a `bool` is that `lazy_static` enforces thread-safety on all its
    /// members.
    ///
    /// # Arguments
    ///
    /// * `metrics_dest` - Buffer for JSON formatted metrics. Needs to implement `Write` and `Send`.
    pub fn init(&self, metrics_dest: M) -> Result<(), MetricsError> {
        self.metrics_buf
            .set(Mutex::new(metrics_dest))
            .map_err(|_| MetricsError::AlreadyInitialized)
    }

    /// Writes metrics to the destination provided as argument upon initialization of the metrics.