A quick, modular metrics toolkit for Rust applications; similar to popular logging frameworks, but with counters, markers, gauges and timers.
Dipstick builds on stable Rust with minimal dependencies and is published as a crate.
Features
- Send metrics to stdout, log, statsd or graphite (one or many)
- Synchronous, asynchronous or mixed operation
- Optional fast random statistical sampling
- Immediate propagation or local aggregation of metrics (count, sum, average, min/max)
- Periodic or programmatic publication of aggregated metrics
- Customizable output statistics and formatting
- Global or scoped (e.g. per request) metrics
- Per-application and per-output metric namespaces
- Predefined or ad-hoc metrics
Cookbook
Dipstick is easy to add to your code:
use *;
let app_metrics = metrics;
app_metrics.counter.count;
Metrics can be sent to multiple outputs at the same time:
let app_metrics = metrics;
Since instruments are decoupled from the backend, outputs can be swapped easily.
Metrics can be aggregated and scheduled to be published periodically in the background:
use Duration;
let = aggregate;
publish_every;
let app_metrics = metrics;
Aggregation is performed locklessly and is very fast.
Count, sum, min, max and average are tracked where they make sense.
Published statistics can be selected with presets such as all_stats
(see previous example),
summary
, average
.
For more control over published statistics, a custom filter can be provided:
let = aggregate;
publish;
Metrics can be statistically sampled:
let app_metrics = metrics;
A fast random algorithm is used to pick samples. Outputs can use sample rate to expand or format published data.
Metrics can be recorded asynchronously:
let app_metrics = metrics;
The async queue uses a Rust channel and a standalone thread. The current behavior is to block when full.
Metric definitions can be cached to make using ad-hoc metrics faster:
let app_metrics = metrics;
app_metrics.gauge.value;
The preferred way is to predefine metrics, possibly in a lazy_static! block:
crate lazy_static;
lazy_static!
COUNTER_A.count;
external
Timers can be used multiple ways:
let timer = app_metrics.timer;
time!;
timer.time;
let start = timer.start;
/* slow code here */
timer.stop;
timer.interval_us;
Related metrics can share a namespace:
let db_metrics = app_metrics.with_prefix;
let db_timer = db_metrics.timer;
let db_counter = db_metrics.counter;
Design
Dipstick's design goals are to:
- support as many metrics backends as possible while favoring none
- support all types of applications, from embedded to servers
- promote metrics conventions that facilitate app monitoring and maintenance
- stay out of the way in the code and at runtime (ergonomic, fast, resilient)
Performance
Predefined timers use a bit more code but are generally faster because their initialization cost is is only paid once.
Ad-hoc timers are redefined "inline" on each use. They are more flexible, but have more overhead because their init cost is paid on each use.
Defining a metric cache()
reduces that cost for recurring metrics.
Run benchmarks with cargo +nightly bench --features bench
.
TODO
Although already usable, Dipstick is still under heavy development and makes no guarantees of any kind at this point. See the following list for any potential caveats :
- META turn TODOs into GitHub issues
- generic publisher / sources
- feature flags
- time measurement units in metric kind (us, ms, etc.) for naming & scaling
- heartbeat metric on publish
- logger templates
- configurable aggregation (?)
- non-aggregating buffers
- framework glue (rocket, iron, gotham, indicatif, etc.)
- more tests & benchmarks
- complete doc / inline samples
- more example apps
- A cool logo
- method annotation processors
#[timer("name")]
- fastsinks (M / &M) vs. safesinks (Arc)
static_metric!
macro to replacelazy_static!
blocks and handle generics boilerplate.
License: MIT/Apache-2.0
this file was generated using cargo readme