#metrics #statsd #graphite #timer #monitoring

dipstick

A fast and modular metrics library decoupling app instrumentation from reporting backend. Similar to popular logging frameworks, but with counters and timers. Can be configured for combined outputs (log + statsd), random sampling, local aggregation of metrics, recurrent background publication, etc

40 releases

0.6.11 Jun 8, 2018
0.6.7 Apr 27, 2018
0.6.5 Jan 16, 2018
0.5.1 Dec 13, 2017
0.4.11 Nov 29, 2017

#6 in Visualization

Download history 92/week @ 2018-05-27 161/week @ 2018-06-03 61/week @ 2018-06-10 148/week @ 2018-06-17 176/week @ 2018-06-24 487/week @ 2018-07-01 153/week @ 2018-07-08 168/week @ 2018-07-15 313/week @ 2018-07-22 282/week @ 2018-07-29 771/week @ 2018-08-05 518/week @ 2018-08-12 206/week @ 2018-08-19

361 downloads per month

MIT/Apache

96KB
2K SLoC

dipstick

A quick, modular metrics toolkit for Rust applications of all types. Similar to popular logging frameworks, but with counters, markers, gauges and timers.

Build Status crates.io

Dipstick's main attraction is the ability to send metrics to multiple customized outputs. For example, metrics could be written immediately to the log and sent over the network after a period of aggregation.

Dipstick promotes structured metrics for clean, safe code and good performance.

Dipstick builds on stable Rust with minimal dependencies.

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

Examples

For complete applications see the examples.

To use Dipstick in your project, add the following line to your Cargo.toml in the [dependencies] section:

dipstick = "0.6.10"

Then add it to your code:

let metrics = app_metrics(to_graphite("host.com:2003")?);
let counter = metrics.counter("my_counter");
counter.count(3);

Send metrics to multiple outputs:

let _app_metrics = app_metrics((
        to_stdout(), 
        to_statsd("localhost:8125")?.with_namespace(&["my", "app"])
    ));

Since instruments are decoupled from the backend, outputs can be swapped easily.

Aggregate metrics and schedule to be periodical publication in the background:

use std::time::Duration;

let app_metrics = app_metrics(aggregate(all_stats, to_stdout()));
app_metrics.flush_every(Duration::from_secs(3));

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, provide your own strategy:

app_metrics(aggregate(
    |_kind, name, score|
        match score {
            ScoreType::Count(count) => 
                Some((Kind::Counter, vec![name, ".per_thousand"], count / 1000)),
            _ => None
        },
    to_log()));

Apply statistical sampling to metrics:

let _app_metrics = app_metrics(to_statsd("server:8125")?.with_sampling_rate(0.01));

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 = app_metrics(to_stdout()).with_async_queue(64);

The async queue uses a Rust channel and a standalone thread. The current behavior is to block when full.

For better performance and easy maintenance, metrics should usually be predefined:

#[macro_use] extern crate dipstick;
#[macro_use] extern crate lazy_static;
use dipstick::*;

app_metrics!(String, APP_METRICS = app_metrics(to_stdout()));
app_counter!(String, APP_METRICS, {
    COUNTER_A: "counter_a",
});

fn main() {
    COUNTER_A.count(11);
}

Metric definition macros are just lazy_static! wrappers.

Where necessary, metrics can be defined ad-hoc:

let user_name = "john_day";
let app_metrics = app_metrics(to_log().with_cache(512));
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.

Timers can be used multiple ways:

let app_metrics = app_metrics(to_stdout());
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);

timer.interval_us(123_456);

Related metrics can share a namespace:

let app_metrics = app_metrics(to_stdout());
let db_metrics = app_metrics.with_prefix("database");
let _db_timer = db_metrics.timer("db_timer");
let _db_counter = db_metrics.counter("db_counter");

License

Dipstick is licensed under the terms of the Apache 2.0 and MIT license.

Dependencies

~2MB
~33K SLoC

  • build build.rs