forked from tikv/rust-prometheus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Provide Static Metric Macros (tikv#166)
- Loading branch information
1 parent
2807b5f
commit 91ac05f
Showing
13 changed files
with
913 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
[package] | ||
name = "prometheus-static-metric" | ||
version = "0.1.0" | ||
license = "Apache-2.0" | ||
authors = ["me@breeswish.org"] | ||
description = "Static metric helper utilities for rust-prometheus." | ||
repository = "https://github.com/pingcap/rust-prometheus" | ||
homepage = "https://github.com/pingcap/rust-prometheus" | ||
documentation = "https://docs.rs/prometheus-static-metric" | ||
include = [ | ||
"LICENSE", | ||
"Cargo.toml", | ||
"src/**/*.rs", | ||
] | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
syn = { version = "0.12", features = ["full", "extra-traits"] } | ||
quote = "0.4" | ||
lazy_static = "1.0" | ||
|
||
[dev-dependencies] | ||
prometheus = { path = ".." } | ||
|
||
[features] | ||
default = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
ENABLE_FEATURES ?= default | ||
|
||
all: format build test examples | ||
|
||
build: | ||
cargo build --features="${ENABLE_FEATURES}" | ||
|
||
test: | ||
cargo test --features="${ENABLE_FEATURES}" -- --nocapture | ||
|
||
format: | ||
@cargo fmt --all -- --write-mode diff >/dev/null || cargo fmt --all | ||
|
||
bench: format | ||
RUSTFLAGS="--cfg bench" cargo bench --features="${ENABLE_FEATURES}" -- --nocapture | ||
|
||
clean: | ||
cargo clean | ||
|
||
examples: | ||
cargo build --example simple | ||
cargo build --example with_lazy_static | ||
cargo build --example advanced | ||
|
||
.PHONY: all |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# prometheus-static-metric | ||
|
||
[![docs.rs](https://docs.rs/prometheus-static-metric/badge.svg)](https://docs.rs/prometheus-static-metric) | ||
[![crates.io](http://meritbadge.herokuapp.com/prometheus-static-metric)](https://crates.io/crates/prometheus-static-metric) | ||
|
||
Utility macro to build static metrics for the [rust-prometheus](https://github.com/pingcap/rust-prometheus) library. | ||
|
||
## Why? | ||
|
||
`MetricVec` (i.e. `CounterVec`, `GaugeVec` or `HistogramVec`) is slow. However if every possible values for labels are | ||
known, each metric in the `MetricVec` can be cached to avoid the runtime cost. | ||
|
||
For example, the following code can be slow when it is invoked multiple times: | ||
|
||
```rust | ||
some_counter_vec.with_label_values(&["label_1_foo", "label_2_bar"]).inc(); | ||
``` | ||
|
||
It is because we are retriving a specific `Counter` according to values each time and to ensure thread-safety there is | ||
a lock inside which makes things worse. | ||
|
||
We can optimize it by caching the counter by label values: | ||
|
||
```rust | ||
// init before hand | ||
let foo_bar_counter = some_counter.with_label_values(&["label_1_foo", "label_2_bar"]); | ||
|
||
foo_bar_counter.inc(); | ||
``` | ||
|
||
So far everything seems good. We achieve the same performance as `Counter` for `CounterVec`. But what if there are many | ||
labels and each of them has many values? We need to hand-craft a lot of code in this way. | ||
|
||
That's what this crate solves. This crate provides a macro that helps you do the optimization above without really | ||
introducing a lot of templating code. | ||
|
||
## Getting Started | ||
|
||
+ Add to `Cargo.toml`: | ||
|
||
```toml | ||
[dependencies] | ||
prometheus-static-metric = "0.1" | ||
``` | ||
|
||
+ Add to `lib.rs`: | ||
|
||
```rust | ||
#![feature(proc_macro)] | ||
|
||
extern crate prometheus_static_metric; | ||
``` | ||
|
||
## Example | ||
|
||
Use the `make_static_metric!` to define all possible values for each label. Your definition will be expanded to a real | ||
`struct` for easy access while keeping high-performance. | ||
|
||
```rust | ||
use prometheus_static_metric::make_static_metric; | ||
|
||
make_static_metric! { | ||
pub struct MyStaticCounterVec: Counter { | ||
"method" => { | ||
post, | ||
get, | ||
put, | ||
delete, | ||
}, | ||
"product" => { | ||
foo, | ||
bar, | ||
}, | ||
} | ||
} | ||
|
||
fn main() { | ||
let vec = CounterVec::new(Opts::new("foo", "bar"), &["method", "product"]).unwrap(); | ||
let static_counter_vec = MyStaticCounterVec::from(&vec); | ||
|
||
static_counter_vec.post.foo.inc(); | ||
static_counter_vec.delete.bar.inc_by(4.0); | ||
assert_eq!(static_counter_vec.post.bar.get(), 0.0); | ||
assert_eq!(vec.with_label_values(&["post", "foo"]).get(), 1.0); | ||
assert_eq!(vec.with_label_values(&["delete", "bar"]).get(), 4.0); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
// Copyright 2018 PingCAP, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#![feature(test)] | ||
#![feature(proc_macro)] | ||
|
||
extern crate prometheus; | ||
extern crate prometheus_static_metric; | ||
extern crate test; | ||
|
||
use prometheus::{IntCounter, IntCounterVec, Opts}; | ||
use prometheus_static_metric::make_static_metric; | ||
use test::Bencher; | ||
|
||
#[bench] | ||
/// Single `IntCounter` performance. | ||
fn bench_single_counter(b: &mut Bencher) { | ||
let counter = IntCounter::new("foo", "bar").unwrap(); | ||
b.iter(|| counter.inc()); | ||
} | ||
|
||
#[bench] | ||
/// `IntCounterVec` performance. | ||
fn bench_counter_vec(b: &mut Bencher) { | ||
let counter_vec = IntCounterVec::new(Opts::new("foo", "bar"), &["d1", "d2"]).unwrap(); | ||
b.iter(|| counter_vec.with_label_values(&["foo", "bar"]).inc()); | ||
} | ||
|
||
#[bench] | ||
/// Manually implemented static metrics performance, metrics are placed outside a struct. | ||
fn bench_static_metrics_handwrite_1(b: &mut Bencher) { | ||
let counter_vec = IntCounterVec::new(Opts::new("foo", "bar"), &["d1", "d2"]).unwrap(); | ||
let counter = counter_vec.with_label_values(&["foo", "bar"]); | ||
b.iter(|| counter.inc()); | ||
} | ||
|
||
#[bench] | ||
/// Manually implemented static metrics performance, metrics are placed nested inside a struct. | ||
fn bench_static_metrics_handwrite_2(b: &mut Bencher) { | ||
let counter_vec = IntCounterVec::new(Opts::new("foo", "bar"), &["d1", "d2"]).unwrap(); | ||
struct StaticCounter1 { | ||
foo: StaticCounter1Field2, | ||
} | ||
struct StaticCounter1Field2 { | ||
bar: IntCounter, | ||
} | ||
let static_counter = StaticCounter1 { | ||
foo: StaticCounter1Field2 { | ||
bar: counter_vec.with_label_values(&["foo", "bar"]), | ||
}, | ||
}; | ||
b.iter(|| static_counter.foo.bar.inc()); | ||
} | ||
|
||
make_static_metric! { | ||
struct StaticCounter2: IntCounter { | ||
"d1" => { | ||
foo, | ||
}, | ||
"d2" => { | ||
bar, | ||
}, | ||
} | ||
} | ||
|
||
#[bench] | ||
/// macro implemented static metrics performance. | ||
fn bench_static_metrics_macro(b: &mut Bencher) { | ||
let counter_vec = IntCounterVec::new(Opts::new("foo", "bar"), &["d1", "d2"]).unwrap(); | ||
let static_counter = StaticCounter2::from(&counter_vec); | ||
b.iter(|| static_counter.foo.bar.inc()); | ||
} | ||
|
||
#[bench] | ||
/// macro implemented static metrics performance, with dynamic lookup. | ||
fn bench_static_metrics_macro_with_lookup(b: &mut Bencher) { | ||
let counter_vec = IntCounterVec::new(Opts::new("foo", "bar"), &["d1", "d2"]).unwrap(); | ||
let static_counter = StaticCounter2::from(&counter_vec); | ||
b.iter(|| static_counter.get("foo").get("bar").inc()); | ||
} | ||
|
||
make_static_metric! { | ||
struct StaticCounter3: IntCounter { | ||
"d1" => { val1 }, | ||
"d2" => { val2 }, | ||
"d3" => { val3 }, | ||
"d4" => { val4 }, | ||
"d5" => { val5 }, | ||
"d6" => { val6 }, | ||
"d7" => { val7 }, | ||
"d8" => { val8 }, | ||
"d9" => { val9 }, | ||
"d10" => { val10 }, | ||
} | ||
} | ||
|
||
#[bench] | ||
/// macro implemented static metrics performance, with a deep nesting level. | ||
fn bench_static_metrics_macro_deep(b: &mut Bencher) { | ||
let counter_vec = IntCounterVec::new( | ||
Opts::new("foo", "bar"), | ||
&["d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10"], | ||
).unwrap(); | ||
let static_counter = StaticCounter3::from(&counter_vec); | ||
b.iter(|| { | ||
static_counter | ||
.val1 | ||
.val2 | ||
.val3 | ||
.val4 | ||
.val5 | ||
.val6 | ||
.val7 | ||
.val8 | ||
.val9 | ||
.val10 | ||
.inc() | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// Copyright 2018 PingCAP, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#![feature(proc_macro)] | ||
|
||
#[macro_use] | ||
extern crate lazy_static; | ||
#[macro_use] | ||
extern crate prometheus; | ||
extern crate prometheus_static_metric; | ||
|
||
use prometheus::IntCounterVec; | ||
use prometheus_static_metric::make_static_metric; | ||
|
||
make_static_metric! { | ||
pub struct HttpRequestStatistics: IntCounter { | ||
"method" => { | ||
post, | ||
get, | ||
put, | ||
delete, | ||
}, | ||
"version" => { | ||
http1: "HTTP/1", | ||
http2: "HTTP/2", | ||
}, | ||
"product" => { | ||
foo, | ||
bar, | ||
}, | ||
} | ||
} | ||
|
||
lazy_static! { | ||
pub static ref HTTP_COUNTER_VEC: IntCounterVec = | ||
register_int_counter_vec!( | ||
"http_requests", | ||
"Total number of HTTP requests.", | ||
&["product", "method", "version"] // it doesn't matter for the label order | ||
).unwrap(); | ||
|
||
pub static ref HTTP_COUNTER: HttpRequestStatistics = HttpRequestStatistics | ||
::from(&HTTP_COUNTER_VEC); | ||
} | ||
|
||
/// This example demonstrates the usage of: | ||
/// 1. using alternative metric types (i.e. IntCounter) | ||
/// 2. specifying different label order compared to the definition | ||
/// 3. using non-identifiers as values | ||
fn main() { | ||
HTTP_COUNTER.post.http1.foo.inc_by(4); | ||
assert_eq!( | ||
HTTP_COUNTER_VEC | ||
.with_label_values(&["foo", "post", "HTTP/1"]) | ||
.get(), | ||
4 | ||
); | ||
|
||
// Note: You cannot specify values other than the definition in `get()` because | ||
// it is purely static. | ||
HTTP_COUNTER | ||
.get("delete") | ||
.unwrap() | ||
.get("HTTP/1") | ||
.unwrap() | ||
.foo | ||
.inc_by(7); | ||
assert_eq!( | ||
HTTP_COUNTER_VEC | ||
.with_label_values(&["foo", "delete", "HTTP/1"]) | ||
.get(), | ||
7 | ||
); | ||
} |
Oops, something went wrong.