1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under both the MIT license found in the
* LICENSE-MIT file in the root directory of this source tree and the Apache
* License, Version 2.0 found in the LICENSE-APACHE file in the root directory
* of this source tree.
*/
use auto_impl::auto_impl;
use fbinit::FacebookInit;
pub type BoxSingletonCounter = Box<dyn SingletonCounter + Send + Sync>;
pub type BoxCounter = Box<dyn Counter + Send + Sync>;
pub type BoxTimeseries = Box<dyn Timeseries + Send + Sync>;
pub type BoxHistogram = Box<dyn Histogram + Send + Sync>;
pub type BoxLocalCounter = Box<dyn Counter>;
pub type BoxLocalTimeseries = Box<dyn Timeseries>;
pub type BoxLocalHistogram = Box<dyn Histogram>;
/// SingletonCounter is a non-aggregated, global counter. Use this if you don't want any aggregation,
/// and just want to expose a value through stats.
#[auto_impl(Box)]
pub trait SingletonCounter {
/// Sets the value of the counter
fn set_value(&self, fb: FacebookInit, value: i64);
/// Increment the value of the counter
fn increment_value(&self, fb: FacebookInit, value: i64);
/// Gets the current value of the counter
fn get_value(&self, fb: FacebookInit) -> Option<i64>;
}
/// Counter is the simplest type of aggregated stat, it behaves as a single number that can be
/// incremented.
#[auto_impl(Box)]
pub trait Counter {
/// Increments the counter by the given amount.
fn increment_value(&self, value: i64);
}
/// Timeseries is a type of stat that can aggregate data send to it into
/// predefined intervals of time. Example aggregations are average, sum or rate.
#[auto_impl(Box)]
pub trait Timeseries {
/// Adds value to the timeseries. It is being aggregated based on ExportType
fn add_value(&self, value: i64);
/// You might want to call this method when you have a very hot counter to avoid some
/// congestions on it.
/// Value is the sum of values of the samples and nsamples is the number of samples.
/// Please notice that difference in the value semantic compared to
/// `Histogram::add_repeated_value`.
fn add_value_aggregated(&self, value: i64, nsamples: u32);
}
/// Histogram is a type of stat that can aggregate data send to it into
/// predefined buckets. Example aggregations are average, sum or P50 (percentile).
/// The aggregation should also happen on an interval basis, since its rarely
/// useful to see aggregated all-time stats of a service running for many days.
#[auto_impl(Box)]
pub trait Histogram {
/// Adds value to the histogram. It is being aggregated based on ExportType
fn add_value(&self, value: i64);
/// You might want to call this method when you have a very hot counter to avoid some
/// congestions on it. The default implementation simply calls add_value O(nsamples) times.
/// If you have a performance-sensitive use case, check whether your Stats type has an O(1)
/// implementation.
/// Value is the value of a single samples and nsamples is the number of samples.
/// Please notice that difference in the value semantic compared to
/// `Timeseries::add_value_aggregated`.
fn add_repeated_value(&self, value: i64, nsamples: u32) {
for _ in 0..nsamples {
self.add_value(value);
}
}
/// Flush any buffered data so that it is observable externally. Should only
/// be used for testing.
fn flush(&self) {}
}
mod localkey_impls {
use std::thread::LocalKey;
use super::*;
pub trait CounterStatic {
fn increment_value(&'static self, value: i64);
}
impl<T: Counter> CounterStatic for LocalKey<T> {
fn increment_value(&'static self, value: i64) {
self.with(|s| T::increment_value(s, value));
}
}
pub trait TimeseriesStatic {
fn add_value(&'static self, value: i64);
fn add_value_aggregated(&'static self, value: i64, nsamples: u32);
}
impl<T: Timeseries> TimeseriesStatic for LocalKey<T> {
fn add_value(&'static self, value: i64) {
self.with(|s| s.add_value(value));
}
fn add_value_aggregated(&'static self, value: i64, nsamples: u32) {
self.with(|s| s.add_value_aggregated(value, nsamples));
}
}
pub trait HistogramStatic {
fn add_value(&'static self, value: i64);
fn add_repeated_value(&'static self, value: i64, nsamples: u32);
}
impl<T: Histogram> HistogramStatic for LocalKey<T> {
fn add_value(&'static self, value: i64) {
self.with(|s| s.add_value(value));
}
fn add_repeated_value(&'static self, value: i64, nsamples: u32) {
self.with(|s| s.add_repeated_value(value, nsamples));
}
}
}
pub use localkey_impls::*;
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_add_repeated_value() {
// Arrange
struct DummyHistogram {
n_added: std::sync::atomic::AtomicU32,
}
impl Histogram for DummyHistogram {
fn add_value(&self, _value: i64) {
self.n_added
.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
}
}
let dummy_histogram = DummyHistogram {
n_added: std::sync::atomic::AtomicU32::new(0),
};
let n_to_add: u32 = 3;
// Act
dummy_histogram.add_repeated_value(0, n_to_add);
// Assert
assert_eq!(dummy_histogram.n_added.into_inner(), n_to_add);
}
}