2018-01-08 05:32:32 +00:00
|
|
|
# dipstick
|
|
|
|
A quick, modular metrics toolkit for Rust applications of all types. Similar to popular logging frameworks,
|
2017-12-08 03:16:26 +00:00
|
|
|
but with counters, markers, gauges and timers.
|
2017-08-19 18:09:17 +00:00
|
|
|
|
2018-01-08 05:32:32 +00:00
|
|
|
[![Build Status](https://travis-ci.org/fralalonde/dipstick.svg?branch=master)](https://travis-ci.org/fralalonde/dipstick)
|
|
|
|
[![crates.io](https://img.shields.io/crates/v/dipstick.svg)](https://crates.io/crates/dipstick)
|
|
|
|
|
2017-12-13 19:58:42 +00:00
|
|
|
Dipstick's main attraction is the ability to send metrics to multiple customized outputs.
|
2018-01-08 05:32:32 +00:00
|
|
|
For example, metrics could be written immediately to the log _and_
|
2017-12-13 19:58:42 +00:00
|
|
|
sent over the network after a period of aggregation.
|
|
|
|
|
2018-01-08 05:32:32 +00:00
|
|
|
Dipstick promotes structured metrics for clean, safe code and good performance.
|
|
|
|
|
|
|
|
Dipstick builds on stable Rust with minimal dependencies.
|
2017-06-29 21:52:37 +00:00
|
|
|
|
2017-12-07 21:23:47 +00:00
|
|
|
## Features
|
2017-09-29 20:45:09 +00:00
|
|
|
|
2017-12-07 21:23:47 +00:00
|
|
|
- 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
|
2018-01-08 05:32:32 +00:00
|
|
|
|
|
|
|
## Examples
|
2017-09-29 20:45:09 +00:00
|
|
|
|
2018-01-08 05:32:32 +00:00
|
|
|
For complete applications see the [examples](https://github.com/fralalonde/dipstick/tree/master/examples).
|
2017-07-24 20:36:23 +00:00
|
|
|
|
2018-01-08 05:32:32 +00:00
|
|
|
To use Dipstick in your project, add the following line to your `Cargo.toml`
|
|
|
|
in the `[dependencies]` section:
|
|
|
|
|
|
|
|
```toml
|
2018-01-17 17:37:38 +00:00
|
|
|
dipstick = "0.6.5"
|
2017-09-28 22:17:16 +00:00
|
|
|
```
|
2017-09-02 05:16:39 +00:00
|
|
|
|
2018-01-08 05:32:32 +00:00
|
|
|
Then add it to your code:
|
|
|
|
|
|
|
|
```rust,skt-fail,no_run
|
2018-03-29 18:22:43 +00:00
|
|
|
let metrics = metric_scope(to_graphite("host.com:2003")?);
|
2018-01-08 05:32:32 +00:00
|
|
|
let counter = metrics.counter("my_counter");
|
|
|
|
counter.count(3);
|
|
|
|
```
|
|
|
|
|
|
|
|
Send metrics to multiple outputs:
|
|
|
|
|
|
|
|
```rust,skt-fail,no_run
|
2018-03-29 18:22:43 +00:00
|
|
|
let _app_metrics = metric_scope((
|
2018-01-08 05:32:32 +00:00
|
|
|
to_stdout(),
|
|
|
|
to_statsd("localhost:8125")?.with_namespace(&["my", "app"])
|
|
|
|
));
|
2017-09-28 22:17:16 +00:00
|
|
|
```
|
2017-09-29 20:45:09 +00:00
|
|
|
Since instruments are decoupled from the backend, outputs can be swapped easily.
|
|
|
|
|
2018-01-08 05:32:32 +00:00
|
|
|
Aggregate metrics and schedule to be periodical publication in the background:
|
|
|
|
```rust,skt-run
|
2017-09-28 22:17:16 +00:00
|
|
|
use std::time::Duration;
|
2018-01-08 05:32:32 +00:00
|
|
|
|
2018-03-29 18:22:43 +00:00
|
|
|
let app_metrics = metric_scope(aggregate());
|
2018-03-27 20:30:12 +00:00
|
|
|
route_aggregate_metrics(to_stdout());
|
2018-01-08 05:32:32 +00:00
|
|
|
app_metrics.flush_every(Duration::from_secs(3));
|
2017-09-28 22:17:16 +00:00
|
|
|
```
|
2018-01-08 05:32:32 +00:00
|
|
|
|
2017-09-29 20:45:09 +00:00
|
|
|
Aggregation is performed locklessly and is very fast.
|
2017-09-29 18:41:22 +00:00
|
|
|
Count, sum, min, max and average are tracked where they make sense.
|
2017-10-10 14:11:42 +00:00
|
|
|
Published statistics can be selected with presets such as `all_stats` (see previous example),
|
|
|
|
`summary`, `average`.
|
2017-09-29 18:41:22 +00:00
|
|
|
|
2018-01-08 05:32:32 +00:00
|
|
|
For more control over published statistics, provide your own strategy:
|
|
|
|
```rust,skt-run
|
2018-03-27 20:30:12 +00:00
|
|
|
metrics(aggregate());
|
|
|
|
set_default_aggregate_fn(|_kind, name, score|
|
|
|
|
match score {
|
|
|
|
ScoreType::Count(count) =>
|
|
|
|
Some((Kind::Counter, vec![name, ".per_thousand"], count / 1000)),
|
|
|
|
_ => None
|
|
|
|
});
|
2017-09-29 20:45:09 +00:00
|
|
|
```
|
2017-09-05 04:24:43 +00:00
|
|
|
|
2018-01-08 05:32:32 +00:00
|
|
|
Apply statistical sampling to metrics:
|
|
|
|
```rust,skt-fail
|
2018-03-29 18:22:43 +00:00
|
|
|
let _app_metrics = metric_scope(to_statsd("server:8125")?.with_sampling_rate(0.01));
|
2017-09-02 05:16:39 +00:00
|
|
|
```
|
2017-09-29 20:45:09 +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-10-10 14:11:42 +00:00
|
|
|
Metrics can be recorded asynchronously:
|
2018-01-08 05:32:32 +00:00
|
|
|
```rust,skt-run
|
2018-03-29 18:22:43 +00:00
|
|
|
let _app_metrics = metric_scope(to_stdout().with_async_queue(64));
|
2017-09-28 22:17:16 +00:00
|
|
|
```
|
2017-10-10 14:11:42 +00:00
|
|
|
The async queue uses a Rust channel and a standalone thread.
|
|
|
|
The current behavior is to block when full.
|
2017-09-28 22:17:16 +00:00
|
|
|
|
2018-01-17 16:24:25 +00:00
|
|
|
For speed and easier maintenance, metrics are usually defined statically:
|
2018-01-08 05:32:32 +00:00
|
|
|
```rust,skt-plain
|
2018-01-16 20:00:47 +00:00
|
|
|
#[macro_use] extern crate dipstick;
|
2018-01-08 05:32:32 +00:00
|
|
|
#[macro_use] extern crate lazy_static;
|
|
|
|
use dipstick::*;
|
2017-09-29 20:45:09 +00:00
|
|
|
|
2018-03-29 18:22:43 +00:00
|
|
|
dispatch_metrics!("my_app" => {
|
2018-03-13 13:54:12 +00:00
|
|
|
@Counter COUNTER_A: "counter_a";
|
2018-01-16 20:00:47 +00:00
|
|
|
});
|
2018-01-08 05:32:32 +00:00
|
|
|
|
|
|
|
fn main() {
|
2018-03-27 20:30:12 +00:00
|
|
|
route_aggregate_metrics(to_stdout());
|
2018-01-08 05:32:32 +00:00
|
|
|
COUNTER_A.count(11);
|
2017-09-29 20:45:09 +00:00
|
|
|
}
|
|
|
|
```
|
2018-01-16 20:00:47 +00:00
|
|
|
Metric definition macros are just `lazy_static!` wrappers.
|
|
|
|
|
|
|
|
|
|
|
|
Where necessary, metrics can be defined _ad-hoc_:
|
|
|
|
```rust,skt-run
|
|
|
|
let user_name = "john_day";
|
2018-03-29 18:22:43 +00:00
|
|
|
let app_metrics = metric_scope(to_log()).with_cache(512);
|
2018-01-16 20:00:47 +00:00
|
|
|
app_metrics.gauge(format!("gauge_for_user_{}", user_name)).value(44);
|
|
|
|
```
|
|
|
|
Defining a cache is optional but will speed up re-definition of common ad-hoc metrics.
|
2017-09-02 05:16:39 +00:00
|
|
|
|
2017-10-10 14:11:42 +00:00
|
|
|
Timers can be used multiple ways:
|
2018-01-08 05:32:32 +00:00
|
|
|
```rust,skt-run
|
2018-03-29 18:22:43 +00:00
|
|
|
let app_metrics = metric_scope(to_stdout());
|
2017-09-28 22:17:16 +00:00
|
|
|
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-10-10 14:11:42 +00:00
|
|
|
Related metrics can share a namespace:
|
2018-01-08 05:32:32 +00:00
|
|
|
```rust,skt-run
|
2018-03-29 18:22:43 +00:00
|
|
|
let app_metrics = metric_scope(to_stdout());
|
2018-06-21 15:52:10 +00:00
|
|
|
let db_metrics = app_metrics.add_prefix("database");
|
2018-01-08 05:32:32 +00:00
|
|
|
let _db_timer = db_metrics.timer("db_timer");
|
|
|
|
let _db_counter = db_metrics.counter("db_counter");
|
2017-09-28 22:17:16 +00:00
|
|
|
```
|
|
|
|
|
2018-01-08 05:32:32 +00:00
|
|
|
## License
|
|
|
|
|
|
|
|
Dipstick is licensed under the terms of the Apache 2.0 and MIT license.
|
2017-09-29 20:45:09 +00:00
|
|
|
|