2017-09-29 18:41:22 +00:00
|
|
|
# Dipstick
|
2017-08-19 18:09:17 +00:00
|
|
|
|
2017-09-28 22:17:16 +00:00
|
|
|
A fast and modular metrics toolkit for all Rust applications.
|
2017-09-29 18:41:22 +00:00
|
|
|
Similar to popular logging frameworks, but with counters, markers, gauges and timers.
|
2017-06-29 21:52:37 +00:00
|
|
|
|
2017-09-29 18:41:22 +00:00
|
|
|
Out of the box, Dipstick _can_ aggregate, sample, cache and queue metrics (async).
|
|
|
|
If aggregated, statistics can be published on demand or on schedule.
|
|
|
|
|
|
|
|
Dipstick does not bind application code to a single metrics output implementation.
|
|
|
|
Outputs `to_log`, `to_stdout` and `to_statsd` are currently provided.
|
|
|
|
Adding a new module is easy and PRs are welcome :)
|
|
|
|
|
|
|
|
Dipstick builds on stable Rust with minimal dependencies.
|
2017-07-24 20:36:23 +00:00
|
|
|
|
2017-08-21 19:31:52 +00:00
|
|
|
```rust
|
2017-08-21 21:36:35 +00:00
|
|
|
use dipstick::*;
|
2017-09-29 18:41:22 +00:00
|
|
|
let app_metrics = metrics(to_log("metrics:"));
|
2017-09-28 22:17:16 +00:00
|
|
|
app_metrics.counter("my_counter").count(3);
|
|
|
|
```
|
2017-09-02 05:16:39 +00:00
|
|
|
|
2017-09-28 22:17:16 +00:00
|
|
|
Metrics can be sent to multiple outputs at the same time.
|
|
|
|
```rust
|
2017-09-29 18:41:22 +00:00
|
|
|
let app_metrics = metrics((to_stdout(), to_statsd("localhost:8125", "app1.host.")));
|
2017-09-28 22:17:16 +00:00
|
|
|
```
|
|
|
|
Since instruments are decoupled from the backend, outputs can be swapped easily.
|
|
|
|
|
2017-09-29 18:41:22 +00:00
|
|
|
Metrics can be aggregated and scheduled to be published periodically in the background.
|
2017-09-28 22:17:16 +00:00
|
|
|
```rust
|
|
|
|
use std::time::Duration;
|
|
|
|
let (to_aggregate, from_aggregate) = aggregate();
|
|
|
|
publish_every(Duration::from_secs(10), from_aggregate, to_log("last_ten_secs:"), all_stats);
|
|
|
|
let app_metrics = metrics(to_aggregate);
|
|
|
|
```
|
2017-09-29 18:41:22 +00:00
|
|
|
Aggregation is performed locklessly and is very fast.
|
|
|
|
Count, sum, min, max and average are tracked where they make sense.
|
|
|
|
|
|
|
|
Publishing can use predefined strategies `all_stats`, `summary`, `average` or a custom one.
|
|
|
|
```rust
|
|
|
|
let (_to_aggregate, from_aggregate) = aggregate();
|
|
|
|
publish(from_aggregate, to_log("my_custom_stats:"),
|
|
|
|
|kind, name, score| match score {
|
|
|
|
HitCount(hit) => Some((Counter, vec![name, ".per_thousand"], hit / 1000)),
|
|
|
|
_ => None
|
|
|
|
});
|
|
|
|
```
|
2017-09-05 04:24:43 +00:00
|
|
|
|
2017-09-28 22:17:16 +00:00
|
|
|
Metrics can be statistically sampled.
|
|
|
|
```rust
|
2017-09-29 18:41:22 +00:00
|
|
|
let app_metrics = metrics(sample(0.001, to_statsd("server:8125", "app.sampled.")));
|
2017-09-02 05:16:39 +00:00
|
|
|
```
|
2017-09-29 18:41:22 +00:00
|
|
|
A fast random algorithm is used to pick samples.
|
|
|
|
Outputs can use sample rate to expand or format published data.
|
2017-09-02 05:16:39 +00:00
|
|
|
|
2017-09-28 22:17:16 +00:00
|
|
|
Metrics can be recorded asynchronously.
|
2017-09-02 05:16:39 +00:00
|
|
|
```rust
|
2017-09-29 18:41:22 +00:00
|
|
|
let app_metrics = metrics(async(48, to_stdout()));
|
2017-09-28 22:17:16 +00:00
|
|
|
```
|
2017-09-29 18:41:22 +00:00
|
|
|
The async queue uses a Rust channel and a standalone thread. Its current behavior is to block when full.
|
2017-09-28 22:17:16 +00:00
|
|
|
|
2017-09-29 18:41:22 +00:00
|
|
|
Metric definitions can be cached to make using _ad-hoc metrics_ faster.
|
2017-09-28 22:17:16 +00:00
|
|
|
```rust
|
|
|
|
let app_metrics = metrics(cache(512, to_log()));
|
|
|
|
app_metrics.gauge(format!("my_gauge_{}", 34)).value(44);
|
|
|
|
```
|
2017-09-29 18:41:22 +00:00
|
|
|
The preferred way is to _predefine metrics_, possibly in a [lazy_static!](https://crates.io/crates/lazy_static) block.
|
2017-09-02 05:16:39 +00:00
|
|
|
|
2017-09-28 22:17:16 +00:00
|
|
|
Timers can be used multiple ways.
|
|
|
|
```rust
|
|
|
|
let timer = app_metrics.timer("my_timer");
|
|
|
|
time!(timer, {/* slow code here */} );
|
|
|
|
timer.time(|| {/* slow code here */} );
|
|
|
|
|
|
|
|
let start = timer.start();
|
|
|
|
/* slow code here */
|
|
|
|
timer.stop(start);
|
2017-09-02 05:16:39 +00:00
|
|
|
|
2017-09-28 22:17:16 +00:00
|
|
|
timer.interval_us(123_456);
|
2017-06-29 21:52:37 +00:00
|
|
|
```
|
2017-09-02 05:16:39 +00:00
|
|
|
|
2017-09-28 22:17:16 +00:00
|
|
|
Related metrics can share a namespace.
|
|
|
|
```rust
|
|
|
|
let db_metrics = app_metrics.with_prefix("database.");
|
|
|
|
let db_timer = db_metrics.timer("db_timer");
|
|
|
|
let db_counter = db_metrics.counter("db_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)
|
2017-08-21 19:31:52 +00:00
|
|
|
|
2017-09-05 04:24:43 +00:00
|
|
|
## Performance
|
2017-09-28 22:17:16 +00:00
|
|
|
Predefined timers use a bit more code but are generally faster because their initialization cost is is only paid once.
|
2017-09-05 04:24:43 +00:00
|
|
|
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.
|
|
|
|
|
2017-09-20 17:13:44 +00:00
|
|
|
Run benchmarks with `cargo +nightly bench --features bench`.
|
2017-09-05 04:24:43 +00:00
|
|
|
|
2017-08-21 21:34:49 +00:00
|
|
|
## TODO
|
2017-08-21 19:31:52 +00:00
|
|
|
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 :
|
2017-08-21 21:34:49 +00:00
|
|
|
- META turn TODOs into GitHub issues
|
2017-07-24 20:36:23 +00:00
|
|
|
- generic publisher / sources
|
2017-08-18 17:55:04 +00:00
|
|
|
- dispatch scopes
|
2017-08-21 21:20:31 +00:00
|
|
|
- feature flags
|
2017-09-20 17:13:44 +00:00
|
|
|
- derive stats
|
2017-09-28 22:17:16 +00:00
|
|
|
- time measurement units in metric kind (us, ms, etc.) for naming & scaling
|
2017-08-21 19:31:52 +00:00
|
|
|
- heartbeat metric on publish
|
2017-08-21 21:34:49 +00:00
|
|
|
- logger templates
|
2017-08-18 17:55:04 +00:00
|
|
|
- configurable aggregation
|
|
|
|
- non-aggregating buffers
|
2017-09-25 10:43:04 +00:00
|
|
|
- framework glue (rocket, iron, gotham, indicatif, etc.)
|
|
|
|
- more tests & benchmarks
|
2017-08-21 21:34:49 +00:00
|
|
|
- complete doc / inline samples
|
2017-09-25 10:43:04 +00:00
|
|
|
- more example apps
|
|
|
|
- A cool logo
|
|
|
|
- method annotation processors `#[timer("name")]`
|
2017-09-27 15:01:43 +00:00
|
|
|
- fastsinks (M / &M) vs. safesinks (Arc<M>)
|
2017-09-29 20:08:28 +00:00
|
|
|
- `static_metric!` macro to replace `lazy_static!` blocks and handle generics boilerplate.
|