mirror of https://github.com/fralalonde/dipstick
259 lines
7.1 KiB
Rust
Executable File
259 lines
7.1 KiB
Rust
Executable File
//! Dipstick metrics core types and traits.
|
|
//! This is mostly centered around the backend.
|
|
//! Application-facing types are in the `app` module.
|
|
|
|
use self::Command::*;
|
|
|
|
use std::sync::Arc;
|
|
|
|
// TODO define an 'AsValue' trait + impl for supported number types, then drop 'num' crate
|
|
pub use num::ToPrimitive;
|
|
|
|
/// Base type for recorded metric values.
|
|
// TODO should this be f64? f32?
|
|
pub type Value = u64;
|
|
|
|
|
|
/// Base type for sampling rate.
|
|
/// - 1.0 records everything
|
|
/// - 0.5 records one of two values
|
|
/// - 0.0 records nothing
|
|
/// The actual distribution (random, fixed-cycled, etc) depends on selected sampling method.
|
|
pub type Sampling = f64;
|
|
|
|
/// Do not sample, use all data.
|
|
pub const FULL_SAMPLING_RATE: Sampling = 1.0;
|
|
|
|
/// Used to differentiate between metric kinds in the backend.
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
|
pub enum Kind {
|
|
/// Handling one item at a time.
|
|
Marker,
|
|
/// Handling quantities or multiples.
|
|
Counter,
|
|
/// Reporting instant measurement of a resource at a point in time.
|
|
Gauge,
|
|
/// Measuring a time interval, internal to the app or provided by an external source.
|
|
Timer,
|
|
}
|
|
|
|
/// A namespace for metrics.
|
|
/// Does _not_ include the metric's "short" name itself.
|
|
/// Can be empty.
|
|
#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
|
|
pub struct Namespace {
|
|
inner: Vec<String>,
|
|
}
|
|
|
|
lazy_static! {
|
|
/// Root namespace contains no string parts.
|
|
pub static ref ROOT_NS: Namespace = Namespace { inner: vec![] };
|
|
}
|
|
|
|
//impl<'a> Index<&'a str> for Namespace {
|
|
// type Output = Self;
|
|
//
|
|
// /// Returns a copy of this namespace with the "index" appended to it.
|
|
// /// Returned reference should be dereferenceable:
|
|
// ///
|
|
// /// ```
|
|
// /// let sub_ns = *ROOT_NS["sub_ns"];
|
|
// /// ```
|
|
// ///
|
|
// fn index(&self, index: &'a str) -> &Self::Output {
|
|
// let mut clone = self.inner.clone();
|
|
// if !index.is_empty() {
|
|
// clone.push(index.into());
|
|
// }
|
|
// &Namespace{ inner: clone }
|
|
// }
|
|
//}
|
|
|
|
impl Namespace {
|
|
|
|
/// Returns true if this namespace contains no elements.
|
|
pub fn is_empty(&self) -> bool {
|
|
self.inner.is_empty()
|
|
}
|
|
|
|
/// Append a component to the name.
|
|
pub fn push(&mut self, name: impl Into<String>) {
|
|
self.inner.push(name.into())
|
|
}
|
|
|
|
/// Returns a copy of this namespace with the second namespace appended.
|
|
/// Both original namespaces stay untouched.
|
|
pub fn extend(&mut self, name: &Namespace) {
|
|
self.inner.extend_from_slice(&name.inner);
|
|
}
|
|
|
|
/// Returns true if the specified namespace is a subset or is equal to this namespace.
|
|
pub fn starts_with(&self, name: &Namespace) -> bool {
|
|
(self.inner.len() >= name.inner.len()) && (name.inner[..] == self.inner[..name.inner.len()])
|
|
}
|
|
|
|
/// Remove the last part of the namespace, returning it or None if namespace was empty.
|
|
pub fn pop(&mut self) -> Option<String> {
|
|
self.inner.pop()
|
|
}
|
|
|
|
/// Returns the number of substrings constituting this namespace.
|
|
pub fn len(&self) -> usize {
|
|
self.inner.len()
|
|
}
|
|
|
|
/// Combine name parts into a string.
|
|
pub fn join(&self, separator: &str) -> String {
|
|
|
|
let mut buf = String::with_capacity(64);
|
|
let mut i = self.inner.iter();
|
|
if let Some(n) = i.next() {
|
|
buf.push_str(n.as_ref());
|
|
} else {
|
|
return "".into()
|
|
}
|
|
for n in i {
|
|
buf.push_str(separator);
|
|
buf.push_str(n.as_ref());
|
|
}
|
|
buf
|
|
}
|
|
}
|
|
|
|
impl WithNamespace for Namespace {
|
|
|
|
fn with_namespace(&self, namespace: &Namespace) -> Self {
|
|
let mut new = self.clone();
|
|
new.extend(namespace);
|
|
new
|
|
}
|
|
}
|
|
|
|
impl From<()> for Namespace {
|
|
fn from(_name: ()) -> Namespace {
|
|
ROOT_NS.clone()
|
|
}
|
|
}
|
|
|
|
impl<'a> From<&'a str> for Namespace {
|
|
fn from(name: &'a str) -> Namespace {
|
|
let mut ns = ROOT_NS.clone();
|
|
if !name.is_empty() {
|
|
ns.push(name)
|
|
}
|
|
ns
|
|
}
|
|
}
|
|
|
|
impl<'a, 'b: 'a> From<&'b [&'a str]> for Namespace {
|
|
fn from(names: &'b [&'a str]) -> Namespace {
|
|
let mut ns = ROOT_NS.clone();
|
|
for name in names {
|
|
if !name.is_empty() {
|
|
ns.push(*name)
|
|
}
|
|
}
|
|
ns
|
|
}
|
|
}
|
|
|
|
impl From<String> for Namespace {
|
|
fn from(name: String) -> Namespace {
|
|
if name.is_empty() {
|
|
ROOT_NS.clone()
|
|
} else {
|
|
ROOT_NS.with_prefix(name.as_ref())
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Common methods of elements that hold a mutable namespace.
|
|
pub trait WithNamespace: Sized {
|
|
|
|
/// Return a copy of this object with the specified namespace appended to the existing one.
|
|
fn with_namespace(&self, namespace: &Namespace) -> Self;
|
|
|
|
/// Join namespace and prepend in newly defined metrics.
|
|
// #[deprecated(since = "0.7.0", note = "Misleading terminology, use with_namespace() instead.")]
|
|
fn with_prefix(&self, name: &str) -> Self {
|
|
self.with_namespace(&name.into())
|
|
}
|
|
|
|
}
|
|
|
|
/// Dynamic metric definition function.
|
|
/// Metrics can be defined from any thread, concurrently (Fn is Sync).
|
|
/// The resulting metrics themselves can be also be safely shared across threads (<M> is Send + Sync).
|
|
/// Concurrent usage of a metric is done using threaded scopes.
|
|
/// Shared concurrent scopes may be provided by some backends (aggregate).
|
|
pub type DefineMetricFn<M> = Arc<Fn(&Namespace, Kind, Sampling) -> M + Send + Sync>;
|
|
|
|
/// A function trait that opens a new metric capture scope.
|
|
pub type OpenScopeFn<M> = Arc<Fn() -> CommandFn<M> + Send + Sync>;
|
|
|
|
/// A function trait that writes to or flushes a certain scope.
|
|
pub type WriteFn = Arc<Fn(Value) + Send + Sync + 'static>;
|
|
|
|
/// A function trait that writes to or flushes a certain scope.
|
|
//#[derive(Clone)]
|
|
pub struct CommandFn<M> {
|
|
inner: Arc<Fn(Command<M>) + Send + Sync + 'static>
|
|
}
|
|
|
|
impl<M> Clone for CommandFn<M> {
|
|
fn clone(&self) -> CommandFn<M> {
|
|
CommandFn {
|
|
inner: self.inner.clone()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An method dispatching command enum to manipulate metric scopes.
|
|
/// Replaces a potential `Writer` trait that would have methods `write` and `flush`.
|
|
/// Using a command pattern allows buffering, async queuing and inline definition of writers.
|
|
pub enum Command<'a, M: 'a> {
|
|
/// Write the value for the metric.
|
|
/// Takes a reference to minimize overhead in single-threaded scenarios.
|
|
Write(&'a M, Value),
|
|
|
|
/// Flush the scope buffer, if applicable.
|
|
Flush,
|
|
}
|
|
|
|
/// Create a new metric scope based on the provided scope function.
|
|
pub fn command_fn<M, F>(scope_fn: F) -> CommandFn<M>
|
|
where
|
|
F: Fn(Command<M>) + Send + Sync + 'static,
|
|
{
|
|
CommandFn {
|
|
inner: Arc::new(scope_fn)
|
|
}
|
|
}
|
|
|
|
impl<M> CommandFn<M> {
|
|
/// Write a value to this scope.
|
|
#[inline]
|
|
pub fn write(&self, metric: &M, value: Value) {
|
|
(self.inner)(Write(metric, value))
|
|
}
|
|
|
|
/// Flush this scope.
|
|
/// Has no effect if scope is unbuffered.
|
|
#[inline]
|
|
pub fn flush(&self) {
|
|
(self.inner)(Flush)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "bench")]
|
|
mod bench {
|
|
|
|
use clock::TimeHandle;
|
|
use test;
|
|
|
|
#[bench]
|
|
fn get_instant(b: &mut test::Bencher) {
|
|
b.iter(|| test::black_box(TimeHandle::now()));
|
|
}
|
|
|
|
} |