mirror of https://github.com/fralalonde/dipstick
Registry dissolved
This commit is contained in:
parent
2681c7b342
commit
d055270d84
|
@ -4,4 +4,6 @@
|
|||
|
||||
- Add `Delegate` mechanism to allow runtime (re)configuration of metrics
|
||||
- Enhance macros to allow metrics of different types within a single block
|
||||
- Additional pre-typed 'delegate' and 'aggregate' macros
|
||||
|
||||
|
||||
|
|
22
README.md
22
README.md
|
@ -57,7 +57,8 @@ Aggregate metrics and schedule to be periodical publication in the background:
|
|||
```rust,skt-run
|
||||
use std::time::Duration;
|
||||
|
||||
let app_metrics = metrics(aggregate(all_stats, to_stdout()));
|
||||
let app_metrics = metrics(aggregate());
|
||||
route_aggregate_metrics(to_stdout());
|
||||
app_metrics.flush_every(Duration::from_secs(3));
|
||||
```
|
||||
|
||||
|
@ -68,14 +69,13 @@ Published statistics can be selected with presets such as `all_stats` (see previ
|
|||
|
||||
For more control over published statistics, provide your own strategy:
|
||||
```rust,skt-run
|
||||
metrics(aggregate(
|
||||
|_kind, name, score|
|
||||
match score {
|
||||
ScoreType::Count(count) =>
|
||||
Some((Kind::Counter, vec![name, ".per_thousand"], count / 1000)),
|
||||
_ => None
|
||||
},
|
||||
to_log()));
|
||||
metrics(aggregate());
|
||||
set_default_aggregate_fn(|_kind, name, score|
|
||||
match score {
|
||||
ScoreType::Count(count) =>
|
||||
Some((Kind::Counter, vec![name, ".per_thousand"], count / 1000)),
|
||||
_ => None
|
||||
});
|
||||
```
|
||||
|
||||
Apply statistical sampling to metrics:
|
||||
|
@ -98,12 +98,12 @@ For speed and easier maintenance, metrics are usually defined statically:
|
|||
#[macro_use] extern crate lazy_static;
|
||||
use dipstick::*;
|
||||
|
||||
metrics!("my_app" => {
|
||||
delegate_metrics!("my_app" => {
|
||||
@Counter COUNTER_A: "counter_a";
|
||||
});
|
||||
|
||||
fn main() {
|
||||
send_metrics(to_stdout());
|
||||
route_aggregate_metrics(to_stdout());
|
||||
COUNTER_A.count(11);
|
||||
}
|
||||
```
|
||||
|
|
|
@ -7,10 +7,12 @@ use std::time::Duration;
|
|||
use dipstick::*;
|
||||
|
||||
fn main() {
|
||||
let to_aggregate = aggregate(all_stats, to_stdout());
|
||||
let to_aggregate = aggregate();
|
||||
|
||||
let app_metrics = metrics(to_aggregate);
|
||||
|
||||
route_aggregate_metrics(to_stdout());
|
||||
|
||||
app_metrics.flush_every(Duration::from_secs(3));
|
||||
|
||||
let counter = app_metrics.counter("counter_a");
|
||||
|
|
|
@ -35,7 +35,9 @@ fn main() {
|
|||
}
|
||||
|
||||
// send application metrics to aggregator
|
||||
let to_aggregate = aggregate(custom_statistics, to_stdout());
|
||||
let to_aggregate = aggregate();
|
||||
|
||||
route_aggregate_metrics(to_stdout());
|
||||
|
||||
let app_metrics = metrics(to_aggregate);
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
//! A sample application sending ad-hoc counter values both to statsd _and_ to stdout.
|
||||
|
||||
extern crate dipstick;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use dipstick::*;
|
||||
use std::time::Duration;
|
||||
|
||||
// undeclared root (un-prefixed) metrics
|
||||
aggregate_metrics!(() => {
|
||||
// create counter "some_counter"
|
||||
pub @Counter ROOT_COUNTER: "root_counter";
|
||||
// create counter "root_counter"
|
||||
pub @Gauge ROOT_GAUGE: "root_gauge";
|
||||
// create counter "root_timer"
|
||||
pub @Timer ROOT_TIMER: "root_timer";
|
||||
});
|
||||
|
||||
// public source
|
||||
aggregate_metrics!(pub PUB_METRICS ="pub_lib_prefix" => {
|
||||
// create counter "lib_prefix.some_counter"
|
||||
pub @Counter PUB_COUNTER: "some_counter";
|
||||
});
|
||||
|
||||
// undeclared (private) prefixed metrics
|
||||
//app_metrics!("closed_lib_prefix" => {
|
||||
// // create counter "lib_prefix.some_counter"
|
||||
// pub @Counter MY_COUNTER: "some_counter";
|
||||
//});
|
||||
|
||||
// declare mod source
|
||||
aggregate_metrics!(LIB_METRICS ="mod_lib_prefix" => {
|
||||
// create counter "mod_lib_prefix.some_counter"
|
||||
pub @Counter SOME_COUNTER: "some_counter";
|
||||
});
|
||||
|
||||
// reuse declared source
|
||||
aggregate_metrics!(LIB_METRICS => {
|
||||
// create counter "mod_lib_prefix.another_counter"
|
||||
@Counter ANOTHER_COUNTER: "another_counter";
|
||||
});
|
||||
|
||||
fn main() {
|
||||
route_aggregate_metrics(to_stdout());
|
||||
|
||||
loop {
|
||||
PUB_COUNTER.count(978);
|
||||
ROOT_COUNTER.count(123);
|
||||
ANOTHER_COUNTER.count(456);
|
||||
ROOT_TIMER.interval_us(2000000);
|
||||
ROOT_GAUGE.value(34534);
|
||||
|
||||
PUB_METRICS.flush();
|
||||
std::thread::sleep(Duration::from_millis(40));
|
||||
}
|
||||
}
|
|
@ -8,17 +8,17 @@ use dipstick::*;
|
|||
use std::time::Duration;
|
||||
|
||||
// undeclared root (un-prefixed) metrics
|
||||
metrics!( => {
|
||||
delegate_metrics! { () => {
|
||||
// create counter "some_counter"
|
||||
pub @Counter ROOT_COUNTER: "root_counter";
|
||||
// create counter "root_counter"
|
||||
pub @Gauge ROOT_GAUGE: "root_gauge";
|
||||
// create counter "root_timer"
|
||||
pub @Timer ROOT_TIMER: "root_timer";
|
||||
});
|
||||
}}
|
||||
|
||||
// public source
|
||||
metrics!(pub PUB_METRICS ="pub_lib_prefix" => {
|
||||
delegate_metrics!(pub PUB_METRICS ="pub_lib_prefix" => {
|
||||
// create counter "lib_prefix.some_counter"
|
||||
pub @Counter PUB_COUNTER: "some_counter";
|
||||
});
|
||||
|
@ -30,19 +30,19 @@ metrics!(pub PUB_METRICS ="pub_lib_prefix" => {
|
|||
//});
|
||||
|
||||
// declare mod source
|
||||
metrics!(LIB_METRICS ="mod_lib_prefix" => {
|
||||
delegate_metrics!(LIB_METRICS ="mod_lib_prefix" => {
|
||||
// create counter "mod_lib_prefix.some_counter"
|
||||
pub @Counter SOME_COUNTER: "some_counter";
|
||||
});
|
||||
|
||||
// reuse declared source
|
||||
metrics!(LIB_METRICS => {
|
||||
delegate_metrics!(LIB_METRICS => {
|
||||
// create counter "mod_lib_prefix.another_counter"
|
||||
@Counter ANOTHER_COUNTER: "another_counter";
|
||||
});
|
||||
|
||||
fn main() {
|
||||
send_metrics(to_stdout());
|
||||
set_default_metric_scope(to_stdout());
|
||||
|
||||
loop {
|
||||
ROOT_COUNTER.count(123);
|
||||
|
|
|
@ -11,16 +11,16 @@ metrics!(<(Statsd, String)> DIFFERENT_TYPES = (
|
|||
// combine outputs of different types by using a tuple
|
||||
to_statsd("localhost:8125").expect("Connecting"),
|
||||
to_stdout(),
|
||||
););
|
||||
));
|
||||
|
||||
metrics!(<Vec<String>> SAME_TYPE = [
|
||||
// combine multiple outputs of the same type by using an array
|
||||
to_stdout().with_prefix("yeah"),
|
||||
to_stdout().with_prefix("ouch"),
|
||||
to_stdout().with_sampling_rate(0.5),
|
||||
][..];);
|
||||
][..]);
|
||||
|
||||
metrics!(<Vec<String>> MUTANT_CHILD = SAME_TYPE.with_prefix("super").with_prefix("duper"););
|
||||
metrics!(<Vec<String>> MUTANT_CHILD = SAME_TYPE.with_prefix("super").with_prefix("duper"));
|
||||
|
||||
fn main() {
|
||||
loop {
|
||||
|
|
|
@ -7,7 +7,7 @@ use dipstick::*;
|
|||
|
||||
fn main() {
|
||||
// print only 1 out of every 10000 metrics recorded
|
||||
let app_metrics: Metrics<String> = metrics(to_stdout().with_sampling_rate(0.0001));
|
||||
let app_metrics: MetricScope<String> = metrics(to_stdout().with_sampling_rate(0.0001));
|
||||
|
||||
let marker = app_metrics.marker("marker_a");
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@ use std::time::Duration;
|
|||
use dipstick::*;
|
||||
|
||||
fn main() {
|
||||
let to_aggregate = aggregate(summary, to_stdout());
|
||||
let to_aggregate = aggregate();
|
||||
|
||||
route_aggregate_metrics(to_stdout());
|
||||
|
||||
let app_metrics = metrics(to_aggregate);
|
||||
|
||||
|
|
185
src/aggregate.rs
185
src/aggregate.rs
|
@ -1,42 +1,69 @@
|
|||
//! Maintain aggregated metrics for deferred reporting,
|
||||
//!
|
||||
use core::*;
|
||||
use context::*;
|
||||
use metrics::*;
|
||||
use namespace::*;
|
||||
use output::to_void;
|
||||
use core::{Value, Kind, control_scope, ScopeCmd, Sampling};
|
||||
use core::Kind::*;
|
||||
use context::DEFAULT_CONTEXT;
|
||||
use metrics::MetricScope;
|
||||
use namespace::WithNamespace;
|
||||
|
||||
use scores::*;
|
||||
use publish::*;
|
||||
use scores::{ScoreSnapshot, ScoreType, Scoreboard};
|
||||
use scores::ScoreType::*;
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// Define aggregate metrics.
|
||||
#[macro_export]
|
||||
macro_rules! aggregate_metrics {
|
||||
(pub $METRIC_ID:ident = $e:expr $(;)*) => { metrics! {<Aggregate> pub $METRIC_ID = $e } };
|
||||
(pub $METRIC_ID:ident = $e:expr => { $($REMAINING:tt)+ }) => { metrics! {<Aggregate> pub $METRIC_ID = $e => { $($REMAINING)* } } };
|
||||
($METRIC_ID:ident = $e:expr $(;)*) => { metrics! {<Aggregate> $METRIC_ID = $e } };
|
||||
($METRIC_ID:ident = $e:expr => { $($REMAINING:tt)+ }) => { metrics! {<Aggregate> $METRIC_ID = $e => { $($REMAINING)* } } };
|
||||
($METRIC_ID:ident => { $($REMAINING:tt)+ }) => { metrics! {<Aggregate> $METRIC_ID => { $($REMAINING)* } } };
|
||||
($e:expr => { $($REMAINING:tt)+ }) => { metrics! {<Aggregate> $e => { $($REMAINING)* } } };
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref DEFAULT_PUBLISH_FN: RwLock<Arc<Fn(Kind, &str, ScoreType) ->
|
||||
Option<(Kind, Vec<&str>, Value)> + Send + Sync + 'static>> =
|
||||
RwLock::new(Arc::new(summary));
|
||||
|
||||
static ref AGGREGATE_REGISTRY: RwLock<Vec<Aggregator>> = RwLock::new(vec![]);
|
||||
}
|
||||
|
||||
pub fn set_default_aggregate_fn<F>(func: F)
|
||||
where
|
||||
F: Fn(Kind, &str, ScoreType) -> Option<(Kind, Vec<&str>, Value)> + Send + Sync + 'static
|
||||
{
|
||||
*DEFAULT_PUBLISH_FN.write().unwrap() = Arc::new(func)
|
||||
}
|
||||
|
||||
/// Get the default metrics summary.
|
||||
pub fn get_default_publish_fn() -> Arc<Fn(Kind, &str, ScoreType) ->
|
||||
Option<(Kind, Vec<&str>, Value)> + Send + Sync + 'static> {
|
||||
DEFAULT_PUBLISH_FN.read().unwrap().clone()
|
||||
}
|
||||
|
||||
|
||||
/// Aggregate metrics in memory.
|
||||
/// Depending on the type of metric, count, sum, minimum and maximum of values will be tracked.
|
||||
/// Needs to be connected to a publish to be useful.
|
||||
/// ```
|
||||
/// use dipstick::*;
|
||||
/// use dipstick;
|
||||
/// let metrics: AppMetrics<_> = aggregate(summary, to_stdout()).into();
|
||||
/// metrics.marker("my_event").mark();
|
||||
/// metrics.marker("my_event").mark();
|
||||
/// ```
|
||||
pub fn aggregate<E, M>(stat_fn: E, to_chain: MetricContext<M>) -> Aggregator
|
||||
where
|
||||
E: Fn(Kind, &str, ScoreType) -> Option<(Kind, Vec<&str>, Value)> + Send + Sync + 'static,
|
||||
M: Clone + Send + Sync + Debug + 'static,
|
||||
{
|
||||
pub fn aggregate() -> Aggregator {
|
||||
Aggregator {
|
||||
metrics: Arc::new(RwLock::new(HashMap::new())),
|
||||
publish: Arc::new(Publisher::new(stat_fn, to_chain)),
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Aggregator> for Metrics<Aggregate> {
|
||||
fn from(agg: Aggregator) -> Metrics<Aggregate> {
|
||||
impl From<Aggregator> for MetricScope<Aggregate> {
|
||||
fn from(agg: Aggregator) -> MetricScope<Aggregate> {
|
||||
let agg_1 = agg.clone();
|
||||
Metrics::new(
|
||||
MetricScope::new(
|
||||
Arc::new(move |kind, name, rate| agg.define_metric(kind, name, rate)),
|
||||
control_scope(move |cmd| match cmd {
|
||||
ScopeCmd::Write(metric, value) => {
|
||||
|
@ -44,15 +71,25 @@ impl From<Aggregator> for Metrics<Aggregate> {
|
|||
metric.update(value)
|
||||
}
|
||||
ScopeCmd::Flush => agg_1.flush(),
|
||||
}),
|
||||
)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Metrics<Aggregate> {
|
||||
fn from(prefix: &'static str) -> Metrics<Aggregate> {
|
||||
let app_metrics: Metrics<Aggregate> = aggregate(summary, to_void()).into();
|
||||
app_metrics.with_prefix(prefix)
|
||||
impl From<&'static str> for MetricScope<Aggregate> {
|
||||
fn from(prefix: &'static str) -> MetricScope<Aggregate> {
|
||||
let app_metrics: MetricScope<Aggregate> = aggregate().into();
|
||||
if !prefix.is_empty() {
|
||||
app_metrics.with_prefix(prefix)
|
||||
} else {
|
||||
app_metrics
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for MetricScope<Aggregate> {
|
||||
fn from(_: ()) -> MetricScope<Aggregate> {
|
||||
let scope: MetricScope<Aggregate> = aggregate().into();
|
||||
scope
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,15 +98,13 @@ impl From<&'static str> for Metrics<Aggregate> {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Aggregator {
|
||||
metrics: Arc<RwLock<HashMap<String, Arc<Scoreboard>>>>,
|
||||
publish: Arc<Publish>,
|
||||
}
|
||||
|
||||
impl Aggregator {
|
||||
/// Build a new metric aggregation point with specified initial capacity of metrics to aggregate.
|
||||
pub fn with_capacity(size: usize, publish: Arc<Publish>) -> Aggregator {
|
||||
pub fn with_capacity(size: usize) -> Aggregator {
|
||||
Aggregator {
|
||||
metrics: Arc::new(RwLock::new(HashMap::with_capacity(size))),
|
||||
publish: publish.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +125,7 @@ impl Aggregator {
|
|||
}
|
||||
|
||||
/// Lookup or create a scoreboard for the requested metric.
|
||||
pub fn define_metric(&self, kind: Kind, name: &str, _rate: Rate) -> Aggregate {
|
||||
pub fn define_metric(&self, kind: Kind, name: &str, _rate: Sampling) -> Aggregate {
|
||||
self.metrics
|
||||
.write()
|
||||
.expect("Locking aggregator")
|
||||
|
@ -102,46 +137,122 @@ impl Aggregator {
|
|||
/// Collect and reset aggregated data.
|
||||
/// Publish statistics
|
||||
pub fn flush(&self) {
|
||||
let metrics = self.metrics.read().expect("Locking metrics scoreboards");
|
||||
let snapshot = metrics.values().flat_map(|score| score.reset()).collect();
|
||||
self.publish.publish(snapshot);
|
||||
let snapshot: Vec<ScoreSnapshot> = {
|
||||
let metrics = self.metrics.read().expect("Locking metrics scoreboards");
|
||||
metrics.values().flat_map(|score| score.reset()).collect()
|
||||
};
|
||||
|
||||
let publish_scope = DEFAULT_CONTEXT.read().unwrap().open_scope();
|
||||
if snapshot.is_empty() {
|
||||
// no data was collected for this period
|
||||
// TODO repeat previous frame min/max ?
|
||||
// TODO update some canary metric ?
|
||||
} else {
|
||||
for metric in snapshot {
|
||||
for score in metric.2 {
|
||||
if let Some(ex) = (get_default_publish_fn())(metric.0, metric.1.as_ref(), score) {
|
||||
publish_scope.define_metric(ex.0, &ex.1.concat(), 1.0).write(ex.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO parameterize whether to keep ad-hoc metrics after publish
|
||||
// source.cleanup();
|
||||
publish_scope.flush()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// The type of metric created by the Aggregator.
|
||||
pub type Aggregate = Arc<Scoreboard>;
|
||||
|
||||
/// A predefined export strategy reporting all aggregated stats for all metric types.
|
||||
/// Resulting stats are named by appending a short suffix to each metric's name.
|
||||
pub fn all_stats(kind: Kind, name: &str, score: ScoreType) -> Option<(Kind, Vec<&str>, Value)> {
|
||||
match score {
|
||||
Count(hit) => Some((Counter, vec![name, ".count"], hit)),
|
||||
Sum(sum) => Some((kind, vec![name, ".sum"], sum)),
|
||||
Mean(mean) => Some((kind, vec![name, ".mean"], mean.round() as Value)),
|
||||
Max(max) => Some((Gauge, vec![name, ".max"], max)),
|
||||
Min(min) => Some((Gauge, vec![name, ".min"], min)),
|
||||
Rate(rate) => Some((Gauge, vec![name, ".rate"], rate.round() as Value)),
|
||||
}
|
||||
}
|
||||
|
||||
/// A predefined export strategy reporting the average value for every non-marker metric.
|
||||
/// Marker metrics export their hit count instead.
|
||||
/// Since there is only one stat per metric, there is no risk of collision
|
||||
/// and so exported stats copy their metric's name.
|
||||
pub fn average(kind: Kind, name: &str, score: ScoreType) -> Option<(Kind, Vec<&str>, Value)> {
|
||||
match kind {
|
||||
Marker => match score {
|
||||
Count(count) => Some((Counter, vec![name], count)),
|
||||
_ => None,
|
||||
},
|
||||
_ => match score {
|
||||
Mean(avg) => Some((Gauge, vec![name], avg.round() as Value)),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// A predefined single-stat-per-metric export strategy:
|
||||
/// - Timers and Counters each export their sums
|
||||
/// - Markers each export their hit count
|
||||
/// - Gauges each export their average
|
||||
/// Since there is only one stat per metric, there is no risk of collision
|
||||
/// and so exported stats copy their metric's name.
|
||||
pub fn summary(kind: Kind, name: &str, score: ScoreType) -> Option<(Kind, Vec<&str>, Value)> {
|
||||
match kind {
|
||||
Marker => match score {
|
||||
Count(count) => Some((Counter, vec![name], count)),
|
||||
_ => None,
|
||||
},
|
||||
Counter | Timer => match score {
|
||||
Sum(sum) => Some((kind, vec![name], sum)),
|
||||
_ => None,
|
||||
},
|
||||
Gauge => match score {
|
||||
Mean(mean) => Some((Gauge, vec![name], mean.round() as Value)),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
mod bench {
|
||||
|
||||
use super::*;
|
||||
use test;
|
||||
use core::Kind::*;
|
||||
use core::Kind::{Marker, Counter};
|
||||
use metrics::MetricScope;
|
||||
use aggregate::{Aggregate, aggregate};
|
||||
|
||||
#[bench]
|
||||
fn aggregate_marker(b: &mut test::Bencher) {
|
||||
let sink: Metrics<Aggregate> = aggregate(summary, to_void()).into();
|
||||
let sink: MetricScope<Aggregate> = aggregate().into();
|
||||
let metric = sink.define_metric(Marker, "event_a", 1.0);
|
||||
b.iter(|| test::black_box(sink.write(&metric, 1)));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn aggregate_counter(b: &mut test::Bencher) {
|
||||
let sink: Metrics<Aggregate> = aggregate(summary, to_void()).into();
|
||||
let sink: MetricScope<Aggregate> = aggregate().into();
|
||||
let metric = sink.define_metric(Counter, "count_a", 1.0);
|
||||
b.iter(|| test::black_box(sink.write(&metric, 1)));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn reset_marker(b: &mut test::Bencher) {
|
||||
let sink: Metrics<Aggregate> = aggregate(summary, to_void()).into();
|
||||
let sink: MetricScope<Aggregate> = aggregate().into();
|
||||
let metric = sink.define_metric(Marker, "marker_a", 1.0);
|
||||
b.iter(|| test::black_box(metric.reset()));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn reset_counter(b: &mut test::Bencher) {
|
||||
let sink: Metrics<Aggregate> = aggregate(summary, to_void()).into();
|
||||
let sink: MetricScope<Aggregate> = aggregate().into();
|
||||
let metric = sink.define_metric(Counter, "count_a", 1.0);
|
||||
b.iter(|| test::black_box(metric.reset()));
|
||||
}
|
||||
|
|
|
@ -1,11 +1,31 @@
|
|||
//! Chain of command for unscoped metrics.
|
||||
|
||||
use core::*;
|
||||
use metrics::Metrics;
|
||||
use metrics::MetricScope;
|
||||
|
||||
use std::sync::Arc;
|
||||
use namespace::{WithNamespace, add_namespace, Namespace};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use metrics::DefineMetric;
|
||||
use output;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref NO_RECV_CONTEXT: Arc<OpenScope + Send + Sync> = Arc::new(output::to_void());
|
||||
pub static ref DEFAULT_CONTEXT: RwLock<Arc<OpenScope + Send + Sync>> = RwLock::new(NO_RECV_CONTEXT.clone());
|
||||
}
|
||||
|
||||
/// Wrap a MetricContext in a non-generic trait.
|
||||
pub trait OpenScope {
|
||||
/// Open a new metrics scope
|
||||
fn open_scope(&self) -> Arc<DefineMetric + Send + Sync>;
|
||||
}
|
||||
|
||||
/// Install a new receiver for all dispatched metrics, replacing any previous receiver.
|
||||
pub fn route_aggregate_metrics<IS: Into<MetricContext<T>>, T: Send + Sync + Clone + 'static>(into_ctx: IS) {
|
||||
let ctx = Arc::new(into_ctx.into());
|
||||
*DEFAULT_CONTEXT.write().unwrap() = ctx;
|
||||
}
|
||||
|
||||
use namespace::*;
|
||||
|
||||
/// A pair of functions composing a twin "chain of command".
|
||||
/// This is the building block for the metrics backend.
|
||||
|
@ -13,10 +33,10 @@ use namespace::*;
|
|||
#[derivative(Debug)]
|
||||
pub struct MetricContext<M> {
|
||||
#[derivative(Debug = "ignore")]
|
||||
define_metric_fn: DefineMetricFn<M>,
|
||||
prototype_metric_fn: DefineMetricFn<M>,
|
||||
|
||||
#[derivative(Debug = "ignore")]
|
||||
scope_metric_fn: OpenScopeFn<M>,
|
||||
open_scope_fn: OpenScopeFn<M>,
|
||||
}
|
||||
|
||||
impl<M> MetricContext<M> {
|
||||
|
@ -33,21 +53,21 @@ impl<M> MetricContext<M> {
|
|||
/// let request_counter = scope_metrics.counter("scope_counter");
|
||||
/// ```
|
||||
///
|
||||
pub fn open_scope(&self) -> Metrics<M> {
|
||||
Metrics::new(self.define_metric_fn.clone(), (self.scope_metric_fn)())
|
||||
pub fn open_scope(&self) -> MetricScope<M> {
|
||||
MetricScope::new(self.prototype_metric_fn.clone(), (self.open_scope_fn)())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Create a new metric chain with the provided metric definition and scope creation functions.
|
||||
pub fn metrics_context<MF, WF, M>(make_metric: MF, make_scope: WF) -> MetricContext<M>
|
||||
pub fn metrics_context<MF, WF, M>(define_fn: MF, open_scope_fn: WF) -> MetricContext<M>
|
||||
where
|
||||
MF: Fn(Kind, &str, Rate) -> M + Send + Sync + 'static,
|
||||
MF: Fn(Kind, &str, Sampling) -> M + Send + Sync + 'static,
|
||||
WF: Fn() -> WriteFn<M> + Send + Sync + 'static,
|
||||
{
|
||||
MetricContext {
|
||||
define_metric_fn: Arc::new(make_metric),
|
||||
scope_metric_fn: Arc::new(make_scope),
|
||||
prototype_metric_fn: Arc::new(define_fn),
|
||||
open_scope_fn: Arc::new(open_scope_fn),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,10 +80,10 @@ impl<M: Send + Sync + Clone + 'static> MetricContext<M> {
|
|||
N: Clone + Send + Sync,
|
||||
{
|
||||
let (metric_fn, scope_fn) =
|
||||
mod_fn(self.define_metric_fn.clone(), self.scope_metric_fn.clone());
|
||||
mod_fn(self.prototype_metric_fn.clone(), self.open_scope_fn.clone());
|
||||
MetricContext {
|
||||
define_metric_fn: metric_fn,
|
||||
scope_metric_fn: scope_fn,
|
||||
prototype_metric_fn: metric_fn,
|
||||
open_scope_fn: scope_fn,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,15 +93,21 @@ impl<M: Send + Sync + Clone + 'static> MetricContext<M> {
|
|||
MF: Fn(OpenScopeFn<M>) -> OpenScopeFn<M>,
|
||||
{
|
||||
MetricContext {
|
||||
define_metric_fn: self.define_metric_fn.clone(),
|
||||
scope_metric_fn: mod_fn(self.scope_metric_fn.clone()),
|
||||
prototype_metric_fn: self.prototype_metric_fn.clone(),
|
||||
open_scope_fn: mod_fn(self.open_scope_fn.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<M> From<MetricContext<M>> for Metrics<M> {
|
||||
fn from(metrics: MetricContext<M>) -> Metrics<M> {
|
||||
impl<M: Send + Sync + Clone + 'static> OpenScope for MetricContext<M> {
|
||||
fn open_scope(&self) -> Arc<DefineMetric + Send + Sync> {
|
||||
Arc::new(self.open_scope())
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> From<MetricContext<M>> for MetricScope<M> {
|
||||
fn from(metrics: MetricContext<M>) -> MetricScope<M> {
|
||||
metrics.open_scope()
|
||||
}
|
||||
}
|
||||
|
@ -90,8 +116,8 @@ impl<M: Send + Sync + Clone + 'static> WithNamespace for MetricContext<M> {
|
|||
fn with_name<IN: Into<Namespace>>(&self, names: IN) -> Self {
|
||||
let ref ninto = names.into();
|
||||
MetricContext {
|
||||
define_metric_fn: add_namespace(ninto, self.define_metric_fn.clone()),
|
||||
scope_metric_fn: self.scope_metric_fn.clone(),
|
||||
prototype_metric_fn: add_namespace(ninto, self.prototype_metric_fn.clone()),
|
||||
open_scope_fn: self.open_scope_fn.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,10 +37,10 @@ impl TimeHandle {
|
|||
/// - 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 Rate = f64;
|
||||
pub type Sampling = f64;
|
||||
|
||||
/// Do not sample, use all data.
|
||||
pub const FULL_SAMPLING_RATE: Rate = 1.0;
|
||||
pub const FULL_SAMPLING_RATE: Sampling = 1.0;
|
||||
|
||||
/// Used to differentiate between metric kinds in the backend.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
|
@ -60,7 +60,7 @@ pub enum Kind {
|
|||
/// 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(Kind, &str, Rate) -> M + Send + Sync>;
|
||||
pub type DefineMetricFn<M> = Arc<Fn(Kind, &str, Sampling) -> M + Send + Sync>;
|
||||
|
||||
/// A function trait that opens a new metric capture scope.
|
||||
pub type OpenScopeFn<M> = Arc<Fn() -> WriteFn<M> + Send + Sync>;
|
||||
|
|
102
src/delegate.rs
102
src/delegate.rs
|
@ -1,15 +1,38 @@
|
|||
//! Decouple metric definition from configuration with trait objects.
|
||||
|
||||
use core::*;
|
||||
use metrics::*;
|
||||
use metrics::{MetricScope, DefineMetric, WriteMetric, NO_RECV_METRICS};
|
||||
use namespace::*;
|
||||
use registry;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock, Weak};
|
||||
|
||||
use atomic_refcell::*;
|
||||
|
||||
/// Define delegate metrics.
|
||||
#[macro_export]
|
||||
macro_rules! delegate_metrics {
|
||||
(pub $METRIC_ID:ident = $e:expr $(;)*) => { metrics! {<Delegate> pub $METRIC_ID = $e; } };
|
||||
(pub $METRIC_ID:ident = $e:expr => { $($REMAINING:tt)+ }) => { metrics! {<Delegate> pub $METRIC_ID = $e => { $($REMAINING)* } } };
|
||||
($METRIC_ID:ident = $e:expr $(;)*) => { metrics! {<Delegate> $METRIC_ID = $e; } };
|
||||
($METRIC_ID:ident = $e:expr => { $($REMAINING:tt)+ }) => { metrics! {<Delegate> $METRIC_ID = $e => { $($REMAINING)* } } };
|
||||
($METRIC_ID:ident => { $($REMAINING:tt)+ }) => { metrics! {<Delegate> $METRIC_ID => { $($REMAINING)* } } };
|
||||
($e:expr => { $($REMAINING:tt)+ }) => { metrics! {<Delegate> $e => { $($REMAINING)* } } };
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref DELEGATE_REGISTRY: RwLock<Vec<MetricsSend>> = RwLock::new(vec![]);
|
||||
pub static ref DEFAULT_METRICS: RwLock<Arc<DefineMetric + Sync + Send>> = RwLock::new(NO_RECV_METRICS.clone());
|
||||
}
|
||||
|
||||
/// Install a new receiver for all dispatched metrics, replacing any previous receiver.
|
||||
pub fn set_default_metric_scope<IS: Into<MetricScope<T>>, T: Send + Sync + Clone + 'static>(into_recv: IS) {
|
||||
let recv = Arc::new(into_recv.into());
|
||||
for d in DELEGATE_REGISTRY.read().unwrap().iter() {
|
||||
d.set_receiver(recv.clone());
|
||||
}
|
||||
*DEFAULT_METRICS.write().unwrap() = recv;
|
||||
}
|
||||
|
||||
/// Create a new dispatch point for metrics.
|
||||
/// All dispatch points are automatically entered in the dispatch registry.
|
||||
|
@ -17,33 +40,13 @@ pub fn delegate_metrics() -> MetricsSend {
|
|||
let send = MetricsSend {
|
||||
inner: Arc::new(RwLock::new(InnerMetricsSend {
|
||||
metrics: HashMap::new(),
|
||||
recv: registry::get_default_metrics_recv(),
|
||||
recv: DEFAULT_METRICS.read().unwrap().clone(),
|
||||
})),
|
||||
};
|
||||
registry::add_metrics_send(send.clone());
|
||||
DELEGATE_REGISTRY.write().unwrap().push(send.clone());
|
||||
send
|
||||
}
|
||||
|
||||
/// Dynamic counterpart of a `Dispatcher`.
|
||||
/// Adapter to AppMetrics<_> of unknown type.
|
||||
pub trait MetricsRecv {
|
||||
/// Register a new metric.
|
||||
/// Only one metric of a certain name will be defined.
|
||||
/// Observer must return a MetricHandle that uniquely identifies the metric.
|
||||
fn define_metric(&self, kind: Kind, name: &str, rate: Rate) -> Box<RecvMetric + Send + Sync>;
|
||||
|
||||
/// Flush the receiver's scope.
|
||||
fn flush(&self);
|
||||
}
|
||||
|
||||
/// Dynamic counterpart of the `DispatcherMetric`.
|
||||
/// Adapter to a metric of unknown type.
|
||||
pub trait RecvMetric {
|
||||
/// Write metric value to a scope.
|
||||
/// Observers only receive previously registered handles.
|
||||
fn write(&self, value: Value);
|
||||
}
|
||||
|
||||
/// Shortcut name because `AppMetrics<Dispatch>`
|
||||
/// looks better than `AppMetrics<Arc<DispatcherMetric>>`.
|
||||
pub type Delegate = Arc<SendMetric>;
|
||||
|
@ -54,9 +57,9 @@ pub type Delegate = Arc<SendMetric>;
|
|||
pub struct SendMetric {
|
||||
kind: Kind,
|
||||
name: String,
|
||||
rate: Rate,
|
||||
rate: Sampling,
|
||||
#[derivative(Debug = "ignore")]
|
||||
recv_metric: AtomicRefCell<Box<RecvMetric + Send + Sync>>,
|
||||
recv_metric: AtomicRefCell<Box<WriteMetric + Send + Sync>>,
|
||||
#[derivative(Debug = "ignore")]
|
||||
send: MetricsSend,
|
||||
}
|
||||
|
@ -79,30 +82,33 @@ pub struct MetricsSend {
|
|||
|
||||
struct InnerMetricsSend {
|
||||
metrics: HashMap<String, Weak<SendMetric>>,
|
||||
recv: Arc<MetricsRecv + Send + Sync>,
|
||||
recv: Arc<DefineMetric + Send + Sync>,
|
||||
}
|
||||
|
||||
/// Allow turning a 'static str into a Delegate, where str is the prefix.
|
||||
impl From<&'static str> for Metrics<Delegate> {
|
||||
fn from(prefix: &'static str) -> Metrics<Delegate> {
|
||||
let app_metrics: Metrics<Delegate> = delegate_metrics().into();
|
||||
app_metrics.with_prefix(prefix)
|
||||
impl From<&'static str> for MetricScope<Delegate> {
|
||||
fn from(prefix: &'static str) -> MetricScope<Delegate> {
|
||||
let app_metrics: MetricScope<Delegate> = delegate_metrics().into();
|
||||
if !prefix.is_empty() {
|
||||
app_metrics.with_prefix(prefix)
|
||||
} else {
|
||||
app_metrics
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow turning a 'static str into a Delegate, where str is the prefix.
|
||||
impl From<()> for Metrics<Delegate> {
|
||||
fn from(_: ()) -> Metrics<Delegate> {
|
||||
let app_metrics: Metrics<Delegate> = delegate_metrics().into();
|
||||
impl From<()> for MetricScope<Delegate> {
|
||||
fn from(_: ()) -> MetricScope<Delegate> {
|
||||
let app_metrics: MetricScope<Delegate> = delegate_metrics().into();
|
||||
app_metrics
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl From<MetricsSend> for Metrics<Delegate> {
|
||||
fn from(send: MetricsSend) -> Metrics<Delegate> {
|
||||
impl From<MetricsSend> for MetricScope<Delegate> {
|
||||
fn from(send: MetricsSend) -> MetricScope<Delegate> {
|
||||
let send_cmd = send.clone();
|
||||
Metrics::new(
|
||||
MetricScope::new(
|
||||
// define metric
|
||||
Arc::new(move |kind, name, rate| send.define_metric(kind, name, rate)),
|
||||
|
||||
|
@ -120,7 +126,7 @@ impl From<MetricsSend> for Metrics<Delegate> {
|
|||
|
||||
impl MetricsSend {
|
||||
/// Install a new metric receiver, replacing the previous one.
|
||||
pub fn set_receiver<R: MetricsRecv + Send + Sync + 'static>(&self, recv: Arc<R>) {
|
||||
pub fn set_receiver<R: DefineMetric + Send + Sync + 'static>(&self, recv: Arc<R>) {
|
||||
let inner = &mut self.inner.write().expect("Lock Metrics Send");
|
||||
|
||||
for mut metric in inner.metrics.values() {
|
||||
|
@ -133,7 +139,7 @@ impl MetricsSend {
|
|||
inner.recv = recv.clone()
|
||||
}
|
||||
|
||||
fn define_metric(&self, kind: Kind, name: &str, rate: Rate) -> Delegate {
|
||||
fn define_metric(&self, kind: Kind, name: &str, rate: Sampling) -> Delegate {
|
||||
let mut inner = self.inner.write().expect("Lock Metrics Send");
|
||||
inner.metrics.get(name)
|
||||
.and_then(|metric_ref| Weak::upgrade(metric_ref))
|
||||
|
@ -165,25 +171,23 @@ impl MetricsSend {
|
|||
#[cfg(feature = "bench")]
|
||||
mod bench {
|
||||
|
||||
use super::*;
|
||||
use delegate::{delegate_metrics, set_default_metric_scope, Delegate};
|
||||
use test;
|
||||
use aggregate::*;
|
||||
use publish::*;
|
||||
use output::*;
|
||||
use metrics::MetricScope;
|
||||
use aggregate::aggregate;
|
||||
|
||||
#[bench]
|
||||
fn dispatch_marker_to_aggregate(b: &mut test::Bencher) {
|
||||
let dispatch = delegate_metrics();
|
||||
let sink: Metrics<Delegate> = dispatch.clone().into();
|
||||
dispatch.set_receiver(aggregate(summary, to_void()));
|
||||
set_default_metric_scope(aggregate());
|
||||
let sink: MetricScope<Delegate> = delegate_metrics().into();
|
||||
let metric = sink.marker("event_a");
|
||||
b.iter(|| test::black_box(metric.mark()));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn dispatch_marker_to_void(b: &mut test::Bencher) {
|
||||
let dispatch = delegate_metrics();
|
||||
let sink: Metrics<Delegate> = dispatch.into();
|
||||
let metrics = delegate_metrics();
|
||||
let sink: MetricScope<Delegate> = metrics.into();
|
||||
let metric = sink.marker("event_a");
|
||||
b.iter(|| test::black_box(metric.mark()));
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ pub fn to_buffered_graphite<ADDR>(address: ADDR) -> error::Result<MetricContext<
|
|||
))
|
||||
}
|
||||
|
||||
fn graphite_metric(kind: Kind, name: &str, rate: Rate) -> Graphite {
|
||||
fn graphite_metric(kind: Kind, name: &str, rate: Sampling) -> Graphite {
|
||||
let mut prefix = String::with_capacity(32);
|
||||
prefix.push_str(name);
|
||||
prefix.push(' ');
|
||||
|
|
17
src/lib.rs
17
src/lib.rs
|
@ -33,12 +33,14 @@ pub use core::*;
|
|||
pub mod context;
|
||||
pub use context::*;
|
||||
|
||||
//pub mod local_delegate;
|
||||
//pub use local_delegate::*;
|
||||
|
||||
#[macro_use]
|
||||
pub mod delegate;
|
||||
pub use delegate::*;
|
||||
|
||||
#[macro_use]
|
||||
mod aggregate;
|
||||
pub use aggregate::*;
|
||||
|
||||
mod output;
|
||||
pub use output::*;
|
||||
|
||||
|
@ -51,11 +53,6 @@ pub use sample::*;
|
|||
mod scores;
|
||||
pub use scores::*;
|
||||
|
||||
mod aggregate;
|
||||
pub use aggregate::*;
|
||||
|
||||
mod publish;
|
||||
pub use publish::*;
|
||||
|
||||
mod statsd;
|
||||
pub use statsd::*;
|
||||
|
@ -84,8 +81,4 @@ pub use async_queue::*;
|
|||
mod schedule;
|
||||
pub use schedule::*;
|
||||
|
||||
mod registry;
|
||||
pub use registry::*;
|
||||
|
||||
mod self_metrics;
|
||||
pub use self_metrics::snapshot;
|
||||
|
|
|
@ -19,25 +19,24 @@ macro_rules! time {
|
|||
|
||||
/// Metrics can be used from anywhere (public), does not need to declare metrics in this block.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! metrics {
|
||||
// TYPED
|
||||
// typed, public, no metrics
|
||||
(<$METRIC_TYPE:ty> pub $METRIC_ID:ident = $e:expr;) => {
|
||||
lazy_static! { pub static ref $METRIC_ID: Metrics<$METRIC_TYPE> = $e.into(); }
|
||||
(<$METRIC_TYPE:ty> pub $METRIC_ID:ident = $e:expr $(;)*) => {
|
||||
lazy_static! { pub static ref $METRIC_ID: MetricScope<$METRIC_TYPE> = $e.into(); }
|
||||
};
|
||||
// typed, public, some metrics
|
||||
(<$METRIC_TYPE:ty> pub $METRIC_ID:ident = $e:expr => { $($REMAINING:tt)+ }) => {
|
||||
lazy_static! { pub static ref $METRIC_ID: Metrics<$METRIC_TYPE> = $e.into(); }
|
||||
lazy_static! { pub static ref $METRIC_ID: MetricScope<$METRIC_TYPE> = $e.into(); }
|
||||
__metrics_block!($METRIC_ID: $METRIC_TYPE; $($REMAINING)*);
|
||||
};
|
||||
// typed, module, no metrics
|
||||
(<$METRIC_TYPE:ty> $METRIC_ID:ident = $e:expr;) => {
|
||||
lazy_static! { pub static ref $METRIC_ID: Metrics<$METRIC_TYPE> = $e.into(); }
|
||||
(<$METRIC_TYPE:ty> $METRIC_ID:ident = $e:expr $(;)*) => {
|
||||
lazy_static! { static ref $METRIC_ID: MetricScope<$METRIC_TYPE> = $e.into(); }
|
||||
};
|
||||
// typed, module, some metrics
|
||||
(<$METRIC_TYPE:ty> $METRIC_ID:ident = $e:expr => { $($REMAINING:tt)+ }) => {
|
||||
lazy_static! { pub static ref $METRIC_ID: Metrics<$METRIC_TYPE> = $e.into(); }
|
||||
lazy_static! { static ref $METRIC_ID: MetricScope<$METRIC_TYPE> = $e.into(); }
|
||||
__metrics_block!($METRIC_ID: $METRIC_TYPE; $($REMAINING)*);
|
||||
};
|
||||
// typed, reuse predeclared
|
||||
|
@ -46,48 +45,14 @@ macro_rules! metrics {
|
|||
};
|
||||
// typed, unidentified, some metrics
|
||||
(<$METRIC_TYPE:ty> $e:expr => { $($REMAINING:tt)+ }) => {
|
||||
lazy_static! { pub static ref UNIDENT_METRIC: Metrics<$METRIC_TYPE> = $e.into(); }
|
||||
lazy_static! { static ref UNIDENT_METRIC: MetricScope<$METRIC_TYPE> = $e.into(); }
|
||||
__metrics_block!(UNIDENT_METRIC: $METRIC_TYPE; $($REMAINING)*);
|
||||
};
|
||||
// typed, root, some metrics
|
||||
(<$METRIC_TYPE:ty> { $($REMAINING:tt)+ }) => {
|
||||
lazy_static! { pub static ref ROOT_METRICS: Metrics<$METRIC_TYPE> = "".into(); }
|
||||
lazy_static! { static ref ROOT_METRICS: MetricScope<$METRIC_TYPE> = ().into(); }
|
||||
__metrics_block!(ROOT_METRICS: $METRIC_TYPE; $($REMAINING)*);
|
||||
};
|
||||
|
||||
// DELEGATED
|
||||
// delegated, public, no metrics
|
||||
(pub $METRIC_ID:ident = $e:expr;) => {
|
||||
lazy_static! { pub static ref $METRIC_ID: Metrics<Delegate> = $e.into(); }
|
||||
};
|
||||
// delegated, public, some metrics
|
||||
(pub $METRIC_ID:ident = $e:expr => { $($REMAINING:tt)+ }) => {
|
||||
lazy_static! { pub static ref $METRIC_ID: Metrics<Delegate> = $e.into(); }
|
||||
__metrics_block!($METRIC_ID: Delegate; $($REMAINING)*);
|
||||
};
|
||||
// delegated, module, no metrics
|
||||
($METRIC_ID:ident = $e:expr;) => {
|
||||
lazy_static! { pub static ref $METRIC_ID: Metrics<Delegate> = $e.into(); }
|
||||
};
|
||||
// delegated, module, some metrics
|
||||
($METRIC_ID:ident = $e:expr => { $($REMAINING:tt)+ }) => {
|
||||
lazy_static! { pub static ref $METRIC_ID: Metrics<Delegate> = $e.into(); }
|
||||
__metrics_block!($METRIC_ID: Delegate; $($REMAINING)*);
|
||||
};
|
||||
// delegated,reuse predeclared
|
||||
($METRIC_ID:ident => { $($REMAINING:tt)+ }) => {
|
||||
__metrics_block!($METRIC_ID: Delegate; $($REMAINING)*);
|
||||
};
|
||||
// delegated, unidentified, some metrics
|
||||
($e:expr => { $($REMAINING:tt)+ }) => {
|
||||
lazy_static! { pub static ref UNIDENT_METRIC: Metrics<Delegate> = $e.into(); }
|
||||
__metrics_block!(UNIDENT_METRIC: Delegate; $($REMAINING)*);
|
||||
};
|
||||
// delegated, root, some metrics
|
||||
( => { $($REMAINING:tt)+ }) => {
|
||||
lazy_static! { pub static ref ROOT_METRICS: Metrics<Delegate> = ().into(); }
|
||||
__metrics_block!(ROOT_METRICS: Delegate; $($REMAINING)*);
|
||||
};
|
||||
}
|
||||
|
||||
/// Internal macro required to abstract over pub/non-pub versions of the macro
|
||||
|
@ -150,13 +115,13 @@ macro_rules! __metrics_block {
|
|||
#[deprecated(since="0.7.0", note="Use metrics!() instead")]
|
||||
macro_rules! app_metrics {
|
||||
($type_param: ty, $metric_id: ident = ($($app_metrics: expr),+ $(,)*)) => {
|
||||
lazy_static! { pub static ref $metric_id: Metrics<$type_param> = metrics(($($app_metrics),*)); }
|
||||
lazy_static! { pub static ref $metric_id: MetricScope<$type_param> = metrics(($($app_metrics),*)); }
|
||||
};
|
||||
($type_param: ty, $metric_id: ident = [$($app_metrics: expr),+ $(,)*]) => {
|
||||
lazy_static! { pub static ref $metric_id: Metrics<$type_param> = metrics(&[$($app_metrics),*][..],); }
|
||||
lazy_static! { pub static ref $metric_id: MetricScope<$type_param> = metrics(&[$($app_metrics),*][..],); }
|
||||
};
|
||||
($type_param: ty, $metric_id: ident = $app_metrics: expr) => {
|
||||
lazy_static! { pub static ref $metric_id: Metrics<$type_param> = $app_metrics.into(); }
|
||||
lazy_static! { pub static ref $metric_id: MetricScope<$type_param> = $app_metrics.into(); }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -213,13 +178,13 @@ macro_rules! app_timer {
|
|||
#[deprecated(since="0.7.0", note="Use metrics!() instead")]
|
||||
macro_rules! mod_metrics {
|
||||
($type_param: ty, $metric_id: ident = ($($app_metrics: expr),+ $(,)*)) => {
|
||||
lazy_static! { static ref $metric_id: Metrics<$type_param> = metrics(($($app_metrics),*)); }
|
||||
lazy_static! { static ref $metric_id: MetricScope<$type_param> = metrics(($($app_metrics),*)); }
|
||||
};
|
||||
($type_param: ty, $metric_id: ident = [$($app_metrics: expr),+ $(,)*]) => {
|
||||
lazy_static! { static ref $metric_id: Metrics<$type_param> = metrics(&[$($app_metrics),*][..],); }
|
||||
lazy_static! { static ref $metric_id: MetricScope<$type_param> = metrics(&[$($app_metrics),*][..],); }
|
||||
};
|
||||
($type_param: ty, $metric_id: ident = $mod_metrics: expr) => {
|
||||
lazy_static! { static ref $metric_id: Metrics<$type_param> = $mod_metrics.into(); }
|
||||
lazy_static! { static ref $metric_id: MetricScope<$type_param> = $mod_metrics.into(); }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
120
src/metrics.rs
120
src/metrics.rs
|
@ -11,31 +11,56 @@ use core::*;
|
|||
use core::Kind::*;
|
||||
use namespace::*;
|
||||
use cache::*;
|
||||
use schedule::*;
|
||||
use delegate::*;
|
||||
use schedule::{schedule, CancelHandle};
|
||||
use context;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
// TODO define an 'AsValue' trait + impl for supported number types, then drop 'num' crate
|
||||
pub use num::ToPrimitive;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref NO_RECV_METRICS: Arc<DefineMetric + Send + Sync> = context::NO_RECV_CONTEXT.open_scope();
|
||||
}
|
||||
|
||||
/// Dynamic counterpart of a `Dispatcher`.
|
||||
/// Adapter to AppMetrics<_> of unknown type.
|
||||
pub trait DefineMetric {
|
||||
/// Register a new metric.
|
||||
/// Only one metric of a certain name will be defined.
|
||||
/// Observer must return a MetricHandle that uniquely identifies the metric.
|
||||
fn define_metric(&self, kind: Kind, name: &str, rate: Sampling) -> Box<WriteMetric + Send + Sync>;
|
||||
|
||||
/// Flush the receiver's scope.
|
||||
fn flush(&self);
|
||||
}
|
||||
|
||||
/// Dynamic counterpart of the `DispatcherMetric`.
|
||||
/// Adapter to a metric of unknown type.
|
||||
pub trait WriteMetric {
|
||||
/// Write metric value to a scope.
|
||||
/// Observers only receive previously registered handles.
|
||||
fn write(&self, value: Value);
|
||||
}
|
||||
|
||||
/// Wrap the metrics backend to provide an application-friendly interface.
|
||||
/// Open a metric scope to share across the application.
|
||||
#[deprecated(since="0.7.0", note="Use metrics() instead")]
|
||||
pub fn app_metrics<M, AM>(scope: AM) -> Metrics<M>
|
||||
pub fn app_metrics<M, AM>(scope: AM) -> MetricScope<M>
|
||||
where
|
||||
M: Clone + Send + Sync + 'static,
|
||||
AM: Into<Metrics<M>>,
|
||||
AM: Into<MetricScope<M>>,
|
||||
{
|
||||
scope.into()
|
||||
}
|
||||
|
||||
/// Wrap the metrics backend to provide an application-friendly interface.
|
||||
/// Open a metric scope to share across the application.
|
||||
pub fn metrics<M, AM>(scope: AM) -> Metrics<M>
|
||||
pub fn metrics<M, AM>(scope: AM) -> MetricScope<M>
|
||||
where
|
||||
M: Clone + Send + Sync + 'static,
|
||||
AM: Into<Metrics<M>>,
|
||||
AM: Into<MetricScope<M>>,
|
||||
{
|
||||
scope.into()
|
||||
}
|
||||
|
@ -145,7 +170,7 @@ impl<M> Timer<M> {
|
|||
|
||||
/// Help transition to new syntax
|
||||
#[deprecated(since="0.7.0", note="Use Metrics instead")]
|
||||
pub type AppMetrics<M> = Metrics<M>;
|
||||
pub type AppMetrics<M> = MetricScope<M>;
|
||||
|
||||
/// Help transition to new syntax
|
||||
#[deprecated(since="0.7.0", note="Use Marker instead")]
|
||||
|
@ -166,31 +191,31 @@ pub type AppTimer<M> = Timer<M>;
|
|||
|
||||
/// Variations of this should also provide control of the metric recording scope.
|
||||
#[derive(Derivative, Clone)]
|
||||
pub struct Metrics<M> {
|
||||
pub struct MetricScope<M> {
|
||||
#[derivative(Debug = "ignore")]
|
||||
define_metric_fn: DefineMetricFn<M>,
|
||||
define_fn: DefineMetricFn<M>,
|
||||
#[derivative(Debug = "ignore")]
|
||||
single_scope: WriteFn<M>,
|
||||
write_fn: WriteFn<M>,
|
||||
}
|
||||
|
||||
impl<M> Metrics<M> {
|
||||
impl<M> MetricScope<M> {
|
||||
/// Create new application metrics instance.
|
||||
pub fn new(define_metric_fn: DefineMetricFn<M>, scope: WriteFn<M>) -> Self {
|
||||
Metrics {
|
||||
define_metric_fn,
|
||||
single_scope: scope,
|
||||
MetricScope {
|
||||
define_fn: define_metric_fn,
|
||||
write_fn: scope,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> Metrics<M>
|
||||
impl<M> MetricScope<M>
|
||||
where
|
||||
M: Clone + Send + Sync + 'static,
|
||||
{
|
||||
/// Define a raw metric.
|
||||
/// Define a metric of specified type.
|
||||
#[inline]
|
||||
pub fn define_metric(&self, kind: Kind, name: &str, rate: Rate) -> M {
|
||||
(self.define_metric_fn)(kind, name, rate)
|
||||
pub fn define_metric(&self, kind: Kind, name: &str, rate: Sampling) -> M {
|
||||
(self.define_fn)(kind, name, rate)
|
||||
}
|
||||
|
||||
/// Define an event counter of the provided name.
|
||||
|
@ -198,7 +223,7 @@ where
|
|||
let metric = self.define_metric(Marker, name.as_ref(), 1.0);
|
||||
Marker {
|
||||
metric,
|
||||
scope: self.single_scope.clone(),
|
||||
scope: self.write_fn.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,7 +232,7 @@ where
|
|||
let metric = self.define_metric(Counter, name.as_ref(), 1.0);
|
||||
Counter {
|
||||
metric,
|
||||
scope: self.single_scope.clone(),
|
||||
scope: self.write_fn.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,7 +241,7 @@ where
|
|||
let metric = self.define_metric(Timer, name.as_ref(), 1.0);
|
||||
Timer {
|
||||
metric,
|
||||
scope: self.single_scope.clone(),
|
||||
scope: self.write_fn.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,73 +250,73 @@ where
|
|||
let metric = self.define_metric(Gauge, name.as_ref(), 1.0);
|
||||
Gauge {
|
||||
metric,
|
||||
scope: self.single_scope.clone(),
|
||||
scope: self.write_fn.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Flush the backing metrics buffer.
|
||||
/// The effect, if any, of this method depends on the selected metrics backend.
|
||||
/// Flushes any recorded metric value.
|
||||
/// Has no effect on unbuffered metrics.
|
||||
pub fn flush(&self) {
|
||||
self.single_scope.flush();
|
||||
self.write_fn.flush();
|
||||
}
|
||||
|
||||
/// Schedule for the metrics aggregated of buffered by downstream metrics sinks to be
|
||||
/// sent out at regular intervals.
|
||||
pub fn flush_every(&self, period: Duration) -> CancelHandle {
|
||||
let scope = self.single_scope.clone();
|
||||
let scope = self.write_fn.clone();
|
||||
schedule(period, move || scope.flush())
|
||||
}
|
||||
|
||||
/// Record a raw metric value.
|
||||
pub fn write(&self, metric: &M, value: Value) {
|
||||
self.single_scope.write(metric, value);
|
||||
self.write_fn.write(metric, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//// Dispatch / Receiver impl
|
||||
|
||||
struct RecvMetricImpl<M> {
|
||||
struct MetricWriter<M> {
|
||||
metric: M,
|
||||
scope: WriteFn<M>,
|
||||
write_fn: WriteFn<M>,
|
||||
}
|
||||
|
||||
impl<M: Send + Sync + Clone + 'static> MetricsRecv for Metrics<M> {
|
||||
fn define_metric(&self, kind: Kind, name: &str, rate: Rate) -> Box<RecvMetric + Send + Sync> {
|
||||
let scope: WriteFn<M> = self.single_scope.clone();
|
||||
let metric: M = self.define_metric(kind, name, rate);
|
||||
|
||||
Box::new(RecvMetricImpl { metric, scope })
|
||||
impl<M: Send + Sync + Clone + 'static> DefineMetric for MetricScope<M> {
|
||||
fn define_metric(&self, kind: Kind, name: &str, rate: Sampling) -> Box<WriteMetric + Send + Sync> {
|
||||
Box::new(MetricWriter {
|
||||
metric: self.define_metric(kind, name, rate),
|
||||
write_fn: self.write_fn.clone()
|
||||
})
|
||||
}
|
||||
|
||||
fn flush(&self) {
|
||||
self.flush()
|
||||
self.flush();
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> RecvMetric for RecvMetricImpl<M> {
|
||||
impl<M> WriteMetric for MetricWriter<M> {
|
||||
fn write(&self, value: Value) {
|
||||
self.scope.write(&self.metric, value);
|
||||
self.write_fn.write(&self.metric, value);
|
||||
}
|
||||
}
|
||||
|
||||
//// Mutators impl
|
||||
|
||||
impl<M: Send + Sync + Clone + 'static> WithNamespace for Metrics<M> {
|
||||
impl<M: Send + Sync + Clone + 'static> WithNamespace for MetricScope<M> {
|
||||
fn with_name<IN: Into<Namespace>>(&self, names: IN) -> Self {
|
||||
let ns = &names.into();
|
||||
Metrics {
|
||||
define_metric_fn: add_namespace(ns, self.define_metric_fn.clone()),
|
||||
single_scope: self.single_scope.clone(),
|
||||
MetricScope {
|
||||
define_fn: add_namespace(ns, self.define_fn.clone()),
|
||||
write_fn: self.write_fn.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Send + Sync + Clone + 'static> WithCache for Metrics<M> {
|
||||
impl<M: Send + Sync + Clone + 'static> WithCache for MetricScope<M> {
|
||||
fn with_cache(&self, cache_size: usize) -> Self {
|
||||
Metrics {
|
||||
define_metric_fn: add_cache(cache_size, self.define_metric_fn.clone()),
|
||||
single_scope: self.single_scope.clone(),
|
||||
MetricScope {
|
||||
define_fn: add_cache(cache_size, self.define_fn.clone()),
|
||||
write_fn: self.write_fn.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -304,8 +329,7 @@ mod bench {
|
|||
|
||||
#[bench]
|
||||
fn time_bench_direct_dispatch_event(b: &mut test::Bencher) {
|
||||
let sink = aggregate(summary, to_void());
|
||||
let metrics = metrics(sink);
|
||||
let metrics = metrics(aggregate());
|
||||
let marker = metrics.marker("aaa");
|
||||
b.iter(|| test::black_box(marker.mark()));
|
||||
}
|
||||
|
|
14
src/multi.rs
14
src/multi.rs
|
@ -9,19 +9,19 @@ use std::sync::Arc;
|
|||
/// Two chains of different types can be combined in a tuple.
|
||||
/// The chains will act as one, each receiving calls in the order the appear in the tuple.
|
||||
/// For more than two types, make tuples of tuples, "Yo Dawg" style.
|
||||
impl<M1, M2> From<(MetricContext<M1>, MetricContext<M2>)> for Metrics<(M1, M2)>
|
||||
impl<M1, M2> From<(MetricContext<M1>, MetricContext<M2>)> for MetricScope<(M1, M2)>
|
||||
where
|
||||
M1: 'static + Clone + Send + Sync,
|
||||
M2: 'static + Clone + Send + Sync,
|
||||
{
|
||||
fn from(combo: (MetricContext<M1>, MetricContext<M2>)) -> Metrics<(M1, M2)> {
|
||||
fn from(combo: (MetricContext<M1>, MetricContext<M2>)) -> MetricScope<(M1, M2)> {
|
||||
let scope0 = combo.0.open_scope();
|
||||
let scope1 = combo.1.open_scope();
|
||||
|
||||
let scope0a = scope0.clone();
|
||||
let scope1a = scope1.clone();
|
||||
|
||||
Metrics::new(
|
||||
MetricScope::new(
|
||||
Arc::new(move |kind, name, rate| (
|
||||
scope0.define_metric(kind, name, rate),
|
||||
scope1.define_metric(kind, name, rate),
|
||||
|
@ -41,15 +41,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, M> From<&'a [MetricContext<M>]> for Metrics<Vec<M>>
|
||||
impl<'a, M> From<&'a [MetricContext<M>]> for MetricScope<Vec<M>>
|
||||
where
|
||||
M: 'static + Clone + Send + Sync,
|
||||
{
|
||||
fn from(chains: &'a [MetricContext<M>]) -> Metrics<Vec<M>> {
|
||||
let scopes: Vec<Metrics<M>> = chains.iter().map(|x| x.open_scope()).collect();
|
||||
fn from(chains: &'a [MetricContext<M>]) -> MetricScope<Vec<M>> {
|
||||
let scopes: Vec<MetricScope<M>> = chains.iter().map(|x| x.open_scope()).collect();
|
||||
let scopes2 = scopes.clone();
|
||||
|
||||
Metrics::new(
|
||||
MetricScope::new(
|
||||
Arc::new(move |kind, name, rate| {
|
||||
scopes.iter().map(|m| m.define_metric(kind, name, rate)).collect()
|
||||
}),
|
||||
|
|
112
src/publish.rs
112
src/publish.rs
|
@ -26,115 +26,3 @@ use scores::{ScoreSnapshot, ScoreType};
|
|||
use scores::ScoreType::*;
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// A trait to publish metrics.
|
||||
pub trait Publish: Send + Sync + Debug {
|
||||
/// Publish the provided metrics data downstream.
|
||||
fn publish(&self, scores: Vec<ScoreSnapshot>);
|
||||
}
|
||||
|
||||
/// Define and write metrics from aggregated scores to the target channel
|
||||
/// If this is called repeatedly it can be a good idea to use the metric cache
|
||||
/// to prevent new metrics from being created every time.
|
||||
#[derive(Derivative, Clone)]
|
||||
#[derivative(Debug)]
|
||||
pub struct Publisher<E, M> {
|
||||
#[derivative(Debug = "ignore")]
|
||||
statistics: Box<E>,
|
||||
output: MetricContext<M>,
|
||||
}
|
||||
|
||||
impl<E, M> Publisher<E, M>
|
||||
where
|
||||
E: Fn(Kind, &str, ScoreType) -> Option<(Kind, Vec<&str>, Value)> + Send + Sync + 'static,
|
||||
M: Clone + Send + Sync + 'static,
|
||||
{
|
||||
/// Define a new metrics publishing strategy, from a transformation
|
||||
/// function and a target metric chain.
|
||||
pub fn new(stat_fn: E, output: MetricContext<M>) -> Self {
|
||||
Publisher {
|
||||
statistics: Box::new(stat_fn),
|
||||
output,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, M> Publish for Publisher<E, M>
|
||||
where
|
||||
M: Clone + Send + Sync + Debug + 'static,
|
||||
E: Fn(Kind, &str, ScoreType) -> Option<(Kind, Vec<&str>, Value)> + Send + Sync + 'static,
|
||||
{
|
||||
fn publish(&self, snapshot: Vec<ScoreSnapshot>) {
|
||||
let publish_scope = self.output.open_scope();
|
||||
if snapshot.is_empty() {
|
||||
// no data was collected for this period
|
||||
// TODO repeat previous frame min/max ?
|
||||
// TODO update some canary metric ?
|
||||
} else {
|
||||
for metric in snapshot {
|
||||
for score in metric.2 {
|
||||
if let Some(ex) = (self.statistics)(metric.0, metric.1.as_ref(), score) {
|
||||
let pub_metric = publish_scope.define_metric(ex.0, &ex.1.concat(), 1.0);
|
||||
publish_scope.write(&pub_metric, ex.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO parameterize whether to keep ad-hoc metrics after publish
|
||||
// source.cleanup();
|
||||
publish_scope.flush()
|
||||
}
|
||||
}
|
||||
|
||||
/// A predefined export strategy reporting all aggregated stats for all metric types.
|
||||
/// Resulting stats are named by appending a short suffix to each metric's name.
|
||||
pub fn all_stats(kind: Kind, name: &str, score: ScoreType) -> Option<(Kind, Vec<&str>, Value)> {
|
||||
match score {
|
||||
Count(hit) => Some((Counter, vec![name, ".count"], hit)),
|
||||
Sum(sum) => Some((kind, vec![name, ".sum"], sum)),
|
||||
Mean(mean) => Some((kind, vec![name, ".mean"], mean.round() as Value)),
|
||||
Max(max) => Some((Gauge, vec![name, ".max"], max)),
|
||||
Min(min) => Some((Gauge, vec![name, ".min"], min)),
|
||||
Rate(rate) => Some((Gauge, vec![name, ".rate"], rate.round() as Value)),
|
||||
}
|
||||
}
|
||||
|
||||
/// A predefined export strategy reporting the average value for every non-marker metric.
|
||||
/// Marker metrics export their hit count instead.
|
||||
/// Since there is only one stat per metric, there is no risk of collision
|
||||
/// and so exported stats copy their metric's name.
|
||||
pub fn average(kind: Kind, name: &str, score: ScoreType) -> Option<(Kind, Vec<&str>, Value)> {
|
||||
match kind {
|
||||
Marker => match score {
|
||||
Count(count) => Some((Counter, vec![name], count)),
|
||||
_ => None,
|
||||
},
|
||||
_ => match score {
|
||||
Mean(avg) => Some((Gauge, vec![name], avg.round() as Value)),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// A predefined single-stat-per-metric export strategy:
|
||||
/// - Timers and Counters each export their sums
|
||||
/// - Markers each export their hit count
|
||||
/// - Gauges each export their average
|
||||
/// Since there is only one stat per metric, there is no risk of collision
|
||||
/// and so exported stats copy their metric's name.
|
||||
pub fn summary(kind: Kind, name: &str, score: ScoreType) -> Option<(Kind, Vec<&str>, Value)> {
|
||||
match kind {
|
||||
Marker => match score {
|
||||
Count(count) => Some((Counter, vec![name], count)),
|
||||
_ => None,
|
||||
},
|
||||
Counter | Timer => match score {
|
||||
Sum(sum) => Some((kind, vec![name], sum)),
|
||||
_ => None,
|
||||
},
|
||||
Gauge => match score {
|
||||
Mean(mean) => Some((Gauge, vec![name], mean.round() as Value)),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,9 @@
|
|||
use metrics::Metrics;
|
||||
use output;
|
||||
use delegate::{MetricsRecv, MetricsSend};
|
||||
use delegate::{MetricsRecv, MetricsSend, ContextRecv};
|
||||
use aggregate::{Aggregator, summary};
|
||||
use core::*;
|
||||
use context::MetricContext;
|
||||
use scores::*;
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
fn no_metrics() -> Arc<MetricsRecv + Send + Sync> {
|
||||
let void_metrics: Metrics<_> = output::to_void().into();
|
||||
Arc::new(void_metrics)
|
||||
}
|
||||
|
||||
/// The registry contains a list of every metrics dispatch point in the app.
|
||||
lazy_static! {
|
||||
static ref NO_RECV: Arc<MetricsRecv + Sync + Send> = no_metrics();
|
||||
|
||||
static ref DEFAULT_RECV: RwLock<Arc<MetricsRecv + Sync + Send>> = RwLock::new(NO_RECV.clone());
|
||||
|
||||
static ref DELEGATE_REGISTRY: RwLock<Vec<MetricsSend>> = RwLock::new(vec![]);
|
||||
}
|
||||
|
||||
/// Register a new app send.
|
||||
pub fn add_metrics_send(send: MetricsSend) {
|
||||
DELEGATE_REGISTRY.write().unwrap().push(send.clone());
|
||||
}
|
||||
|
||||
/// Get the default app recv.
|
||||
pub fn get_default_metrics_recv() -> Arc<MetricsRecv + Send + Sync> {
|
||||
DEFAULT_RECV.read().unwrap().clone()
|
||||
}
|
||||
|
||||
/// Install a new receiver for all dispatched metrics, replacing any previous receiver.
|
||||
pub fn send_metrics<IS: Into<Metrics<T>>, T: Send + Sync + Clone + 'static>(
|
||||
into_recv: IS,
|
||||
) {
|
||||
let recv = Arc::new(into_recv.into());
|
||||
for d in DELEGATE_REGISTRY.read().unwrap().iter() {
|
||||
d.set_receiver(recv.clone());
|
||||
}
|
||||
|
||||
*DEFAULT_RECV.write().unwrap() = recv;
|
||||
}
|
|
@ -13,11 +13,11 @@ where
|
|||
Self: Sized,
|
||||
{
|
||||
/// Perform random sampling of values according to the specified rate.
|
||||
fn with_sampling_rate(&self, sampling_rate: Rate) -> Self;
|
||||
fn with_sampling_rate(&self, sampling_rate: Sampling) -> Self;
|
||||
}
|
||||
|
||||
impl<M: Send + Sync + 'static + Clone> WithSamplingRate for MetricContext<M> {
|
||||
fn with_sampling_rate(&self, sampling_rate: Rate) -> Self {
|
||||
fn with_sampling_rate(&self, sampling_rate: Sampling) -> Self {
|
||||
let int_sampling_rate = pcg32::to_int_rate(sampling_rate);
|
||||
|
||||
self.mod_both(|metric_fn, scope_fn| {
|
||||
|
@ -52,7 +52,7 @@ impl<M: Send + Sync + 'static + Clone> WithSamplingRate for MetricContext<M> {
|
|||
|
||||
/// Perform random sampling of values according to the specified rate.
|
||||
#[deprecated(since = "0.5.0", note = "Use `with_sampling_rate` instead.")]
|
||||
pub fn sample<M, IC>(sampling_rate: Rate, chain: IC) -> MetricContext<M>
|
||||
pub fn sample<M, IC>(sampling_rate: Sampling, chain: IC) -> MetricContext<M>
|
||||
where
|
||||
M: Clone + Send + Sync + 'static,
|
||||
IC: Into<MetricContext<M>>,
|
||||
|
|
|
@ -1,37 +1,12 @@
|
|||
//! Internal Dipstick metrics.
|
||||
//! Collect statistics about various metrics modules at runtime.
|
||||
//! Stats can can be obtained for publication from `selfstats::SOURCE`.
|
||||
//! Internal Dipstick runtime metrics.
|
||||
//! Because the possibly high volume of data, this is pre-set to use aggregation.
|
||||
//! This is also kept in a separate module because it is not to be exposed outside of the crate.
|
||||
|
||||
pub use core::*;
|
||||
|
||||
pub use metrics::*;
|
||||
pub use aggregate::*;
|
||||
pub use publish::*;
|
||||
pub use scores::*;
|
||||
pub use namespace::*;
|
||||
|
||||
use output::to_void;
|
||||
aggregate_metrics!(pub DIPSTICK_METRICS = "dipstick");
|
||||
|
||||
lazy_static! {
|
||||
static ref DIPSTICK_AGGREGATOR: Aggregator = build_aggregator();
|
||||
}
|
||||
|
||||
/// Application metrics are collected to the aggregator
|
||||
|
||||
metrics!(<Aggregate> DIPSTICK_METRICS = build_self_metrics(););
|
||||
|
||||
fn build_aggregator() -> Aggregator {
|
||||
// TODO make publishable
|
||||
aggregate(summary, to_void())
|
||||
}
|
||||
|
||||
/// Capture a snapshot of Dipstick's internal metrics since the last snapshot.
|
||||
pub fn snapshot() -> Vec<ScoreSnapshot> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn build_self_metrics() -> Metrics<Aggregate> {
|
||||
let mug: &Aggregator = &DIPSTICK_AGGREGATOR;
|
||||
let am: Metrics<Aggregate> = mug.clone().into();
|
||||
am.with_prefix("dipstick")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue