diff --git a/examples/observer.rs b/examples/observer.rs index f185701..9f2a40f 100644 --- a/examples/observer.rs +++ b/examples/observer.rs @@ -20,7 +20,6 @@ extern crate dipstick; use std::fs::File; use std::io::{self, BufRead, BufReader}; -use std::sync::Arc; use std::time::{Duration, Instant}; use dipstick::{AtomicBucket, InputScope, MetricValue, Prefixed, ScheduleFlush, Stream}; @@ -34,8 +33,8 @@ fn main() { metrics.set_drain(Stream::to_stderr()); let flush_handle = metrics.flush_every(Duration::from_secs(1)); - metrics.observe("uptime", Arc::new(move || dur2ms(start_time.elapsed()))); - metrics.observe("threads", Arc::new(threads)); + metrics.observe("uptime", move || dur2ms(start_time.elapsed())); + metrics.observe("threads", threads); println!("Press Enter key to exit"); io::stdin().read_line(&mut String::new()).expect("Example, ignored"); diff --git a/src/bucket/atomic.rs b/src/bucket/atomic.rs index f7da83e..2642636 100755 --- a/src/bucket/atomic.rs +++ b/src/bucket/atomic.rs @@ -252,7 +252,7 @@ impl InputScope for AtomicBucket { InputMetric::new(move |value, _labels| scores.update(value)) } - fn observe(&self, name: &str, callback: GaugeCallback) { + fn observe_helper(&self, name: &str, callback: GaugeCallback) { let gauge = self.gauge(name); write_lock!(self.inner) diff --git a/src/core/input.rs b/src/core/input.rs index 4166f99..82f1a5d 100755 --- a/src/core/input.rs +++ b/src/core/input.rs @@ -1,282 +1,290 @@ -use core::clock::TimeHandle; -use core::{MetricValue, Flush}; -use core::name::MetricName; -use core::label::Labels; - -use std::sync::Arc; -use std::fmt; - -// TODO maybe define an 'AsValue' trait + impl for supported number types, then drop 'num' crate -pub use num::{ToPrimitive}; -pub use num::integer; - -/// A function trait that opens a new metric capture scope. -pub trait Input: Send + Sync + 'static + InputDyn { - /// The type of Scope returned byt this input. - type SCOPE: InputScope + Send + Sync + 'static; - - /// Open a new scope from this input. - fn metrics(&self) -> Self::SCOPE; - - /// Open a new scope from this input. - #[deprecated(since="0.7.2", note="Use metrics()")] - fn input(&self) -> Self::SCOPE { - self.metrics() - } -} - -/// A function trait that opens a new metric capture scope. -pub trait InputDyn: Send + Sync + 'static { - /// Open a new scope from this output. - fn input_dyn(&self) -> Arc; -} - -/// Blanket impl of dyn input trait -impl InputDyn for T { - fn input_dyn(&self) -> Arc { - Arc::new(self.metrics()) - } -} - -/// InputScope -/// Define metrics, write values and flush them. -pub trait InputScope: Flush { - /// Define a generic metric of the specified type. - /// It is preferable to use counter() / marker() / timer() / gauge() methods. - fn new_metric(&self, name: MetricName, kind: InputKind) -> InputMetric; - - /// Define a Counter. - fn counter(&self, name: &str) -> Counter { - self.new_metric(name.into(), InputKind::Counter).into() - } - - /// Define a Marker. - fn marker(&self, name: &str) -> Marker { - self.new_metric(name.into(), InputKind::Marker).into() - } - - /// Define a Timer. - fn timer(&self, name: &str) -> Timer { - self.new_metric(name.into(), InputKind::Timer).into() - } - - /// Define a Gauge. - fn gauge(&self, name: &str) -> Gauge { - self.new_metric(name.into(), InputKind::Gauge).into() - } - - /// Define a Level. - fn level(&self, name: &str) -> Level { - self.new_metric(name.into(), InputKind::Level).into() - } - - /// Observe a gauge value using a callback function. - fn observe(&self, _name: &str, _callback: GaugeCallback) { - // TODO: Not yet finished, remove default impl. - } -} - -/// A metric is actually a function that knows to write a metric value to a metric output. -#[derive(Clone)] -pub struct InputMetric { - inner: Arc -} - -impl fmt::Debug for InputMetric { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "InputMetric") - } -} - -impl InputMetric { - /// Utility constructor - pub fn new(metric: F) -> InputMetric { - InputMetric { inner: Arc::new(metric) } - } - - /// Collect a new value for this metric. - #[inline] - pub fn write(&self, value: MetricValue, labels: Labels) { - (self.inner)(value, labels) - } -} - -/// Used to differentiate between metric kinds in the backend. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum InputKind { - /// Handling one item at a time. - Marker, - /// Handling cumulative observed quantities. - Counter, - /// Handling quantity fluctuations. - Level, - /// Reporting instant measurement of a resource at a point in time (non-cumulative). - Gauge, - /// Measuring a time interval, internal to the app or provided by an external source. - Timer, -} - -/// Used by the metrics! macro to obtain the InputKind from the stringified type. -impl<'a> From<&'a str> for InputKind { - fn from(s: &str) -> InputKind { - match s { - "Marker" => InputKind::Marker, - "Counter" => InputKind::Counter, - "Gauge" => InputKind::Gauge, - "Timer" => InputKind::Timer, - "Level" => InputKind::Level, - _ => panic!("No InputKind '{}' defined", s) - } - } -} - -/// A monotonic counter metric. -/// Since value is only ever increased by one, no value parameter is provided, -/// preventing programming errors. -#[derive(Debug, Clone)] -pub struct Marker { - inner: InputMetric, -} - -impl Marker { - /// Record a single event occurence. - pub fn mark(&self) { - self.inner.write(1, labels![]) - } -} - -/// A counter of absolute observed values (non-negative amounts). -/// Used to count things that cannot be undone: -/// - Bytes sent -/// - Records written -/// - Apples eaten -/// For relative (possibly negative) values, the `Level` counter type can be used. -/// If aggregated, minimum and maximum scores will track the collected values, not their sum. -#[derive(Debug, Clone)] -pub struct Counter { - inner: InputMetric, -} - -impl Counter { - /// Record a value count. - pub fn count(&self, count: usize) { - self.inner.write(count as isize, labels![]) - } -} - -/// A counter of fluctuating resources accepting positive and negative values. -/// Can be used as a stateful `Gauge` or as a `Counter` of possibly decreasing amounts. -/// - Size of messages in a queue -/// - Strawberries on a conveyor belt -/// If aggregated, minimum and maximum scores will track the sum of values, not the collected values themselves. -#[derive(Debug, Clone)] -pub struct Level { - inner: InputMetric, -} - -impl Level { - /// Record a positive or negative value count - pub fn adjust(&self, count: V) { - self.inner.write(count.to_isize().unwrap(), labels![]) - } -} - -/// A gauge that sends values to the metrics backend -#[derive(Debug, Clone)] -pub struct Gauge { - inner: InputMetric, -} - -impl Gauge { - /// Record a value point for this gauge. - pub fn value(&self, value: V) { - self.inner.write(value.to_isize().unwrap(), labels![]) - } -} - -/// Callback function for gauge observer. -pub type GaugeCallback = Arc MetricValue + Send + Sync>; - -/// Gauge and it's observer callback. -#[derive(Clone)] -pub(crate) struct GaugeObserver { - pub(crate) gauge: Gauge, - pub(crate) callback: GaugeCallback -} - -/// A timer that sends values to the metrics backend -/// Timers can record time intervals in multiple ways : -/// - with the time! macrohich wraps an expression or block with start() and stop() calls. -/// - with the time(Fn) methodhich wraps a closure with start() and stop() calls. -/// - with start() and stop() methodsrapping around the operation to time -/// - with the interval_us() method, providing an externally determined microsecond interval -#[derive(Debug, Clone)] -pub struct Timer { - inner: InputMetric, -} - -impl Timer { - /// Record a microsecond interval for this timer - /// Can be used in place of start()/stop() if an external time interval source is used - pub fn interval_us(&self, interval_us: u64) -> u64 { - self.inner.write(interval_us as isize, labels![]); - interval_us - } - - /// Obtain a opaque handle to the current time. - /// The handle is passed back to the stop() method to record a time interval. - /// This is actually a convenience method to the TimeHandle::now() - /// Beware, handles obtained here are not bound to this specific timer instance - /// _for now_ but might be in the future for safety. - /// If you require safe multi-timer handles, get them through TimeType::now() - pub fn start(&self) -> TimeHandle { - TimeHandle::now() - } - - /// Record the time elapsed since the start_time handle was obtained. - /// This call can be performed multiple times using the same handle, - /// reporting distinct time intervals each time. - /// Returns the microsecond interval value that was recorded. - pub fn stop(&self, start_time: TimeHandle) -> MetricValue { - let elapsed_us = start_time.elapsed_us(); - self.interval_us(elapsed_us) as isize - } - - /// Record the time taken to execute the provided closure - pub fn time R, R>(&self, operations: F) -> R { - let start_time = self.start(); - let value: R = operations(); - self.stop(start_time); - value - } -} - -impl From for Gauge { - fn from(metric: InputMetric) -> Gauge { - Gauge { inner: metric } - } -} - -impl From for Timer { - fn from(metric: InputMetric) -> Timer { - Timer { inner: metric } - } -} - -impl From for Counter { - fn from(metric: InputMetric) -> Counter { - Counter { inner: metric } - } -} - -impl From for Marker { - fn from(metric: InputMetric) -> Marker { - Marker { inner: metric } - } -} - -impl From for Level { - fn from(metric: InputMetric) -> Level { - Level { inner: metric } - } -} +use core::clock::TimeHandle; +use core::{MetricValue, Flush}; +use core::name::MetricName; +use core::label::Labels; + +use std::sync::Arc; +use std::fmt; + +// TODO maybe define an 'AsValue' trait + impl for supported number types, then drop 'num' crate +pub use num::{ToPrimitive}; +pub use num::integer; + +/// A function trait that opens a new metric capture scope. +pub trait Input: Send + Sync + 'static + InputDyn { + /// The type of Scope returned byt this input. + type SCOPE: InputScope + Send + Sync + 'static; + + /// Open a new scope from this input. + fn metrics(&self) -> Self::SCOPE; + + /// Open a new scope from this input. + #[deprecated(since="0.7.2", note="Use metrics()")] + fn input(&self) -> Self::SCOPE { + self.metrics() + } +} + +/// A function trait that opens a new metric capture scope. +pub trait InputDyn: Send + Sync + 'static { + /// Open a new scope from this output. + fn input_dyn(&self) -> Arc; +} + +/// Blanket impl of dyn input trait +impl InputDyn for T { + fn input_dyn(&self) -> Arc { + Arc::new(self.metrics()) + } +} + +/// InputScope +/// Define metrics, write values and flush them. +pub trait InputScope: Flush { + /// Define a generic metric of the specified type. + /// It is preferable to use counter() / marker() / timer() / gauge() methods. + fn new_metric(&self, name: MetricName, kind: InputKind) -> InputMetric; + + /// Define a Counter. + fn counter(&self, name: &str) -> Counter { + self.new_metric(name.into(), InputKind::Counter).into() + } + + /// Define a Marker. + fn marker(&self, name: &str) -> Marker { + self.new_metric(name.into(), InputKind::Marker).into() + } + + /// Define a Timer. + fn timer(&self, name: &str) -> Timer { + self.new_metric(name.into(), InputKind::Timer).into() + } + + /// Define a Gauge. + fn gauge(&self, name: &str) -> Gauge { + self.new_metric(name.into(), InputKind::Gauge).into() + } + + /// Define a Level. + fn level(&self, name: &str) -> Level { + self.new_metric(name.into(), InputKind::Level).into() + } + + /// Observe a gauge value using a callback function. If multiple callbacks are registered under + /// the same conflicting key, only the last one will survive. + fn observe MetricValue + Send + Sync + 'static>(&self, name: &str, callback: F) where Self: Sized { + self.observe_helper(name, Arc::new(callback)); + } + + /// Helper method to make use of `observe()` more pleasant. The Arc wrapper is not necessary + /// in the client code now. Consider this as an internal method. + fn observe_helper(&self, _name: &str, _callback: GaugeCallback) { + // TODO: Not yet finished, remove default impl. + } + +} + +/// A metric is actually a function that knows to write a metric value to a metric output. +#[derive(Clone)] +pub struct InputMetric { + inner: Arc +} + +impl fmt::Debug for InputMetric { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "InputMetric") + } +} + +impl InputMetric { + /// Utility constructor + pub fn new(metric: F) -> InputMetric { + InputMetric { inner: Arc::new(metric) } + } + + /// Collect a new value for this metric. + #[inline] + pub fn write(&self, value: MetricValue, labels: Labels) { + (self.inner)(value, labels) + } +} + +/// Used to differentiate between metric kinds in the backend. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum InputKind { + /// Handling one item at a time. + Marker, + /// Handling cumulative observed quantities. + Counter, + /// Handling quantity fluctuations. + Level, + /// Reporting instant measurement of a resource at a point in time (non-cumulative). + Gauge, + /// Measuring a time interval, internal to the app or provided by an external source. + Timer, +} + +/// Used by the metrics! macro to obtain the InputKind from the stringified type. +impl<'a> From<&'a str> for InputKind { + fn from(s: &str) -> InputKind { + match s { + "Marker" => InputKind::Marker, + "Counter" => InputKind::Counter, + "Gauge" => InputKind::Gauge, + "Timer" => InputKind::Timer, + "Level" => InputKind::Level, + _ => panic!("No InputKind '{}' defined", s) + } + } +} + +/// A monotonic counter metric. +/// Since value is only ever increased by one, no value parameter is provided, +/// preventing programming errors. +#[derive(Debug, Clone)] +pub struct Marker { + inner: InputMetric, +} + +impl Marker { + /// Record a single event occurence. + pub fn mark(&self) { + self.inner.write(1, labels![]) + } +} + +/// A counter of absolute observed values (non-negative amounts). +/// Used to count things that cannot be undone: +/// - Bytes sent +/// - Records written +/// - Apples eaten +/// For relative (possibly negative) values, the `Level` counter type can be used. +/// If aggregated, minimum and maximum scores will track the collected values, not their sum. +#[derive(Debug, Clone)] +pub struct Counter { + inner: InputMetric, +} + +impl Counter { + /// Record a value count. + pub fn count(&self, count: usize) { + self.inner.write(count as isize, labels![]) + } +} + +/// A counter of fluctuating resources accepting positive and negative values. +/// Can be used as a stateful `Gauge` or as a `Counter` of possibly decreasing amounts. +/// - Size of messages in a queue +/// - Strawberries on a conveyor belt +/// If aggregated, minimum and maximum scores will track the sum of values, not the collected values themselves. +#[derive(Debug, Clone)] +pub struct Level { + inner: InputMetric, +} + +impl Level { + /// Record a positive or negative value count + pub fn adjust(&self, count: V) { + self.inner.write(count.to_isize().unwrap(), labels![]) + } +} + +/// A gauge that sends values to the metrics backend +#[derive(Debug, Clone)] +pub struct Gauge { + inner: InputMetric, +} + +impl Gauge { + /// Record a value point for this gauge. + pub fn value(&self, value: V) { + self.inner.write(value.to_isize().unwrap(), labels![]) + } +} + +/// Callback function for gauge observer. +pub type GaugeCallback = Arc MetricValue + Send + Sync>; + +/// Gauge and it's observer callback. +#[derive(Clone)] +pub(crate) struct GaugeObserver { + pub(crate) gauge: Gauge, + pub(crate) callback: GaugeCallback +} + +/// A timer that sends values to the metrics backend +/// Timers can record time intervals in multiple ways : +/// - with the time! macrohich wraps an expression or block with start() and stop() calls. +/// - with the time(Fn) methodhich wraps a closure with start() and stop() calls. +/// - with start() and stop() methodsrapping around the operation to time +/// - with the interval_us() method, providing an externally determined microsecond interval +#[derive(Debug, Clone)] +pub struct Timer { + inner: InputMetric, +} + +impl Timer { + /// Record a microsecond interval for this timer + /// Can be used in place of start()/stop() if an external time interval source is used + pub fn interval_us(&self, interval_us: u64) -> u64 { + self.inner.write(interval_us as isize, labels![]); + interval_us + } + + /// Obtain a opaque handle to the current time. + /// The handle is passed back to the stop() method to record a time interval. + /// This is actually a convenience method to the TimeHandle::now() + /// Beware, handles obtained here are not bound to this specific timer instance + /// _for now_ but might be in the future for safety. + /// If you require safe multi-timer handles, get them through TimeType::now() + pub fn start(&self) -> TimeHandle { + TimeHandle::now() + } + + /// Record the time elapsed since the start_time handle was obtained. + /// This call can be performed multiple times using the same handle, + /// reporting distinct time intervals each time. + /// Returns the microsecond interval value that was recorded. + pub fn stop(&self, start_time: TimeHandle) -> MetricValue { + let elapsed_us = start_time.elapsed_us(); + self.interval_us(elapsed_us) as isize + } + + /// Record the time taken to execute the provided closure + pub fn time R, R>(&self, operations: F) -> R { + let start_time = self.start(); + let value: R = operations(); + self.stop(start_time); + value + } +} + +impl From for Gauge { + fn from(metric: InputMetric) -> Gauge { + Gauge { inner: metric } + } +} + +impl From for Timer { + fn from(metric: InputMetric) -> Timer { + Timer { inner: metric } + } +} + +impl From for Counter { + fn from(metric: InputMetric) -> Counter { + Counter { inner: metric } + } +} + +impl From for Marker { + fn from(metric: InputMetric) -> Marker { + Marker { inner: metric } + } +} + +impl From for Level { + fn from(metric: InputMetric) -> Level { + Level { inner: metric } + } +}