Compare commits
34 Commits
Author | SHA1 | Date |
---|---|---|
|
1d3174bf5d | |
|
53d306635f | |
|
e07efb4f37 | |
|
26e46ec03a | |
|
e17c5ced2b | |
|
e5809b7ab9 | |
|
4a0e282888 | |
|
c3865f3c40 | |
|
7e4e6f2d33 | |
|
5b62f4b78b | |
|
52d76fc2d8 | |
|
3bd0e82f1f | |
|
45c43c9130 | |
|
f0c9bc271b | |
|
04fce2f3bf | |
|
6e435db331 | |
|
439e3b8c14 | |
|
bf696d642c | |
|
b7e874524f | |
|
f49c724df0 | |
|
76a634587a | |
|
a72d8d7db8 | |
|
7a9adcacd0 | |
|
6e81890773 | |
|
36acf4753d | |
|
a7b8f09b8f | |
|
8b462b1945 | |
|
422b3afcf4 | |
|
c092b95879 | |
|
a11df02f27 | |
|
fc591fdee0 | |
|
e38ac6b4f2 | |
|
4e552eede7 | |
|
5ca37fe4fe |
|
@ -9,7 +9,9 @@ on:
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
# Pinned toolchain for linting and benchmarks
|
# Pinned toolchain for linting and benchmarks
|
||||||
ACTIONS_LINTS_TOOLCHAIN: 1.53.0
|
ACTIONS_LINTS_TOOLCHAIN: 1.81.0
|
||||||
|
# Minimum supported Rust version (MSRV)
|
||||||
|
ACTION_MSRV_TOOLCHAIN: 1.81.0
|
||||||
EXTRA_FEATURES: "protobuf push process"
|
EXTRA_FEATURES: "protobuf push process"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
@ -18,16 +20,15 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: Install toolchain
|
- name: Install toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
|
||||||
toolchain: "stable"
|
|
||||||
default: true
|
|
||||||
- name: cargo build
|
- name: cargo build
|
||||||
run: cargo build
|
run: cargo build
|
||||||
- name: cargo test
|
- name: cargo test
|
||||||
run: cargo test
|
run: cargo test
|
||||||
|
- name: cargo test (no default features)
|
||||||
|
run: cargo test --no-default-features
|
||||||
- name: cargo test (extra features)
|
- name: cargo test (extra features)
|
||||||
run: cargo test --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
|
run: cargo test --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
|
||||||
- name: cargo package
|
- name: cargo package
|
||||||
|
@ -43,12 +44,11 @@ jobs:
|
||||||
- "nightly"
|
- "nightly"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: Install toolchain
|
- name: Install toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.channel }}
|
toolchain: ${{ matrix.channel }}
|
||||||
default: true
|
|
||||||
- name: cargo build
|
- name: cargo build
|
||||||
run: cargo build
|
run: cargo build
|
||||||
- name: cargo test
|
- name: cargo test
|
||||||
|
@ -57,33 +57,51 @@ jobs:
|
||||||
run: cargo build -p prometheus-static-metric --examples --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
|
run: cargo build -p prometheus-static-metric --examples --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
|
||||||
- name: cargo test (static-metric)
|
- name: cargo test (static-metric)
|
||||||
run: cargo test -p prometheus-static-metric --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
|
run: cargo test -p prometheus-static-metric --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
|
||||||
|
build-msrv:
|
||||||
|
name: "Build, minimum toolchain"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: ${{ env['ACTION_MSRV_TOOLCHAIN'] }}
|
||||||
|
- run: cargo build
|
||||||
|
- run: cargo test --no-run
|
||||||
|
- run: cargo build --no-default-features
|
||||||
|
- run: cargo test --no-default-features --no-run
|
||||||
|
- run: cargo build --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
|
||||||
|
- run: cargo test --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
|
||||||
linting:
|
linting:
|
||||||
name: "Lints, pinned toolchain"
|
name: "Lints, pinned toolchain"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: Install toolchain
|
- name: Install toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env['ACTIONS_LINTS_TOOLCHAIN'] }}
|
toolchain: ${{ env['ACTIONS_LINTS_TOOLCHAIN'] }}
|
||||||
default: true
|
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
- name: cargo fmt (check)
|
- name: cargo fmt (check)
|
||||||
run: cargo fmt --all -- --check -l
|
run: cargo fmt --all -- --check -l
|
||||||
- name: cargo clippy
|
- name: cargo clippy
|
||||||
run: cargo clippy --all -- -D clippy::all
|
run: cargo clippy --all
|
||||||
|
- name: cargo clippy (no default features)
|
||||||
|
run: cargo clippy --all --no-default-features
|
||||||
|
- name: cargo clippy (extra features)
|
||||||
|
run: cargo clippy --all --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
|
||||||
criterion:
|
criterion:
|
||||||
name: "Benchmarks (criterion)"
|
name: "Benchmarks (criterion)"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: Install toolchain
|
- name: Install toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env['ACTIONS_LINTS_TOOLCHAIN'] }}
|
toolchain: ${{ env['ACTIONS_LINTS_TOOLCHAIN'] }}
|
||||||
default: true
|
|
||||||
- name: cargo bench (prometheus)
|
- name: cargo bench (prometheus)
|
||||||
run: cargo bench -p prometheus
|
run: cargo bench -p prometheus
|
||||||
- name: cargo bench (prometheus-static-metric)
|
- name: cargo bench (prometheus-static-metric)
|
||||||
|
|
48
CHANGELOG.md
48
CHANGELOG.md
|
@ -1,5 +1,53 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.14.0
|
||||||
|
|
||||||
|
- API change: Use `AsRef<str>` for owned label values (#537)
|
||||||
|
|
||||||
|
- Improvement: Hashing improvements (#532)
|
||||||
|
|
||||||
|
- Dependency upgrade: Update `hyper` to 1.6 (#524)
|
||||||
|
|
||||||
|
- Dependency upgrade: Update `procfs` to 0.17 (#543)
|
||||||
|
|
||||||
|
- Dependency upgrade: Update `protobuf` to 3.7.2 for RUSTSEC-2024-0437 (#541)
|
||||||
|
|
||||||
|
- Dependency upgrade: Update `thiserror` to 2.0 (#534)
|
||||||
|
|
||||||
|
- Internal change: Fix LSP and Clippy warnings (#540)
|
||||||
|
|
||||||
|
- Internal change: Bump MSRV to 1.81 (#539)
|
||||||
|
|
||||||
|
- Documentation: Fix `register_histogram_vec_with_registry` docstring (#528)
|
||||||
|
|
||||||
|
- Documentation: Fix typos in static-metric docstrings (#479)
|
||||||
|
|
||||||
|
- Documentation: Add missing `protobuf` feature to README list (#531)
|
||||||
|
|
||||||
|
## 0.13.4
|
||||||
|
|
||||||
|
- Improvement: Add PullingGauge (#405)
|
||||||
|
|
||||||
|
- Improvement: Let cargo know which example requires which features (#511)
|
||||||
|
|
||||||
|
- Bug fix: Prevent `clippy::ignored_unit_patterns` in macro expansions (#497)
|
||||||
|
|
||||||
|
- Internal change: Add CI job for minimum toolchain (MSRV) (#467)
|
||||||
|
|
||||||
|
- Internal change: Update CI to `actions/checkout@v4` (#499)
|
||||||
|
|
||||||
|
- Internal change: Update dependencies
|
||||||
|
|
||||||
|
## 0.13.3
|
||||||
|
|
||||||
|
- Bug fix: Prevent ProcessCollector underflow with CPU time counter (#465)
|
||||||
|
|
||||||
|
- Internal change: Update dependencies
|
||||||
|
|
||||||
|
## 0.13.2
|
||||||
|
|
||||||
|
- Bug fix: Fix compilation on 32-bit targets (#446)
|
||||||
|
|
||||||
## 0.13.1
|
## 0.13.1
|
||||||
|
|
||||||
- Improvement: ProcessCollector use IntGauge to provide better performance (#430)
|
- Improvement: ProcessCollector use IntGauge to provide better performance (#430)
|
||||||
|
|
33
Cargo.toml
33
Cargo.toml
|
@ -9,17 +9,15 @@ license = "Apache-2.0"
|
||||||
name = "prometheus"
|
name = "prometheus"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/tikv/rust-prometheus"
|
repository = "https://github.com/tikv/rust-prometheus"
|
||||||
version = "0.13.1"
|
rust-version = "1.81"
|
||||||
|
version = "0.14.0"
|
||||||
[badges]
|
|
||||||
travis-ci = { repository = "pingcap/rust-prometheus" }
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["nightly"]
|
features = ["nightly"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["protobuf"]
|
default = ["protobuf"]
|
||||||
gen = ["protobuf-codegen-pure"]
|
gen = ["protobuf-codegen"]
|
||||||
nightly = ["libc"]
|
nightly = ["libc"]
|
||||||
process = ["libc", "procfs"]
|
process = ["libc", "procfs"]
|
||||||
push = ["reqwest", "libc", "protobuf"]
|
push = ["reqwest", "libc", "protobuf"]
|
||||||
|
@ -30,22 +28,23 @@ fnv = "^1.0"
|
||||||
lazy_static = "^1.4"
|
lazy_static = "^1.4"
|
||||||
libc = { version = "^0.2", optional = true }
|
libc = { version = "^0.2", optional = true }
|
||||||
parking_lot = "^0.12"
|
parking_lot = "^0.12"
|
||||||
protobuf = { version = "^2.0", optional = true }
|
protobuf = { version = "^3.7.2", optional = true }
|
||||||
memchr = "^2.3"
|
memchr = "^2.3"
|
||||||
reqwest = { version = "^0.11", features = ["blocking"], optional = true }
|
reqwest = { version = "^0.12", features = ["blocking"], optional = true }
|
||||||
thiserror = "^1.0"
|
thiserror = "^2.0"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
procfs = { version = "^0.12", optional = true, default-features = false }
|
procfs = { version = "^0.17", optional = true, default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.5"
|
||||||
getopts = "^0.2"
|
getopts = "^0.2"
|
||||||
hyper = { version = "^0.14", features = ["server", "http1", "tcp"] }
|
hyper = { version = "^1.6", features = ["http1", "server"] }
|
||||||
tokio = { version = "^1.0", features = ["macros", "rt-multi-thread"] }
|
hyper-util = { version = "^0.1", features = ["http1", "server", "tokio"] }
|
||||||
|
tokio = { version = "^1.0", features = ["macros", "net", "rt-multi-thread"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
protobuf-codegen-pure = { version = "^2.0", optional = true }
|
protobuf-codegen = { version = "^3.7.2", optional = true }
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["static-metric"]
|
members = ["static-metric"]
|
||||||
|
@ -73,3 +72,11 @@ harness = false
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "text_encoder"
|
name = "text_encoder"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "example_push"
|
||||||
|
required-features = ["push"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "example_process_collector"
|
||||||
|
required-features = ["process"]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Prometheus Rust client library
|
# Prometheus Rust client library
|
||||||
|
|
||||||
[](https://travis-ci.org/pingcap/rust-prometheus)
|
[](https://github.com/tikv/rust-prometheus/actions/workflows/rust.yml)
|
||||||
[](https://docs.rs/prometheus)
|
[](https://docs.rs/prometheus)
|
||||||
[](https://crates.io/crates/prometheus)
|
[](https://crates.io/crates/prometheus)
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ Find the latest documentation at <https://docs.rs/prometheus>.
|
||||||
|
|
||||||
This crate provides several optional components which can be enabled via [Cargo `[features]`](https://doc.rust-lang.org/cargo/reference/features.html):
|
This crate provides several optional components which can be enabled via [Cargo `[features]`](https://doc.rust-lang.org/cargo/reference/features.html):
|
||||||
|
|
||||||
|
- `protobuf`: Protobuf support, enabled by default.
|
||||||
|
|
||||||
- `gen`: To generate protobuf client with the latest protobuf version instead of
|
- `gen`: To generate protobuf client with the latest protobuf version instead of
|
||||||
using the pre-generated client.
|
using the pre-generated client.
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
use fnv::FnvBuildHasher;
|
||||||
use prometheus::{Counter, CounterVec, IntCounter, Opts};
|
use prometheus::{Counter, CounterVec, IntCounter, Opts};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{atomic, Arc};
|
use std::sync::{atomic, Arc};
|
||||||
|
@ -24,7 +25,11 @@ fn bench_counter_with_label_values(c: &mut Criterion) {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
c.bench_function("counter_with_label_values", |b| {
|
c.bench_function("counter_with_label_values", |b| {
|
||||||
b.iter(|| counter.with_label_values(&["eins", "zwei", "drei"]).inc())
|
b.iter(|| {
|
||||||
|
counter
|
||||||
|
.with_label_values(&black_box(["eins", "zwei", "drei"]))
|
||||||
|
.inc()
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +46,25 @@ fn bench_counter_with_mapped_labels(c: &mut Criterion) {
|
||||||
labels.insert("two", "zwei");
|
labels.insert("two", "zwei");
|
||||||
labels.insert("one", "eins");
|
labels.insert("one", "eins");
|
||||||
labels.insert("three", "drei");
|
labels.insert("three", "drei");
|
||||||
counter.with(&labels).inc();
|
counter.with(&black_box(labels)).inc();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_counter_with_mapped_labels_fnv(c: &mut Criterion) {
|
||||||
|
let counter = CounterVec::new(
|
||||||
|
Opts::new("benchmark_counter", "A counter to benchmark it."),
|
||||||
|
&["one", "two", "three"],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
c.bench_function("counter_with_mapped_labels_fnv", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut labels = HashMap::with_capacity_and_hasher(3, FnvBuildHasher::default());
|
||||||
|
labels.insert("two", "zwei");
|
||||||
|
labels.insert("one", "eins");
|
||||||
|
labels.insert("three", "drei");
|
||||||
|
counter.with(&black_box(labels)).inc();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -192,6 +215,7 @@ criterion_group!(
|
||||||
bench_counter_with_label_values,
|
bench_counter_with_label_values,
|
||||||
bench_counter_with_label_values_concurrent_write,
|
bench_counter_with_label_values_concurrent_write,
|
||||||
bench_counter_with_mapped_labels,
|
bench_counter_with_mapped_labels,
|
||||||
|
bench_counter_with_mapped_labels_fnv,
|
||||||
bench_counter_with_prepared_mapped_labels,
|
bench_counter_with_prepared_mapped_labels,
|
||||||
bench_int_counter_no_labels,
|
bench_int_counter_no_labels,
|
||||||
bench_int_counter_no_labels_concurrent_write,
|
bench_int_counter_no_labels_concurrent_write,
|
||||||
|
|
13
build.rs
13
build.rs
|
@ -2,13 +2,12 @@
|
||||||
|
|
||||||
#[cfg(feature = "gen")]
|
#[cfg(feature = "gen")]
|
||||||
fn generate_protobuf_binding_file() {
|
fn generate_protobuf_binding_file() {
|
||||||
protobuf_codegen_pure::run(protobuf_codegen_pure::Args {
|
protobuf_codegen::Codegen::new()
|
||||||
out_dir: "proto",
|
.out_dir("proto")
|
||||||
input: &["proto/proto_model.proto"],
|
.inputs(["proto/proto_model.proto"])
|
||||||
includes: &["proto"],
|
.includes(["proto"])
|
||||||
..Default::default()
|
.run()
|
||||||
})
|
.expect("Protobuf codegen failed");
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "gen"))]
|
#[cfg(not(feature = "gen"))]
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
||||||
|
|
||||||
use hyper::{
|
use std::net::SocketAddr;
|
||||||
header::CONTENT_TYPE,
|
|
||||||
service::{make_service_fn, service_fn},
|
|
||||||
Body, Request, Response, Server,
|
|
||||||
};
|
|
||||||
use prometheus::{Counter, Encoder, Gauge, HistogramVec, TextEncoder};
|
|
||||||
|
|
||||||
|
use hyper::body::Incoming;
|
||||||
|
use hyper::header::CONTENT_TYPE;
|
||||||
|
use hyper::server::conn::http1;
|
||||||
|
use hyper::service::service_fn;
|
||||||
|
use hyper::Request;
|
||||||
|
use hyper::Response;
|
||||||
|
use hyper_util::rt::TokioIo;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use prometheus::{labels, opts, register_counter, register_gauge, register_histogram_vec};
|
use prometheus::{labels, opts, register_counter, register_gauge, register_histogram_vec};
|
||||||
|
use prometheus::{Counter, Encoder, Gauge, HistogramVec, TextEncoder};
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
|
type BoxedErr = Box<dyn std::error::Error + Send + Sync + 'static>;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref HTTP_COUNTER: Counter = register_counter!(opts!(
|
static ref HTTP_COUNTER: Counter = register_counter!(opts!(
|
||||||
|
@ -31,22 +37,20 @@ lazy_static! {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn serve_req(_req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
async fn serve_req(_req: Request<Incoming>) -> Result<Response<String>, BoxedErr> {
|
||||||
let encoder = TextEncoder::new();
|
let encoder = TextEncoder::new();
|
||||||
|
|
||||||
HTTP_COUNTER.inc();
|
HTTP_COUNTER.inc();
|
||||||
let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["all"]).start_timer();
|
let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["all"]).start_timer();
|
||||||
|
|
||||||
let metric_families = prometheus::gather();
|
let metric_families = prometheus::gather();
|
||||||
let mut buffer = vec![];
|
let body = encoder.encode_to_string(&metric_families)?;
|
||||||
encoder.encode(&metric_families, &mut buffer).unwrap();
|
HTTP_BODY_GAUGE.set(body.len() as f64);
|
||||||
HTTP_BODY_GAUGE.set(buffer.len() as f64);
|
|
||||||
|
|
||||||
let response = Response::builder()
|
let response = Response::builder()
|
||||||
.status(200)
|
.status(200)
|
||||||
.header(CONTENT_TYPE, encoder.format_type())
|
.header(CONTENT_TYPE, encoder.format_type())
|
||||||
.body(Body::from(buffer))
|
.body(body)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
timer.observe_duration();
|
timer.observe_duration();
|
||||||
|
|
||||||
|
@ -54,15 +58,18 @@ async fn serve_req(_req: Request<Body>) -> Result<Response<Body>, hyper::Error>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() -> Result<(), BoxedErr> {
|
||||||
let addr = ([127, 0, 0, 1], 9898).into();
|
let addr: SocketAddr = ([127, 0, 0, 1], 9898).into();
|
||||||
println!("Listening on http://{}", addr);
|
println!("Listening on http://{}", addr);
|
||||||
|
let listener = TcpListener::bind(addr).await?;
|
||||||
|
|
||||||
let serve_future = Server::bind(&addr).serve(make_service_fn(|_| async {
|
loop {
|
||||||
Ok::<_, hyper::Error>(service_fn(serve_req))
|
let (stream, _) = listener.accept().await?;
|
||||||
}));
|
let io = TokioIo::new(stream);
|
||||||
|
|
||||||
if let Err(err) = serve_future.await {
|
let service = service_fn(serve_req);
|
||||||
eprintln!("server error: {}", err);
|
if let Err(err) = http1::Builder::new().serve_connection(io, service).await {
|
||||||
|
eprintln!("server error: {:?}", err);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
||||||
|
|
||||||
#[cfg(all(feature = "process", target_os = "linux"))]
|
|
||||||
fn main() {
|
fn main() {
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -21,11 +20,3 @@ fn main() {
|
||||||
thread::sleep(Duration::from_secs(1));
|
thread::sleep(Duration::from_secs(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(not(feature = "process"), not(target_os = "linux")))]
|
|
||||||
fn main() {
|
|
||||||
println!(
|
|
||||||
r#"Please enable feature "process", try:
|
|
||||||
cargo run --features="process" --example example_process_collector"#
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "push"), allow(unused_imports, dead_code))]
|
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
@ -25,7 +23,6 @@ lazy_static! {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "push")]
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let program = args[0].clone();
|
let program = args[0].clone();
|
||||||
|
@ -68,11 +65,3 @@ fn main() {
|
||||||
|
|
||||||
println!("Okay, please check the Pushgateway.");
|
println!("Okay, please check the Pushgateway.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "push"))]
|
|
||||||
fn main() {
|
|
||||||
println!(
|
|
||||||
r#"Please enable feature "push", try:
|
|
||||||
cargo run --features="push" --example example_push"#
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
// @generated
|
||||||
|
|
||||||
|
pub mod proto_model;
|
2467
proto/proto_model.rs
2467
proto/proto_model.rs
File diff suppressed because it is too large
Load Diff
|
@ -241,8 +241,8 @@ impl AtomicU64 {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use std::f64;
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
use std::f64::{self, EPSILON};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ mod test {
|
||||||
let table: Vec<f64> = vec![0.0, 1.0, PI, f64::MIN, f64::MAX];
|
let table: Vec<f64> = vec![0.0, 1.0, PI, f64::MIN, f64::MAX];
|
||||||
|
|
||||||
for f in table {
|
for f in table {
|
||||||
assert!((f - AtomicF64::new(f).get()).abs() < EPSILON);
|
assert!((f - AtomicF64::new(f).get()).abs() < f64::EPSILON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
109
src/counter.rs
109
src/counter.rs
|
@ -44,10 +44,10 @@ impl<P: Atomic> GenericCounter<P> {
|
||||||
|
|
||||||
/// Create a [`GenericCounter`] with the `opts` options.
|
/// Create a [`GenericCounter`] with the `opts` options.
|
||||||
pub fn with_opts(opts: Opts) -> Result<Self> {
|
pub fn with_opts(opts: Opts) -> Result<Self> {
|
||||||
Self::with_opts_and_label_values(&opts, &[])
|
Self::with_opts_and_label_values::<&str>(&opts, &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result<Self> {
|
fn with_opts_and_label_values<V: AsRef<str>>(opts: &Opts, label_values: &[V]) -> Result<Self> {
|
||||||
let v = Value::new(opts, ValueType::Counter, P::T::from_i64(0), label_values)?;
|
let v = Value::new(opts, ValueType::Counter, P::T::from_i64(0), label_values)?;
|
||||||
Ok(Self { v: Arc::new(v) })
|
Ok(Self { v: Arc::new(v) })
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ impl<P: Atomic> MetricVecBuilder for CounterVecBuilder<P> {
|
||||||
type M = GenericCounter<P>;
|
type M = GenericCounter<P>;
|
||||||
type P = Opts;
|
type P = Opts;
|
||||||
|
|
||||||
fn build(&self, opts: &Opts, vals: &[&str]) -> Result<Self::M> {
|
fn build<V: AsRef<str>>(&self, opts: &Opts, vals: &[V]) -> Result<Self::M> {
|
||||||
Self::M::with_opts_and_label_values(opts, vals)
|
Self::M::with_opts_and_label_values(opts, vals)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,10 +323,12 @@ impl<P: Atomic> Clone for GenericLocalCounterVec<P> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::f64::EPSILON;
|
use std::f64;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::metrics::{Collector, Opts};
|
use crate::metrics::{Collector, Opts};
|
||||||
|
#[cfg(feature = "protobuf")]
|
||||||
|
use crate::proto_ext::MessageFieldExt;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_counter() {
|
fn test_counter() {
|
||||||
|
@ -343,7 +345,7 @@ mod tests {
|
||||||
assert_eq!(mfs.len(), 1);
|
assert_eq!(mfs.len(), 1);
|
||||||
|
|
||||||
let mf = mfs.pop().unwrap();
|
let mf = mfs.pop().unwrap();
|
||||||
let m = mf.get_metric().get(0).unwrap();
|
let m = mf.get_metric().first().unwrap();
|
||||||
assert_eq!(m.get_label().len(), 2);
|
assert_eq!(m.get_label().len(), 2);
|
||||||
assert_eq!(m.get_counter().get_value() as u64, 43);
|
assert_eq!(m.get_counter().get_value() as u64, 43);
|
||||||
|
|
||||||
|
@ -363,12 +365,12 @@ mod tests {
|
||||||
assert_eq!(mfs.len(), 1);
|
assert_eq!(mfs.len(), 1);
|
||||||
|
|
||||||
let mf = mfs.pop().unwrap();
|
let mf = mfs.pop().unwrap();
|
||||||
let m = mf.get_metric().get(0).unwrap();
|
let m = mf.get_metric().first().unwrap();
|
||||||
assert_eq!(m.get_label().len(), 0);
|
assert_eq!(m.get_label().len(), 0);
|
||||||
assert_eq!(m.get_counter().get_value() as u64, 12);
|
assert_eq!(m.get_counter().get_value() as u64, 12);
|
||||||
|
|
||||||
counter.reset();
|
counter.reset();
|
||||||
assert_eq!(counter.get() as u64, 0);
|
assert_eq!(counter.get(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -414,9 +416,9 @@ mod tests {
|
||||||
|
|
||||||
local_counter.reset();
|
local_counter.reset();
|
||||||
counter.reset();
|
counter.reset();
|
||||||
assert_eq!(counter.get() as u64, 0);
|
assert_eq!(counter.get(), 0);
|
||||||
local_counter.flush();
|
local_counter.flush();
|
||||||
assert_eq!(counter.get() as u64, 0);
|
assert_eq!(counter.get(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -450,6 +452,40 @@ mod tests {
|
||||||
assert!(vec.remove(&labels3).is_err());
|
assert!(vec.remove(&labels3).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_counter_vec_with_owned_labels() {
|
||||||
|
let vec = CounterVec::new(
|
||||||
|
Opts::new("test_couter_vec", "test counter vec help"),
|
||||||
|
&["l1", "l2"],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let v1 = "v1".to_string();
|
||||||
|
let v2 = "v2".to_string();
|
||||||
|
|
||||||
|
let mut labels = HashMap::new();
|
||||||
|
labels.insert("l1", v1.clone());
|
||||||
|
labels.insert("l2", v2.clone());
|
||||||
|
assert!(vec.remove(&labels).is_err());
|
||||||
|
|
||||||
|
vec.with(&labels).inc();
|
||||||
|
assert!(vec.remove(&labels).is_ok());
|
||||||
|
assert!(vec.remove(&labels).is_err());
|
||||||
|
|
||||||
|
let mut labels2 = HashMap::new();
|
||||||
|
labels2.insert("l1", v2.clone());
|
||||||
|
labels2.insert("l2", v1.clone());
|
||||||
|
|
||||||
|
vec.with(&labels).inc();
|
||||||
|
assert!(vec.remove(&labels2).is_err());
|
||||||
|
|
||||||
|
vec.with(&labels).inc();
|
||||||
|
|
||||||
|
let mut labels3 = HashMap::new();
|
||||||
|
labels3.insert("l1", v1.clone());
|
||||||
|
assert!(vec.remove(&labels3).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_int_counter_vec() {
|
fn test_int_counter_vec() {
|
||||||
let vec = IntCounterVec::new(Opts::new("foo", "bar"), &["l1", "l2"]).unwrap();
|
let vec = IntCounterVec::new(Opts::new("foo", "bar"), &["l1", "l2"]).unwrap();
|
||||||
|
@ -489,6 +525,27 @@ mod tests {
|
||||||
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
|
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_counter_vec_with_owned_label_values() {
|
||||||
|
let vec = CounterVec::new(
|
||||||
|
Opts::new("test_vec", "test counter vec help"),
|
||||||
|
&["l1", "l2"],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let v1 = "v1".to_string();
|
||||||
|
let v2 = "v2".to_string();
|
||||||
|
let v3 = "v3".to_string();
|
||||||
|
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
|
||||||
|
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone()]).is_err());
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_counter_vec_local() {
|
fn test_counter_vec_local() {
|
||||||
let vec = CounterVec::new(
|
let vec = CounterVec::new(
|
||||||
|
@ -502,48 +559,48 @@ mod tests {
|
||||||
assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_err());
|
assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_err());
|
||||||
|
|
||||||
local_vec_1.with_label_values(&["v1", "v2"]).inc_by(23.0);
|
local_vec_1.with_label_values(&["v1", "v2"]).inc_by(23.0);
|
||||||
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
|
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
|
||||||
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
|
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
|
||||||
|
|
||||||
local_vec_1.flush();
|
local_vec_1.flush();
|
||||||
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
|
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
|
||||||
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
|
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
|
||||||
|
|
||||||
local_vec_1.flush();
|
local_vec_1.flush();
|
||||||
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
|
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
|
||||||
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
|
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
|
||||||
|
|
||||||
local_vec_1.with_label_values(&["v1", "v2"]).inc_by(11.0);
|
local_vec_1.with_label_values(&["v1", "v2"]).inc_by(11.0);
|
||||||
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 11.0) <= EPSILON);
|
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 11.0) <= f64::EPSILON);
|
||||||
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
|
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
|
||||||
|
|
||||||
local_vec_1.flush();
|
local_vec_1.flush();
|
||||||
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
|
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
|
||||||
assert!((vec.with_label_values(&["v1", "v2"]).get() - 34.0) <= EPSILON);
|
assert!((vec.with_label_values(&["v1", "v2"]).get() - 34.0) <= f64::EPSILON);
|
||||||
|
|
||||||
// When calling `remove_label_values`, it is "flushed" immediately.
|
// When calling `remove_label_values`, it is "flushed" immediately.
|
||||||
assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_ok());
|
assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_ok());
|
||||||
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
|
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
|
||||||
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
|
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
|
||||||
|
|
||||||
local_vec_1.with_label_values(&["v1", "v2"]).inc();
|
local_vec_1.with_label_values(&["v1", "v2"]).inc();
|
||||||
assert!(local_vec_1.remove_label_values(&["v1"]).is_err());
|
assert!(local_vec_1.remove_label_values(&["v1"]).is_err());
|
||||||
assert!(local_vec_1.remove_label_values(&["v1", "v3"]).is_err());
|
assert!(local_vec_1.remove_label_values(&["v1", "v3"]).is_err());
|
||||||
|
|
||||||
local_vec_1.with_label_values(&["v1", "v2"]).inc_by(13.0);
|
local_vec_1.with_label_values(&["v1", "v2"]).inc_by(13.0);
|
||||||
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 14.0) <= EPSILON);
|
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 14.0) <= f64::EPSILON);
|
||||||
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
|
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
|
||||||
|
|
||||||
local_vec_2.with_label_values(&["v1", "v2"]).inc_by(7.0);
|
local_vec_2.with_label_values(&["v1", "v2"]).inc_by(7.0);
|
||||||
assert!((local_vec_2.with_label_values(&["v1", "v2"]).get() - 7.0) <= EPSILON);
|
assert!((local_vec_2.with_label_values(&["v1", "v2"]).get() - 7.0) <= f64::EPSILON);
|
||||||
|
|
||||||
local_vec_1.flush();
|
local_vec_1.flush();
|
||||||
local_vec_2.flush();
|
local_vec_2.flush();
|
||||||
assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= EPSILON);
|
assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= f64::EPSILON);
|
||||||
|
|
||||||
local_vec_1.flush();
|
local_vec_1.flush();
|
||||||
local_vec_2.flush();
|
local_vec_2.flush();
|
||||||
assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= EPSILON);
|
assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= f64::EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
11
src/desc.rs
11
src/desc.rs
|
@ -27,7 +27,7 @@ fn is_valid_ident<F: FnMut(char) -> bool>(input: &str, mut charset_validator: F)
|
||||||
zeroth
|
zeroth
|
||||||
.and_then(|zeroth| {
|
.and_then(|zeroth| {
|
||||||
if charset_validator(zeroth) {
|
if charset_validator(zeroth) {
|
||||||
Some(chars.all(|c| charset_validator(c) || c.is_digit(10)))
|
Some(chars.all(|c| charset_validator(c) || c.is_ascii_digit()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -253,8 +253,7 @@ mod tests {
|
||||||
vec![name.into()],
|
vec![name.into()],
|
||||||
HashMap::new(),
|
HashMap::new(),
|
||||||
)
|
)
|
||||||
.err()
|
.expect_err(format!("expected error for {}", name).as_ref());
|
||||||
.expect(format!("expected error for {}", name).as_ref());
|
|
||||||
match res {
|
match res {
|
||||||
Error::Msg(msg) => assert_eq!(msg, format!("'{}' is not a valid label name", name)),
|
Error::Msg(msg) => assert_eq!(msg, format!("'{}' is not a valid label name", name)),
|
||||||
other => panic!("{}", other),
|
other => panic!("{}", other),
|
||||||
|
@ -268,8 +267,7 @@ mod tests {
|
||||||
let mut labels = HashMap::new();
|
let mut labels = HashMap::new();
|
||||||
labels.insert(name.into(), "value".into());
|
labels.insert(name.into(), "value".into());
|
||||||
let res = Desc::new("name".into(), "help".into(), vec![], labels)
|
let res = Desc::new("name".into(), "help".into(), vec![], labels)
|
||||||
.err()
|
.expect_err(format!("expected error for {}", name).as_ref());
|
||||||
.expect(format!("expected error for {}", name).as_ref());
|
|
||||||
match res {
|
match res {
|
||||||
Error::Msg(msg) => assert_eq!(msg, format!("'{}' is not a valid label name", name)),
|
Error::Msg(msg) => assert_eq!(msg, format!("'{}' is not a valid label name", name)),
|
||||||
other => panic!("{}", other),
|
other => panic!("{}", other),
|
||||||
|
@ -281,8 +279,7 @@ mod tests {
|
||||||
fn test_invalid_metric_name() {
|
fn test_invalid_metric_name() {
|
||||||
for &name in &["-dash", "9gag", "has space"] {
|
for &name in &["-dash", "9gag", "has space"] {
|
||||||
let res = Desc::new(name.into(), "help".into(), vec![], HashMap::new())
|
let res = Desc::new(name.into(), "help".into(), vec![], HashMap::new())
|
||||||
.err()
|
.expect_err(format!("expected error for {}", name).as_ref());
|
||||||
.expect(format!("expected error for {}", name).as_ref());
|
|
||||||
match res {
|
match res {
|
||||||
Error::Msg(msg) => {
|
Error::Msg(msg) => {
|
||||||
assert_eq!(msg, format!("'{}' is not a valid metric name", name))
|
assert_eq!(msg, format!("'{}' is not a valid metric name", name))
|
||||||
|
|
|
@ -30,7 +30,7 @@ fn check_metric_family(mf: &MetricFamily) -> Result<()> {
|
||||||
if mf.get_metric().is_empty() {
|
if mf.get_metric().is_empty() {
|
||||||
return Err(Error::Msg(format!("MetricFamily has no metrics: {:?}", mf)));
|
return Err(Error::Msg(format!("MetricFamily has no metrics: {:?}", mf)));
|
||||||
}
|
}
|
||||||
if mf.get_name().is_empty() {
|
if mf.name().is_empty() {
|
||||||
return Err(Error::Msg(format!("MetricFamily has no name: {:?}", mf)));
|
return Err(Error::Msg(format!("MetricFamily has no name: {:?}", mf)));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -6,6 +6,8 @@ use std::io::{self, Write};
|
||||||
use crate::errors::Result;
|
use crate::errors::Result;
|
||||||
use crate::histogram::BUCKET_LABEL;
|
use crate::histogram::BUCKET_LABEL;
|
||||||
use crate::proto::{self, MetricFamily, MetricType};
|
use crate::proto::{self, MetricFamily, MetricType};
|
||||||
|
#[cfg(feature = "protobuf")]
|
||||||
|
use crate::proto_ext::MessageFieldExt;
|
||||||
|
|
||||||
use super::{check_metric_family, Encoder};
|
use super::{check_metric_family, Encoder};
|
||||||
|
|
||||||
|
@ -55,8 +57,8 @@ impl TextEncoder {
|
||||||
check_metric_family(mf)?;
|
check_metric_family(mf)?;
|
||||||
|
|
||||||
// Write `# HELP` header.
|
// Write `# HELP` header.
|
||||||
let name = mf.get_name();
|
let name = mf.name();
|
||||||
let help = mf.get_help();
|
let help = mf.help();
|
||||||
if !help.is_empty() {
|
if !help.is_empty() {
|
||||||
writer.write_all("# HELP ")?;
|
writer.write_all("# HELP ")?;
|
||||||
writer.write_all(name)?;
|
writer.write_all(name)?;
|
||||||
|
@ -87,14 +89,14 @@ impl TextEncoder {
|
||||||
|
|
||||||
let mut inf_seen = false;
|
let mut inf_seen = false;
|
||||||
for b in h.get_bucket() {
|
for b in h.get_bucket() {
|
||||||
let upper_bound = b.get_upper_bound();
|
let upper_bound = b.upper_bound();
|
||||||
write_sample(
|
write_sample(
|
||||||
writer,
|
writer,
|
||||||
name,
|
name,
|
||||||
Some("_bucket"),
|
Some("_bucket"),
|
||||||
m,
|
m,
|
||||||
Some((BUCKET_LABEL, &upper_bound.to_string())),
|
Some((BUCKET_LABEL, &upper_bound.to_string())),
|
||||||
b.get_cumulative_count() as f64,
|
b.cumulative_count() as f64,
|
||||||
)?;
|
)?;
|
||||||
if upper_bound.is_sign_positive() && upper_bound.is_infinite() {
|
if upper_bound.is_sign_positive() && upper_bound.is_infinite() {
|
||||||
inf_seen = true;
|
inf_seen = true;
|
||||||
|
@ -125,18 +127,18 @@ impl TextEncoder {
|
||||||
MetricType::SUMMARY => {
|
MetricType::SUMMARY => {
|
||||||
let s = m.get_summary();
|
let s = m.get_summary();
|
||||||
|
|
||||||
for q in s.get_quantile() {
|
for q in s.get_quantile().iter() {
|
||||||
write_sample(
|
write_sample(
|
||||||
writer,
|
writer,
|
||||||
name,
|
name,
|
||||||
None,
|
None,
|
||||||
m,
|
m,
|
||||||
Some((QUANTILE, &q.get_quantile().to_string())),
|
Some((QUANTILE, &q.quantile().to_string())),
|
||||||
q.get_value(),
|
q.value(),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_sample(writer, name, Some("_sum"), m, None, s.get_sample_sum())?;
|
write_sample(writer, name, Some("_sum"), m, None, s.sample_sum())?;
|
||||||
|
|
||||||
write_sample(
|
write_sample(
|
||||||
writer,
|
writer,
|
||||||
|
@ -144,7 +146,7 @@ impl TextEncoder {
|
||||||
Some("_count"),
|
Some("_count"),
|
||||||
m,
|
m,
|
||||||
None,
|
None,
|
||||||
s.get_sample_count() as f64,
|
s.sample_count() as f64,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
MetricType::UNTYPED => {
|
MetricType::UNTYPED => {
|
||||||
|
@ -191,7 +193,7 @@ fn write_sample(
|
||||||
writer.write_all(" ")?;
|
writer.write_all(" ")?;
|
||||||
writer.write_all(&value.to_string())?;
|
writer.write_all(&value.to_string())?;
|
||||||
|
|
||||||
let timestamp = mc.get_timestamp_ms();
|
let timestamp = mc.timestamp_ms();
|
||||||
if timestamp != 0 {
|
if timestamp != 0 {
|
||||||
writer.write_all(" ")?;
|
writer.write_all(" ")?;
|
||||||
writer.write_all(×tamp.to_string())?;
|
writer.write_all(×tamp.to_string())?;
|
||||||
|
@ -221,9 +223,9 @@ fn label_pairs_to_text(
|
||||||
let mut separator = "{";
|
let mut separator = "{";
|
||||||
for lp in pairs {
|
for lp in pairs {
|
||||||
writer.write_all(separator)?;
|
writer.write_all(separator)?;
|
||||||
writer.write_all(lp.get_name())?;
|
writer.write_all(lp.name())?;
|
||||||
writer.write_all("=\"")?;
|
writer.write_all("=\"")?;
|
||||||
writer.write_all(&escape_string(lp.get_value(), true))?;
|
writer.write_all(&escape_string(lp.value(), true))?;
|
||||||
writer.write_all("\"")?;
|
writer.write_all("\"")?;
|
||||||
|
|
||||||
separator = ",";
|
separator = ",";
|
||||||
|
@ -423,11 +425,11 @@ test_histogram_count{a="1"} 1
|
||||||
quantile2.set_quantile(100.0);
|
quantile2.set_quantile(100.0);
|
||||||
quantile2.set_value(5.0);
|
quantile2.set_value(5.0);
|
||||||
|
|
||||||
summary.set_quantile(from_vec!(vec!(quantile1, quantile2)));
|
summary.set_quantile(vec![quantile1, quantile2]);
|
||||||
|
|
||||||
let mut metric = Metric::default();
|
let mut metric = Metric::default();
|
||||||
metric.set_summary(summary);
|
metric.set_summary(summary);
|
||||||
metric_family.set_metric(from_vec!(vec!(metric)));
|
metric_family.set_metric(vec![metric]);
|
||||||
|
|
||||||
let mut writer = Vec::<u8>::new();
|
let mut writer = Vec::<u8>::new();
|
||||||
let encoder = TextEncoder::new();
|
let encoder = TextEncoder::new();
|
||||||
|
|
|
@ -22,10 +22,10 @@ pub enum Error {
|
||||||
/// An error containing a [`std::io::Error`].
|
/// An error containing a [`std::io::Error`].
|
||||||
#[error("Io error: {0}")]
|
#[error("Io error: {0}")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
/// An error containing a [`protobuf::error::ProtobufError`].
|
/// An error containing a [`protobuf::Error`].
|
||||||
#[cfg(feature = "protobuf")]
|
#[cfg(feature = "protobuf")]
|
||||||
#[error("Protobuf error: {0}")]
|
#[error("Protobuf error: {0}")]
|
||||||
Protobuf(#[from] protobuf::error::ProtobufError),
|
Protobuf(#[from] protobuf::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A specialized Result type for prometheus.
|
/// A specialized Result type for prometheus.
|
||||||
|
|
59
src/gauge.rs
59
src/gauge.rs
|
@ -43,10 +43,10 @@ impl<P: Atomic> GenericGauge<P> {
|
||||||
|
|
||||||
/// Create a [`GenericGauge`] with the `opts` options.
|
/// Create a [`GenericGauge`] with the `opts` options.
|
||||||
pub fn with_opts(opts: Opts) -> Result<Self> {
|
pub fn with_opts(opts: Opts) -> Result<Self> {
|
||||||
Self::with_opts_and_label_values(&opts, &[])
|
Self::with_opts_and_label_values::<&str>(&opts, &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result<Self> {
|
fn with_opts_and_label_values<V: AsRef<str>>(opts: &Opts, label_values: &[V]) -> Result<Self> {
|
||||||
let v = Value::new(opts, ValueType::Gauge, P::T::from_i64(0), label_values)?;
|
let v = Value::new(opts, ValueType::Gauge, P::T::from_i64(0), label_values)?;
|
||||||
Ok(Self { v: Arc::new(v) })
|
Ok(Self { v: Arc::new(v) })
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ impl<P: Atomic> MetricVecBuilder for GaugeVecBuilder<P> {
|
||||||
type M = GenericGauge<P>;
|
type M = GenericGauge<P>;
|
||||||
type P = Opts;
|
type P = Opts;
|
||||||
|
|
||||||
fn build(&self, opts: &Opts, vals: &[&str]) -> Result<Self::M> {
|
fn build<V: AsRef<str>>(&self, opts: &Opts, vals: &[V]) -> Result<Self::M> {
|
||||||
Self::M::with_opts_and_label_values(opts, vals)
|
Self::M::with_opts_and_label_values(opts, vals)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,6 +166,8 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::metrics::{Collector, Opts};
|
use crate::metrics::{Collector, Opts};
|
||||||
|
#[cfg(feature = "protobuf")]
|
||||||
|
use crate::proto_ext::MessageFieldExt;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gauge() {
|
fn test_gauge() {
|
||||||
|
@ -188,7 +190,7 @@ mod tests {
|
||||||
assert_eq!(mfs.len(), 1);
|
assert_eq!(mfs.len(), 1);
|
||||||
|
|
||||||
let mf = mfs.pop().unwrap();
|
let mf = mfs.pop().unwrap();
|
||||||
let m = mf.get_metric().get(0).unwrap();
|
let m = mf.get_metric().first().unwrap();
|
||||||
assert_eq!(m.get_label().len(), 2);
|
assert_eq!(m.get_label().len(), 2);
|
||||||
assert_eq!(m.get_gauge().get_value() as u64, 42);
|
assert_eq!(m.get_gauge().get_value() as u64, 42);
|
||||||
}
|
}
|
||||||
|
@ -216,6 +218,29 @@ mod tests {
|
||||||
assert!(vec.remove(&labels).is_err());
|
assert!(vec.remove(&labels).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gauge_vec_with_owned_labels() {
|
||||||
|
let vec = GaugeVec::new(
|
||||||
|
Opts::new("test_gauge_vec", "test gauge vec help"),
|
||||||
|
&["l1", "l2"],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut labels = HashMap::new();
|
||||||
|
labels.insert("l1", "v1");
|
||||||
|
labels.insert("l2", "v2");
|
||||||
|
assert!(vec.remove(&labels).is_err());
|
||||||
|
|
||||||
|
vec.with(&labels).inc();
|
||||||
|
vec.with(&labels).dec();
|
||||||
|
vec.with(&labels).add(42.0);
|
||||||
|
vec.with(&labels).sub(42.0);
|
||||||
|
vec.with(&labels).set(42.0);
|
||||||
|
|
||||||
|
assert!(vec.remove(&labels).is_ok());
|
||||||
|
assert!(vec.remove(&labels).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gauge_vec_with_label_values() {
|
fn test_gauge_vec_with_label_values() {
|
||||||
let vec = GaugeVec::new(
|
let vec = GaugeVec::new(
|
||||||
|
@ -237,4 +262,30 @@ mod tests {
|
||||||
assert!(vec.remove_label_values(&["v1"]).is_err());
|
assert!(vec.remove_label_values(&["v1"]).is_err());
|
||||||
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
|
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gauge_vec_with_owned_label_values() {
|
||||||
|
let vec = GaugeVec::new(
|
||||||
|
Opts::new("test_gauge_vec", "test gauge vec help"),
|
||||||
|
&["l1", "l2"],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let v1 = "v1".to_string();
|
||||||
|
let v2 = "v2".to_string();
|
||||||
|
let v3 = "v3".to_string();
|
||||||
|
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
|
||||||
|
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).dec();
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).add(42.0);
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).sub(42.0);
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).set(42.0);
|
||||||
|
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone()]).is_err());
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,11 +284,11 @@ impl ShardAndCount {
|
||||||
/// A histogram supports two main execution paths:
|
/// A histogram supports two main execution paths:
|
||||||
///
|
///
|
||||||
/// 1. `observe` which increases the overall observation counter, updates the
|
/// 1. `observe` which increases the overall observation counter, updates the
|
||||||
/// observation sum and increases a single bucket counter.
|
/// observation sum and increases a single bucket counter.
|
||||||
///
|
///
|
||||||
/// 2. `proto` (aka. collecting the metric, from now on referred to as the
|
/// 2. `proto` (aka. collecting the metric, from now on referred to as the
|
||||||
/// collect operation) which snapshots the state of the histogram and exposes it
|
/// collect operation) which snapshots the state of the histogram and exposes
|
||||||
/// as a Protobuf struct.
|
/// it as a Protobuf struct.
|
||||||
///
|
///
|
||||||
/// If an observe and a collect operation interleave, the latter could be
|
/// If an observe and a collect operation interleave, the latter could be
|
||||||
/// exposing a snapshot of the histogram that does not uphold all histogram
|
/// exposing a snapshot of the histogram that does not uphold all histogram
|
||||||
|
@ -327,14 +327,14 @@ pub struct HistogramCore {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HistogramCore {
|
impl HistogramCore {
|
||||||
pub fn new(opts: &HistogramOpts, label_values: &[&str]) -> Result<HistogramCore> {
|
pub fn new<V: AsRef<str>>(opts: &HistogramOpts, label_values: &[V]) -> Result<HistogramCore> {
|
||||||
let desc = opts.describe()?;
|
let desc = opts.describe()?;
|
||||||
|
|
||||||
for name in &desc.variable_labels {
|
for name in &desc.variable_labels {
|
||||||
check_bucket_label(name)?;
|
check_bucket_label(name)?;
|
||||||
}
|
}
|
||||||
for pair in &desc.const_label_pairs {
|
for pair in &desc.const_label_pairs {
|
||||||
check_bucket_label(pair.get_name())?;
|
check_bucket_label(pair.name())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let label_pairs = make_label_pairs(&desc, label_values)?;
|
let label_pairs = make_label_pairs(&desc, label_values)?;
|
||||||
|
@ -453,7 +453,7 @@ impl HistogramCore {
|
||||||
b.set_upper_bound(*upper_bound);
|
b.set_upper_bound(*upper_bound);
|
||||||
buckets.push(b);
|
buckets.push(b);
|
||||||
}
|
}
|
||||||
h.set_bucket(from_vec!(buckets));
|
h.set_bucket(buckets);
|
||||||
|
|
||||||
// Update the hot shard.
|
// Update the hot shard.
|
||||||
hot_shard.count.inc_by(overall_count);
|
hot_shard.count.inc_by(overall_count);
|
||||||
|
@ -516,7 +516,7 @@ impl Instant {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn elapsed(&self) -> Duration {
|
pub fn elapsed(&self) -> Duration {
|
||||||
match &*self {
|
match self {
|
||||||
// We use `saturating_duration_since` to avoid panics caused by non-monotonic clocks.
|
// We use `saturating_duration_since` to avoid panics caused by non-monotonic clocks.
|
||||||
Instant::Monotonic(i) => StdInstant::now().saturating_duration_since(*i),
|
Instant::Monotonic(i) => StdInstant::now().saturating_duration_since(*i),
|
||||||
|
|
||||||
|
@ -542,7 +542,7 @@ impl Instant {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn elapsed_sec(&self) -> f64 {
|
pub fn elapsed_sec(&self) -> f64 {
|
||||||
duration_to_seconds(self.elapsed())
|
self.elapsed().as_secs_f64()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,12 +674,12 @@ pub struct Histogram {
|
||||||
impl Histogram {
|
impl Histogram {
|
||||||
/// `with_opts` creates a [`Histogram`] with the `opts` options.
|
/// `with_opts` creates a [`Histogram`] with the `opts` options.
|
||||||
pub fn with_opts(opts: HistogramOpts) -> Result<Histogram> {
|
pub fn with_opts(opts: HistogramOpts) -> Result<Histogram> {
|
||||||
Histogram::with_opts_and_label_values(&opts, &[])
|
Histogram::with_opts_and_label_values::<&str>(&opts, &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_opts_and_label_values(
|
fn with_opts_and_label_values<V: AsRef<str>>(
|
||||||
opts: &HistogramOpts,
|
opts: &HistogramOpts,
|
||||||
label_values: &[&str],
|
label_values: &[V],
|
||||||
) -> Result<Histogram> {
|
) -> Result<Histogram> {
|
||||||
let core = HistogramCore::new(opts, label_values)?;
|
let core = HistogramCore::new(opts, label_values)?;
|
||||||
|
|
||||||
|
@ -750,8 +750,7 @@ impl Histogram {
|
||||||
|
|
||||||
impl Metric for Histogram {
|
impl Metric for Histogram {
|
||||||
fn metric(&self) -> proto::Metric {
|
fn metric(&self) -> proto::Metric {
|
||||||
let mut m = proto::Metric::default();
|
let mut m = proto::Metric::from_label(self.core.label_pairs.clone());
|
||||||
m.set_label(from_vec!(self.core.label_pairs.clone()));
|
|
||||||
|
|
||||||
let h = self.core.proto();
|
let h = self.core.proto();
|
||||||
m.set_histogram(h);
|
m.set_histogram(h);
|
||||||
|
@ -770,7 +769,7 @@ impl Collector for Histogram {
|
||||||
m.set_name(self.core.desc.fq_name.clone());
|
m.set_name(self.core.desc.fq_name.clone());
|
||||||
m.set_help(self.core.desc.help.clone());
|
m.set_help(self.core.desc.help.clone());
|
||||||
m.set_field_type(proto::MetricType::HISTOGRAM);
|
m.set_field_type(proto::MetricType::HISTOGRAM);
|
||||||
m.set_metric(from_vec!(vec![self.metric()]));
|
m.set_metric(vec![self.metric()]);
|
||||||
|
|
||||||
vec![m]
|
vec![m]
|
||||||
}
|
}
|
||||||
|
@ -783,7 +782,7 @@ impl MetricVecBuilder for HistogramVecBuilder {
|
||||||
type M = Histogram;
|
type M = Histogram;
|
||||||
type P = HistogramOpts;
|
type P = HistogramOpts;
|
||||||
|
|
||||||
fn build(&self, opts: &HistogramOpts, vals: &[&str]) -> Result<Histogram> {
|
fn build<V: AsRef<str>>(&self, opts: &HistogramOpts, vals: &[V]) -> Result<Histogram> {
|
||||||
Histogram::with_opts_and_label_values(opts, vals)
|
Histogram::with_opts_and_label_values(opts, vals)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -882,13 +881,6 @@ pub fn exponential_buckets(start: f64, factor: f64, count: usize) -> Result<Vec<
|
||||||
Ok(buckets)
|
Ok(buckets)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `duration_to_seconds` converts Duration to seconds.
|
|
||||||
#[inline]
|
|
||||||
pub fn duration_to_seconds(d: Duration) -> f64 {
|
|
||||||
let nanos = f64::from(d.subsec_nanos()) / 1e9;
|
|
||||||
d.as_secs() as f64 + nanos
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct LocalHistogramCore {
|
pub struct LocalHistogramCore {
|
||||||
histogram: Histogram,
|
histogram: Histogram,
|
||||||
|
@ -1207,7 +1199,7 @@ impl Clone for LocalHistogramVec {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::f64::{EPSILON, INFINITY};
|
use std::f64;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
@ -1237,7 +1229,7 @@ mod tests {
|
||||||
assert_eq!(mfs.len(), 1);
|
assert_eq!(mfs.len(), 1);
|
||||||
|
|
||||||
let mf = mfs.pop().unwrap();
|
let mf = mfs.pop().unwrap();
|
||||||
let m = mf.get_metric().get(0).unwrap();
|
let m = mf.get_metric().first().unwrap();
|
||||||
assert_eq!(m.get_label().len(), 2);
|
assert_eq!(m.get_label().len(), 2);
|
||||||
let proto_histogram = m.get_histogram();
|
let proto_histogram = m.get_histogram();
|
||||||
assert_eq!(proto_histogram.get_sample_count(), 3);
|
assert_eq!(proto_histogram.get_sample_count(), 3);
|
||||||
|
@ -1251,11 +1243,11 @@ mod tests {
|
||||||
assert_eq!(mfs.len(), 1);
|
assert_eq!(mfs.len(), 1);
|
||||||
|
|
||||||
let mf = mfs.pop().unwrap();
|
let mf = mfs.pop().unwrap();
|
||||||
let m = mf.get_metric().get(0).unwrap();
|
let m = mf.get_metric().first().unwrap();
|
||||||
assert_eq!(m.get_label().len(), 0);
|
assert_eq!(m.get_label().len(), 0);
|
||||||
let proto_histogram = m.get_histogram();
|
let proto_histogram = m.get_histogram();
|
||||||
assert_eq!(proto_histogram.get_sample_count(), 0);
|
assert_eq!(proto_histogram.get_sample_count(), 0);
|
||||||
assert!((proto_histogram.get_sample_sum() - 0.0) < EPSILON);
|
assert!((proto_histogram.get_sample_sum() - 0.0) < f64::EPSILON);
|
||||||
assert_eq!(proto_histogram.get_bucket().len(), buckets.len())
|
assert_eq!(proto_histogram.get_bucket().len(), buckets.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1287,7 +1279,7 @@ mod tests {
|
||||||
let m = mf.get_metric().get(0).unwrap();
|
let m = mf.get_metric().get(0).unwrap();
|
||||||
let proto_histogram = m.get_histogram();
|
let proto_histogram = m.get_histogram();
|
||||||
assert_eq!(proto_histogram.get_sample_count(), 3);
|
assert_eq!(proto_histogram.get_sample_count(), 3);
|
||||||
assert!((proto_histogram.get_sample_sum() - 0.0) > EPSILON);
|
assert!((proto_histogram.get_sample_sum() - 0.0) > f64::EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1311,7 +1303,11 @@ mod tests {
|
||||||
(vec![], true, DEFAULT_BUCKETS.len()),
|
(vec![], true, DEFAULT_BUCKETS.len()),
|
||||||
(vec![-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0], true, 7),
|
(vec![-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0], true, 7),
|
||||||
(vec![-2.0, -1.0, -0.5, 10.0, 0.5, 1.0, 2.0], false, 7),
|
(vec![-2.0, -1.0, -0.5, 10.0, 0.5, 1.0, 2.0], false, 7),
|
||||||
(vec![-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, INFINITY], true, 6),
|
(
|
||||||
|
vec![-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, f64::INFINITY],
|
||||||
|
true,
|
||||||
|
6,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (buckets, is_ok, length) in table {
|
for (buckets, is_ok, length) in table {
|
||||||
|
@ -1360,16 +1356,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_duration_to_seconds() {
|
|
||||||
let tbls = vec![(1000, 1.0), (1100, 1.1), (100_111, 100.111)];
|
|
||||||
for (millis, seconds) in tbls {
|
|
||||||
let d = Duration::from_millis(millis);
|
|
||||||
let v = duration_to_seconds(d);
|
|
||||||
assert!((v - seconds).abs() < EPSILON);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_histogram_vec_with_label_values() {
|
fn test_histogram_vec_with_label_values() {
|
||||||
let vec = HistogramVec::new(
|
let vec = HistogramVec::new(
|
||||||
|
@ -1386,6 +1372,27 @@ mod tests {
|
||||||
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
|
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_histogram_vec_with_owned_label_values() {
|
||||||
|
let vec = HistogramVec::new(
|
||||||
|
HistogramOpts::new("test_histogram_vec", "test histogram vec help"),
|
||||||
|
&["l1", "l2"],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let v1 = "v1".to_string();
|
||||||
|
let v2 = "v2".to_string();
|
||||||
|
let v3 = "v3".to_string();
|
||||||
|
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()])
|
||||||
|
.observe(1.0);
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
|
||||||
|
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone()]).is_err());
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_histogram_vec_with_opts_buckets() {
|
fn test_histogram_vec_with_opts_buckets() {
|
||||||
let labels = ["l1", "l2"];
|
let labels = ["l1", "l2"];
|
||||||
|
@ -1405,7 +1412,7 @@ mod tests {
|
||||||
|
|
||||||
let proto_histogram = m.get_histogram();
|
let proto_histogram = m.get_histogram();
|
||||||
assert_eq!(proto_histogram.get_sample_count(), 1);
|
assert_eq!(proto_histogram.get_sample_count(), 1);
|
||||||
assert!((proto_histogram.get_sample_sum() - 1.0) < EPSILON);
|
assert!((proto_histogram.get_sample_sum() - 1.0) < f64::EPSILON);
|
||||||
assert_eq!(proto_histogram.get_bucket().len(), buckets.len())
|
assert_eq!(proto_histogram.get_bucket().len(), buckets.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1421,7 +1428,7 @@ mod tests {
|
||||||
let m = histogram.metric();
|
let m = histogram.metric();
|
||||||
let proto_histogram = m.get_histogram();
|
let proto_histogram = m.get_histogram();
|
||||||
assert_eq!(proto_histogram.get_sample_count(), count);
|
assert_eq!(proto_histogram.get_sample_count(), count);
|
||||||
assert!((proto_histogram.get_sample_sum() - sum) < EPSILON);
|
assert!((proto_histogram.get_sample_sum() - sum) < f64::EPSILON);
|
||||||
};
|
};
|
||||||
|
|
||||||
local.observe(1.0);
|
local.observe(1.0);
|
||||||
|
@ -1456,7 +1463,7 @@ mod tests {
|
||||||
let ms = vec.collect()[0].take_metric();
|
let ms = vec.collect()[0].take_metric();
|
||||||
let proto_histogram = ms[0].get_histogram();
|
let proto_histogram = ms[0].get_histogram();
|
||||||
assert_eq!(proto_histogram.get_sample_count(), count);
|
assert_eq!(proto_histogram.get_sample_count(), count);
|
||||||
assert!((proto_histogram.get_sample_sum() - sum) < EPSILON);
|
assert!((proto_histogram.get_sample_sum() - sum) < f64::EPSILON);
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1516,7 +1523,7 @@ mod tests {
|
||||||
sample_count = proto.get_sample_count();
|
sample_count = proto.get_sample_count();
|
||||||
sample_sum = proto.get_sample_sum() as u64;
|
sample_sum = proto.get_sample_sum() as u64;
|
||||||
// There is only one bucket thus the `[0]`.
|
// There is only one bucket thus the `[0]`.
|
||||||
cumulative_count = proto.get_bucket()[0].get_cumulative_count();
|
cumulative_count = proto.get_bucket()[0].cumulative_count();
|
||||||
|
|
||||||
if sample_count != cumulative_count {
|
if sample_count != cumulative_count {
|
||||||
break;
|
break;
|
||||||
|
|
18
src/lib.rs
18
src/lib.rs
|
@ -129,23 +129,12 @@ This library supports four features:
|
||||||
#[path = "../proto/proto_model.rs"]
|
#[path = "../proto/proto_model.rs"]
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
|
|
||||||
#[cfg(feature = "protobuf")]
|
|
||||||
macro_rules! from_vec {
|
|
||||||
($e: expr) => {
|
|
||||||
::protobuf::RepeatedField::from_vec($e)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "protobuf"))]
|
#[cfg(not(feature = "protobuf"))]
|
||||||
#[path = "plain_model.rs"]
|
#[path = "plain_model.rs"]
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
|
|
||||||
#[cfg(not(feature = "protobuf"))]
|
#[cfg(feature = "protobuf")]
|
||||||
macro_rules! from_vec {
|
mod proto_ext;
|
||||||
($e: expr) => {
|
|
||||||
$e
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
@ -158,6 +147,8 @@ mod errors;
|
||||||
mod gauge;
|
mod gauge;
|
||||||
mod histogram;
|
mod histogram;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
|
mod nohash;
|
||||||
|
mod pulling_gauge;
|
||||||
#[cfg(feature = "push")]
|
#[cfg(feature = "push")]
|
||||||
mod push;
|
mod push;
|
||||||
mod registry;
|
mod registry;
|
||||||
|
@ -219,6 +210,7 @@ pub use self::histogram::DEFAULT_BUCKETS;
|
||||||
pub use self::histogram::{exponential_buckets, linear_buckets};
|
pub use self::histogram::{exponential_buckets, linear_buckets};
|
||||||
pub use self::histogram::{Histogram, HistogramOpts, HistogramTimer, HistogramVec};
|
pub use self::histogram::{Histogram, HistogramOpts, HistogramTimer, HistogramVec};
|
||||||
pub use self::metrics::Opts;
|
pub use self::metrics::Opts;
|
||||||
|
pub use self::pulling_gauge::PullingGauge;
|
||||||
#[cfg(feature = "push")]
|
#[cfg(feature = "push")]
|
||||||
pub use self::push::{
|
pub use self::push::{
|
||||||
hostname_grouping_key, push_add_collector, push_add_metrics, push_collector, push_metrics,
|
hostname_grouping_key, push_add_collector, push_add_metrics, push_collector, push_metrics,
|
||||||
|
|
|
@ -43,7 +43,7 @@ fn test_labels_without_trailing_comma() {
|
||||||
"foo" => "bar"
|
"foo" => "bar"
|
||||||
};
|
};
|
||||||
assert_eq!(labels.len(), 2);
|
assert_eq!(labels.len(), 2);
|
||||||
assert!(labels.get("test").is_some());
|
assert!(labels.contains_key("test"));
|
||||||
assert_eq!(*(labels.get("test").unwrap()), "hello");
|
assert_eq!(*(labels.get("test").unwrap()), "hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +84,7 @@ macro_rules! opts {
|
||||||
let opts = $crate::Opts::new($NAME, $HELP);
|
let opts = $crate::Opts::new($NAME, $HELP);
|
||||||
let lbs = HashMap::<String, String>::new();
|
let lbs = HashMap::<String, String>::new();
|
||||||
$(
|
$(
|
||||||
|
#[allow(clippy::redundant_locals)]
|
||||||
let mut lbs = lbs;
|
let mut lbs = lbs;
|
||||||
lbs.extend($CONST_LABELS.iter().map(|(k, v)| ((*k).into(), (*v).into())));
|
lbs.extend($CONST_LABELS.iter().map(|(k, v)| ((*k).into(), (*v).into())));
|
||||||
)*
|
)*
|
||||||
|
@ -104,7 +105,7 @@ fn test_opts_trailing_comma() {
|
||||||
|
|
||||||
let opts = opts!(name, help, labels! {"test" => "hello", "foo" => "bar",},);
|
let opts = opts!(name, help, labels! {"test" => "hello", "foo" => "bar",},);
|
||||||
assert_eq!(opts.const_labels.len(), 2);
|
assert_eq!(opts.const_labels.len(), 2);
|
||||||
assert!(opts.const_labels.get("foo").is_some());
|
assert!(opts.const_labels.contains_key("foo"));
|
||||||
assert_eq!(opts.const_labels.get("foo").unwrap(), "bar");
|
assert_eq!(opts.const_labels.get("foo").unwrap(), "bar");
|
||||||
|
|
||||||
let opts = opts!(
|
let opts = opts!(
|
||||||
|
@ -114,7 +115,7 @@ fn test_opts_trailing_comma() {
|
||||||
labels! {"ans" => "42",},
|
labels! {"ans" => "42",},
|
||||||
);
|
);
|
||||||
assert_eq!(opts.const_labels.len(), 3);
|
assert_eq!(opts.const_labels.len(), 3);
|
||||||
assert!(opts.const_labels.get("ans").is_some());
|
assert!(opts.const_labels.contains_key("ans"));
|
||||||
assert_eq!(opts.const_labels.get("ans").unwrap(), "42");
|
assert_eq!(opts.const_labels.get("ans").unwrap(), "42");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +192,7 @@ fn test_histogram_opts_trailing_comma() {
|
||||||
assert_eq!(opts.common_opts.name, name);
|
assert_eq!(opts.common_opts.name, name);
|
||||||
assert_eq!(opts.common_opts.help, help);
|
assert_eq!(opts.common_opts.help, help);
|
||||||
assert_eq!(opts.buckets.len(), 2);
|
assert_eq!(opts.buckets.len(), 2);
|
||||||
assert!(opts.common_opts.const_labels.get("key").is_some());
|
assert!(opts.common_opts.const_labels.contains_key("key"));
|
||||||
assert_eq!(opts.common_opts.const_labels.get("key").unwrap(), "value");
|
assert_eq!(opts.common_opts.const_labels.get("key").unwrap(), "value");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +215,7 @@ fn test_histogram_opts_trailing_comma() {
|
||||||
macro_rules! register_counter {
|
macro_rules! register_counter {
|
||||||
(@of_type $TYPE:ident, $OPTS:expr) => {{
|
(@of_type $TYPE:ident, $OPTS:expr) => {{
|
||||||
let counter = $crate::$TYPE::with_opts($OPTS).unwrap();
|
let counter = $crate::$TYPE::with_opts($OPTS).unwrap();
|
||||||
$crate::register(Box::new(counter.clone())).map(|_| counter)
|
$crate::register(Box::new(counter.clone())).map(|()| counter)
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($OPTS:expr $(,)?) => {{
|
($OPTS:expr $(,)?) => {{
|
||||||
|
@ -260,7 +261,7 @@ fn test_register_counter_trailing_comma() {
|
||||||
macro_rules! register_counter_with_registry {
|
macro_rules! register_counter_with_registry {
|
||||||
(@of_type $TYPE: ident, $OPTS:expr, $REGISTRY:expr) => {{
|
(@of_type $TYPE: ident, $OPTS:expr, $REGISTRY:expr) => {{
|
||||||
let counter = $crate::$TYPE::with_opts($OPTS).unwrap();
|
let counter = $crate::$TYPE::with_opts($OPTS).unwrap();
|
||||||
$REGISTRY.register(Box::new(counter.clone())).map(|_| counter)
|
$REGISTRY.register(Box::new(counter.clone())).map(|()| counter)
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($OPTS:expr, $REGISTRY:expr $(,)?) => {{
|
($OPTS:expr, $REGISTRY:expr $(,)?) => {{
|
||||||
|
@ -361,14 +362,14 @@ fn test_register_int_counter() {
|
||||||
macro_rules! __register_counter_vec {
|
macro_rules! __register_counter_vec {
|
||||||
($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr) => {{
|
($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr) => {{
|
||||||
let counter_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap();
|
let counter_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap();
|
||||||
$crate::register(Box::new(counter_vec.clone())).map(|_| counter_vec)
|
$crate::register(Box::new(counter_vec.clone())).map(|()| counter_vec)
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr, $REGISTRY:expr) => {{
|
($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr, $REGISTRY:expr) => {{
|
||||||
let counter_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap();
|
let counter_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap();
|
||||||
$REGISTRY
|
$REGISTRY
|
||||||
.register(Box::new(counter_vec.clone()))
|
.register(Box::new(counter_vec.clone()))
|
||||||
.map(|_| counter_vec)
|
.map(|()| counter_vec)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,12 +544,12 @@ fn test_register_int_counter_vec() {
|
||||||
macro_rules! __register_gauge {
|
macro_rules! __register_gauge {
|
||||||
($TYPE:ident, $OPTS:expr) => {{
|
($TYPE:ident, $OPTS:expr) => {{
|
||||||
let gauge = $crate::$TYPE::with_opts($OPTS).unwrap();
|
let gauge = $crate::$TYPE::with_opts($OPTS).unwrap();
|
||||||
$crate::register(Box::new(gauge.clone())).map(|_| gauge)
|
$crate::register(Box::new(gauge.clone())).map(|()| gauge)
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($TYPE:ident, $OPTS:expr, $REGISTRY:expr) => {{
|
($TYPE:ident, $OPTS:expr, $REGISTRY:expr) => {{
|
||||||
let gauge = $crate::$TYPE::with_opts($OPTS).unwrap();
|
let gauge = $crate::$TYPE::with_opts($OPTS).unwrap();
|
||||||
$REGISTRY.register(Box::new(gauge.clone())).map(|_| gauge)
|
$REGISTRY.register(Box::new(gauge.clone())).map(|()| gauge)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -670,14 +671,14 @@ macro_rules! register_int_gauge_with_registry {
|
||||||
macro_rules! __register_gauge_vec {
|
macro_rules! __register_gauge_vec {
|
||||||
($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr $(,)?) => {{
|
($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr $(,)?) => {{
|
||||||
let gauge_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap();
|
let gauge_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap();
|
||||||
$crate::register(Box::new(gauge_vec.clone())).map(|_| gauge_vec)
|
$crate::register(Box::new(gauge_vec.clone())).map(|()| gauge_vec)
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{
|
($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{
|
||||||
let gauge_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap();
|
let gauge_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap();
|
||||||
$REGISTRY
|
$REGISTRY
|
||||||
.register(Box::new(gauge_vec.clone()))
|
.register(Box::new(gauge_vec.clone()))
|
||||||
.map(|_| gauge_vec)
|
.map(|()| gauge_vec)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -917,7 +918,7 @@ macro_rules! register_histogram {
|
||||||
|
|
||||||
($HOPTS:expr $(,)?) => {{
|
($HOPTS:expr $(,)?) => {{
|
||||||
let histogram = $crate::Histogram::with_opts($HOPTS).unwrap();
|
let histogram = $crate::Histogram::with_opts($HOPTS).unwrap();
|
||||||
$crate::register(Box::new(histogram.clone())).map(|_| histogram)
|
$crate::register(Box::new(histogram.clone())).map(|()| histogram)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -974,7 +975,7 @@ macro_rules! register_histogram_with_registry {
|
||||||
let histogram = $crate::Histogram::with_opts($HOPTS).unwrap();
|
let histogram = $crate::Histogram::with_opts($HOPTS).unwrap();
|
||||||
$REGISTRY
|
$REGISTRY
|
||||||
.register(Box::new(histogram.clone()))
|
.register(Box::new(histogram.clone()))
|
||||||
.map(|_| histogram)
|
.map(|()| histogram)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1030,7 +1031,7 @@ fn test_register_histogram_with_registry_trailing_comma() {
|
||||||
macro_rules! register_histogram_vec {
|
macro_rules! register_histogram_vec {
|
||||||
($HOPTS:expr, $LABELS_NAMES:expr $(,)?) => {{
|
($HOPTS:expr, $LABELS_NAMES:expr $(,)?) => {{
|
||||||
let histogram_vec = $crate::HistogramVec::new($HOPTS, $LABELS_NAMES).unwrap();
|
let histogram_vec = $crate::HistogramVec::new($HOPTS, $LABELS_NAMES).unwrap();
|
||||||
$crate::register(Box::new(histogram_vec.clone())).map(|_| histogram_vec)
|
$crate::register(Box::new(histogram_vec.clone())).map(|()| histogram_vec)
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($NAME:expr, $HELP:expr, $LABELS_NAMES:expr $(,)?) => {{
|
($NAME:expr, $HELP:expr, $LABELS_NAMES:expr $(,)?) => {{
|
||||||
|
@ -1060,7 +1061,7 @@ fn test_register_histogram_vec_trailing_comma() {
|
||||||
assert!(histogram_vec.is_ok());
|
assert!(histogram_vec.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a [`HistogramVec`][crate::HistogramVec] and registers to default registry.
|
/// Create a [`HistogramVec`][crate::HistogramVec] and registers to a custom registry.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -1094,7 +1095,7 @@ macro_rules! register_histogram_vec_with_registry {
|
||||||
let histogram_vec = $crate::HistogramVec::new($HOPTS, $LABELS_NAMES).unwrap();
|
let histogram_vec = $crate::HistogramVec::new($HOPTS, $LABELS_NAMES).unwrap();
|
||||||
$REGISTRY
|
$REGISTRY
|
||||||
.register(Box::new(histogram_vec.clone()))
|
.register(Box::new(histogram_vec.clone()))
|
||||||
.map(|_| histogram_vec)
|
.map(|()| histogram_vec)
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($NAME:expr, $HELP:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{
|
($NAME:expr, $HELP:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{
|
||||||
|
|
|
@ -177,7 +177,7 @@ impl Describer for Opts {
|
||||||
|
|
||||||
impl Ord for LabelPair {
|
impl Ord for LabelPair {
|
||||||
fn cmp(&self, other: &LabelPair) -> Ordering {
|
fn cmp(&self, other: &LabelPair) -> Ordering {
|
||||||
self.get_name().cmp(other.get_name())
|
self.name().cmp(other.name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
use std::hash::{BuildHasherDefault, Hasher};
|
||||||
|
|
||||||
|
/// Inspired by nohash-hasher, but we avoid the crate dependency because it's in public archive.
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
pub(crate) struct NoHashHasher(u64);
|
||||||
|
|
||||||
|
pub(crate) type BuildNoHashHasher = BuildHasherDefault<NoHashHasher>;
|
||||||
|
|
||||||
|
impl Hasher for NoHashHasher {
|
||||||
|
#[inline]
|
||||||
|
fn finish(&self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, _bytes: &[u8]) {
|
||||||
|
panic!("Invalid use of NoHashHasher");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_u64(&mut self, i: u64) {
|
||||||
|
self.0 = i;
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,13 @@ impl LabelPair {
|
||||||
self.name = v;
|
self.name = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.name()` instead")]
|
||||||
pub fn get_name(&self) -> &str {
|
pub fn get_name(&self) -> &str {
|
||||||
|
self.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the name of this label pair.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +43,13 @@ impl LabelPair {
|
||||||
self.value = v;
|
self.value = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.value()` instead")]
|
||||||
pub fn get_value(&self) -> &str {
|
pub fn get_value(&self) -> &str {
|
||||||
|
self.value()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of this label pair.
|
||||||
|
pub fn value(&self) -> &str {
|
||||||
&self.value
|
&self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +111,13 @@ impl Quantile {
|
||||||
self.quantile = v;
|
self.quantile = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.quantile()` instead")]
|
||||||
pub fn get_quantile(&self) -> f64 {
|
pub fn get_quantile(&self) -> f64 {
|
||||||
|
self.quantile()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the quantile of this quantile.
|
||||||
|
pub fn quantile(&self) -> f64 {
|
||||||
self.quantile
|
self.quantile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +125,13 @@ impl Quantile {
|
||||||
self.value = v;
|
self.value = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.value()` instead")]
|
||||||
pub fn get_value(&self) -> f64 {
|
pub fn get_value(&self) -> f64 {
|
||||||
|
self.value()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of this quantile.
|
||||||
|
pub fn value(&self) -> f64 {
|
||||||
self.value
|
self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,15 +153,27 @@ impl Summary {
|
||||||
self.sample_count = v;
|
self.sample_count = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.sample_count()` instead")]
|
||||||
pub fn get_sample_count(&self) -> u64 {
|
pub fn get_sample_count(&self) -> u64 {
|
||||||
self.sample_count
|
self.sample_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the sample count of this summary.
|
||||||
|
pub fn sample_count(&self) -> u64 {
|
||||||
|
self.sample_count
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_sample_sum(&mut self, v: f64) {
|
pub fn set_sample_sum(&mut self, v: f64) {
|
||||||
self.sample_sum = v;
|
self.sample_sum = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.sample_sum()` instead")]
|
||||||
pub fn get_sample_sum(&self) -> f64 {
|
pub fn get_sample_sum(&self) -> f64 {
|
||||||
|
self.sample_sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the sample sum of this summary.
|
||||||
|
pub fn sample_sum(&self) -> f64 {
|
||||||
self.sample_sum
|
self.sample_sum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +268,13 @@ impl Bucket {
|
||||||
self.cumulative_count = v;
|
self.cumulative_count = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.cumulative_count()` instead")]
|
||||||
pub fn get_cumulative_count(&self) -> u64 {
|
pub fn get_cumulative_count(&self) -> u64 {
|
||||||
|
self.cumulative_count()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the cumulative count of this bucket.
|
||||||
|
pub fn cumulative_count(&self) -> u64 {
|
||||||
self.cumulative_count
|
self.cumulative_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +282,13 @@ impl Bucket {
|
||||||
self.upper_bound = v;
|
self.upper_bound = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.upper_bound()` instead")]
|
||||||
pub fn get_upper_bound(&self) -> f64 {
|
pub fn get_upper_bound(&self) -> f64 {
|
||||||
|
self.upper_bound()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the upper bound of this bucket.
|
||||||
|
pub fn upper_bound(&self) -> f64 {
|
||||||
self.upper_bound
|
self.upper_bound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,6 +311,22 @@ impl Metric {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new metric with the specified label pairs.
|
||||||
|
pub fn from_label(label: Vec<LabelPair>) -> Self {
|
||||||
|
Metric {
|
||||||
|
label,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new metric with the specified gauge value.
|
||||||
|
pub fn from_gauge(gauge: Gauge) -> Self {
|
||||||
|
Metric {
|
||||||
|
gauge: gauge.into(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_label(&mut self, v: Vec<LabelPair>) {
|
pub fn set_label(&mut self, v: Vec<LabelPair>) {
|
||||||
self.label = v;
|
self.label = v;
|
||||||
}
|
}
|
||||||
|
@ -331,7 +395,13 @@ impl Metric {
|
||||||
self.timestamp_ms = v;
|
self.timestamp_ms = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.timestamp_ms()` instead")]
|
||||||
pub fn get_timestamp_ms(&self) -> i64 {
|
pub fn get_timestamp_ms(&self) -> i64 {
|
||||||
|
self.timestamp_ms()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the timestamp of this metric.
|
||||||
|
pub fn timestamp_ms(&self) -> i64 {
|
||||||
self.timestamp_ms
|
self.timestamp_ms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,7 +443,13 @@ impl MetricFamily {
|
||||||
self.name = v;
|
self.name = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.name()` instead")]
|
||||||
pub fn get_name(&self) -> &str {
|
pub fn get_name(&self) -> &str {
|
||||||
|
self.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the name of this metric family.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +457,13 @@ impl MetricFamily {
|
||||||
self.help = v;
|
self.help = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.help()` instead")]
|
||||||
pub fn get_help(&self) -> &str {
|
pub fn get_help(&self) -> &str {
|
||||||
|
self.help()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the help text of this metric family.
|
||||||
|
pub fn help(&self) -> &str {
|
||||||
&self.help
|
&self.help
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,8 +99,8 @@ impl ProcessCollector {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// proc_start_time init once because it is immutable
|
// proc_start_time init once because it is immutable
|
||||||
if let Ok(boot_time) = procfs::boot_time_secs() {
|
if let Ok(boot_time) = procfs::boot_time_secs() {
|
||||||
if let Ok(p) = procfs::process::Process::myself() {
|
if let Ok(stat) = procfs::process::Process::myself().and_then(|p| p.stat()) {
|
||||||
start_time.set(p.stat.starttime as i64 / *CLK_TCK + boot_time as i64);
|
start_time.set(stat.starttime as i64 / *CLK_TCK + boot_time as i64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
descs.extend(start_time.desc().into_iter().cloned());
|
descs.extend(start_time.desc().into_iter().cloned());
|
||||||
|
@ -156,25 +156,30 @@ impl Collector for ProcessCollector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// memory
|
let mut cpu_total_mfs = None;
|
||||||
self.vsize.set(p.stat.vsize as i64);
|
if let Ok(stat) = p.stat() {
|
||||||
self.rss.set(p.stat.rss * *PAGESIZE);
|
// memory
|
||||||
|
self.vsize.set(stat.vsize as i64);
|
||||||
|
self.rss.set((stat.rss as i64) * *PAGESIZE);
|
||||||
|
|
||||||
// cpu
|
// cpu
|
||||||
let cpu_total_mfs = {
|
let total = (stat.utime + stat.stime) / *CLK_TCK as u64;
|
||||||
let total = (p.stat.utime + p.stat.stime) / *CLK_TCK as u64;
|
|
||||||
let past = self.cpu_total.get();
|
let past = self.cpu_total.get();
|
||||||
self.cpu_total.inc_by(total - past);
|
// If two threads are collecting metrics at the same time,
|
||||||
|
// the cpu_total counter may have already been updated,
|
||||||
|
// and the subtraction may underflow.
|
||||||
|
self.cpu_total.inc_by(total.saturating_sub(past));
|
||||||
|
cpu_total_mfs = Some(self.cpu_total.collect());
|
||||||
|
|
||||||
self.cpu_total.collect()
|
// threads
|
||||||
};
|
self.threads.set(stat.num_threads);
|
||||||
|
}
|
||||||
// threads
|
|
||||||
self.threads.set(p.stat.num_threads);
|
|
||||||
|
|
||||||
// collect MetricFamilys.
|
// collect MetricFamilys.
|
||||||
let mut mfs = Vec::with_capacity(METRICS_NUMBER);
|
let mut mfs = Vec::with_capacity(METRICS_NUMBER);
|
||||||
mfs.extend(cpu_total_mfs);
|
if let Some(cpu) = cpu_total_mfs {
|
||||||
|
mfs.extend(cpu);
|
||||||
|
}
|
||||||
mfs.extend(self.open_fds.collect());
|
mfs.extend(self.open_fds.collect());
|
||||||
mfs.extend(self.max_fds.collect());
|
mfs.extend(self.max_fds.collect());
|
||||||
mfs.extend(self.vsize.collect());
|
mfs.extend(self.vsize.collect());
|
||||||
|
@ -190,14 +195,14 @@ lazy_static! {
|
||||||
static ref CLK_TCK: i64 = {
|
static ref CLK_TCK: i64 = {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::sysconf(libc::_SC_CLK_TCK)
|
libc::sysconf(libc::_SC_CLK_TCK)
|
||||||
}
|
}.into()
|
||||||
};
|
};
|
||||||
|
|
||||||
// getconf PAGESIZE
|
// getconf PAGESIZE
|
||||||
static ref PAGESIZE: i64 = {
|
static ref PAGESIZE: i64 = {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::sysconf(libc::_SC_PAGESIZE)
|
libc::sysconf(libc::_SC_PAGESIZE)
|
||||||
}
|
}.into()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
use protobuf::{EnumOrUnknown, MessageField};
|
||||||
|
|
||||||
|
use crate::proto::{
|
||||||
|
Bucket, Counter, Gauge, Histogram, LabelPair, Metric, MetricFamily, MetricType, Quantile,
|
||||||
|
Summary,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Metric {
|
||||||
|
/// Creates a new metric with the specified label pairs.
|
||||||
|
pub fn from_label(label: Vec<LabelPair>) -> Self {
|
||||||
|
Metric {
|
||||||
|
label,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new metric with the specified gauge value.
|
||||||
|
pub fn from_gauge(gauge: Gauge) -> Self {
|
||||||
|
Metric {
|
||||||
|
gauge: gauge.into(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.timestamp_ms()` instead")]
|
||||||
|
/// Returns the timestamp of this metric.
|
||||||
|
pub fn get_timestamp_ms(&self) -> i64 {
|
||||||
|
self.timestamp_ms()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the summary of this metric.
|
||||||
|
pub fn get_summary(&self) -> &MessageField<Summary> {
|
||||||
|
&self.summary
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the summary of this metric to the specified summary.
|
||||||
|
pub fn set_summary(&mut self, summary: Summary) {
|
||||||
|
self.summary = summary.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of the counter for this metric.
|
||||||
|
pub fn get_counter(&self) -> &MessageField<Counter> {
|
||||||
|
&self.counter
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the counter of this metric to the specified counter.
|
||||||
|
pub fn set_counter(&mut self, counter: Counter) {
|
||||||
|
self.counter = counter.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all label pairs associated with this metric.
|
||||||
|
pub fn get_label(&self) -> &[LabelPair] {
|
||||||
|
&self.label
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the label pairs associated with this metric.
|
||||||
|
pub fn set_label(&mut self, label: Vec<LabelPair>) {
|
||||||
|
self.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all label pairs associated with ownership.
|
||||||
|
pub fn take_label(&mut self) -> Vec<LabelPair> {
|
||||||
|
std::mem::take(&mut self.label)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the gauge of this metric.
|
||||||
|
pub fn get_gauge(&self) -> &MessageField<Gauge> {
|
||||||
|
&self.gauge
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the gauge of this metric to the specified gauge.
|
||||||
|
pub fn set_gauge(&mut self, gauge: Gauge) {
|
||||||
|
self.gauge = gauge.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the histogram of this metric.
|
||||||
|
pub fn get_histogram(&self) -> &MessageField<Histogram> {
|
||||||
|
&self.histogram
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the histogram of this metric to the specified histogram.
|
||||||
|
pub fn set_histogram(&mut self, histogram: Histogram) {
|
||||||
|
self.histogram = histogram.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MetricFamily {
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.name()` instead")]
|
||||||
|
/// Returns the name of this metric family.
|
||||||
|
pub fn get_name(&self) -> &str {
|
||||||
|
self.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.help()` instead")]
|
||||||
|
/// Returns the help text of this metric family.
|
||||||
|
pub fn get_help(&self) -> &str {
|
||||||
|
self.help()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the metric for this metric family (replaces any existing metrics).
|
||||||
|
pub fn set_metric(&mut self, metric: Vec<Metric>) {
|
||||||
|
self.metric = metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the type of this metric family.
|
||||||
|
pub fn get_field_type(&self) -> MetricType {
|
||||||
|
self.type_()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the type of this metric family.
|
||||||
|
pub fn set_field_type(&mut self, t: MetricType) {
|
||||||
|
self.type_ = t.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all metrics in this metric family.
|
||||||
|
pub fn get_metric(&self) -> &[Metric] {
|
||||||
|
&self.metric
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all metrics in this metric family mutably.
|
||||||
|
pub fn mut_metric(&mut self) -> &mut Vec<Metric> {
|
||||||
|
&mut self.metric
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all metrics in this metric family with taking ownership.
|
||||||
|
pub fn take_metric(&mut self) -> Vec<Metric> {
|
||||||
|
std::mem::take(&mut self.metric)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Summary {
|
||||||
|
/// Sets the quantiles for this summary.
|
||||||
|
pub fn set_quantile(&mut self, quantiles: Vec<Quantile>) {
|
||||||
|
self.quantile = quantiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the quantiles of this summary.
|
||||||
|
pub fn get_quantile(&self) -> &[Quantile] {
|
||||||
|
&self.quantile
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.sample_count()` instead")]
|
||||||
|
/// Returns the sample count of this summary.
|
||||||
|
pub fn get_sample_count(&self) -> u64 {
|
||||||
|
self.sample_count()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.sample_sum()` instead")]
|
||||||
|
/// Returns the sample sum of this summary.
|
||||||
|
pub fn get_sample_sum(&self) -> f64 {
|
||||||
|
self.sample_sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Quantile {
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.quantile()` instead")]
|
||||||
|
/// Returns the quantile of this quantile.
|
||||||
|
pub fn get_quantile(&self) -> f64 {
|
||||||
|
self.quantile()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.value()` instead")]
|
||||||
|
/// Returns the value of this quantile.
|
||||||
|
pub fn get_value(&self) -> f64 {
|
||||||
|
self.value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MessageFieldExt {
|
||||||
|
/// Returns the value of the wrapped gauge.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn get_value(&self) -> f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageFieldExt for MessageField<Gauge> {
|
||||||
|
fn get_value(&self) -> f64 {
|
||||||
|
self.value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageFieldExt for MessageField<Counter> {
|
||||||
|
fn get_value(&self) -> f64 {
|
||||||
|
self.value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Histogram {
|
||||||
|
/// Returns the sample count of this histogram.
|
||||||
|
pub fn get_sample_count(&self) -> u64 {
|
||||||
|
self.sample_count.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the sample sum of this histogram.
|
||||||
|
pub fn get_sample_sum(&self) -> f64 {
|
||||||
|
self.sample_sum.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all buckets in this histogram.
|
||||||
|
pub fn get_bucket(&self) -> &[Bucket] {
|
||||||
|
&self.bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the buckets of this histogram.
|
||||||
|
pub fn set_bucket(&mut self, bucket: Vec<Bucket>) {
|
||||||
|
self.bucket = bucket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bucket {
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.cumulative_count()` instead")]
|
||||||
|
/// Returns the cumulative count of this bucket.
|
||||||
|
pub fn get_cumulative_count(&self) -> u64 {
|
||||||
|
self.cumulative_count()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.upper_bound()` instead")]
|
||||||
|
/// Returns the upper bound of this bucket.
|
||||||
|
pub fn get_upper_bound(&self) -> f64 {
|
||||||
|
self.upper_bound()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LabelPair {
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.value()` instead")]
|
||||||
|
/// Returns the value of this label pair.
|
||||||
|
pub fn get_value(&self) -> &str {
|
||||||
|
self.value()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.14.0", note = "Please use `.name()` instead")]
|
||||||
|
/// Returns the name of this label pair.
|
||||||
|
pub fn get_name(&self) -> &str {
|
||||||
|
self.name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Counter> for MessageField<Counter> {
|
||||||
|
fn from(value: Counter) -> Self {
|
||||||
|
MessageField::some(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Gauge> for MessageField<Gauge> {
|
||||||
|
fn from(value: Gauge) -> Self {
|
||||||
|
MessageField::some(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Histogram> for MessageField<Histogram> {
|
||||||
|
fn from(value: Histogram) -> Self {
|
||||||
|
MessageField::some(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Summary> for MessageField<Summary> {
|
||||||
|
fn from(value: Summary) -> Self {
|
||||||
|
MessageField::some(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MetricType> for Option<EnumOrUnknown<MetricType>> {
|
||||||
|
fn from(value: MetricType) -> Self {
|
||||||
|
Some(EnumOrUnknown::from(value))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
use std::{collections::HashMap, fmt, sync::Arc};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
core::Collector,
|
||||||
|
proto::{Gauge, Metric, MetricFamily, MetricType},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A [Gauge] that returns the value from a provided function on every collect run.
|
||||||
|
///
|
||||||
|
/// This metric is the equivalant of Go's
|
||||||
|
/// <https://pkg.go.dev/github.com/prometheus/client_golang@v1.11.0/prometheus#GaugeFunc>
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use prometheus::{Registry, PullingGauge};
|
||||||
|
/// # // We are stubbing out std::thread::available_parallelism since it's not available in the
|
||||||
|
/// # // oldest Rust version that we support.
|
||||||
|
/// # fn available_parallelism() -> f64 { 0.0 }
|
||||||
|
///
|
||||||
|
/// let registry = Registry::new();
|
||||||
|
/// let gauge = PullingGauge::new(
|
||||||
|
/// "available_parallelism",
|
||||||
|
/// "The available parallelism, usually the numbers of logical cores.",
|
||||||
|
/// Box::new(|| available_parallelism())
|
||||||
|
/// ).unwrap();
|
||||||
|
/// registry.register(Box::new(gauge));
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PullingGauge {
|
||||||
|
desc: crate::core::Desc,
|
||||||
|
value: Arc<Box<dyn Fn() -> f64 + Send + Sync>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for PullingGauge {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("PullingGauge")
|
||||||
|
.field("desc", &self.desc)
|
||||||
|
.field("value", &"<opaque>")
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PullingGauge {
|
||||||
|
/// Create a new [`PullingGauge`].
|
||||||
|
pub fn new<S1: Into<String>, S2: Into<String>>(
|
||||||
|
name: S1,
|
||||||
|
help: S2,
|
||||||
|
value: Box<dyn Fn() -> f64 + Send + Sync>,
|
||||||
|
) -> crate::Result<Self> {
|
||||||
|
Ok(PullingGauge {
|
||||||
|
value: Arc::new(value),
|
||||||
|
desc: crate::core::Desc::new(name.into(), help.into(), Vec::new(), HashMap::new())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metric(&self) -> Metric {
|
||||||
|
let mut gauge = Gauge::default();
|
||||||
|
let getter = &self.value;
|
||||||
|
gauge.set_value(getter());
|
||||||
|
|
||||||
|
Metric::from_gauge(gauge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collector for PullingGauge {
|
||||||
|
fn desc(&self) -> Vec<&crate::core::Desc> {
|
||||||
|
vec![&self.desc]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect(&self) -> Vec<crate::proto::MetricFamily> {
|
||||||
|
let mut m = MetricFamily::default();
|
||||||
|
m.set_name(self.desc.fq_name.clone());
|
||||||
|
m.set_help(self.desc.help.clone());
|
||||||
|
m.set_field_type(MetricType::GAUGE);
|
||||||
|
m.set_metric(vec![self.metric()]);
|
||||||
|
vec![m]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::metrics::Collector;
|
||||||
|
#[cfg(feature = "protobuf")]
|
||||||
|
use crate::proto_ext::MessageFieldExt;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pulling_gauge() {
|
||||||
|
const VALUE: f64 = 10.0;
|
||||||
|
|
||||||
|
let gauge =
|
||||||
|
PullingGauge::new("test_gauge", "Purely for testing", Box::new(|| VALUE)).unwrap();
|
||||||
|
|
||||||
|
let metrics = gauge.collect();
|
||||||
|
assert_eq!(metrics.len(), 1);
|
||||||
|
|
||||||
|
assert_eq!(VALUE, metrics[0].get_metric()[0].get_gauge().get_value());
|
||||||
|
}
|
||||||
|
}
|
|
@ -277,9 +277,9 @@ mod tests {
|
||||||
let mut l = proto::LabelPair::new();
|
let mut l = proto::LabelPair::new();
|
||||||
l.set_name(case.0.to_owned());
|
l.set_name(case.0.to_owned());
|
||||||
let mut m = proto::Metric::new();
|
let mut m = proto::Metric::new();
|
||||||
m.set_label(from_vec!(vec![l]));
|
m.set_label(vec![l]);
|
||||||
let mut mf = proto::MetricFamily::new();
|
let mut mf = proto::MetricFamily::new();
|
||||||
mf.set_metric(from_vec!(vec![m]));
|
mf.set_metric(vec![m]);
|
||||||
let res = push_metrics("test", hostname_grouping_key(), "mockurl", vec![mf], None);
|
let res = push_metrics("test", hostname_grouping_key(), "mockurl", vec![mf], None);
|
||||||
assert!(format!("{}", res.unwrap_err()).contains(case.1));
|
assert!(format!("{}", res.unwrap_err()).contains(case.1));
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ impl RegistryCore {
|
||||||
let mut id_set = Vec::new();
|
let mut id_set = Vec::new();
|
||||||
let mut collector_id: u64 = 0;
|
let mut collector_id: u64 = 0;
|
||||||
for desc in c.desc() {
|
for desc in c.desc() {
|
||||||
if !id_set.iter().any(|id| *id == desc.id) {
|
if !id_set.contains(&desc.id) {
|
||||||
id_set.push(desc.id);
|
id_set.push(desc.id);
|
||||||
collector_id = collector_id.wrapping_add(desc.id);
|
collector_id = collector_id.wrapping_add(desc.id);
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ impl RegistryCore {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = mf.get_name().to_owned();
|
let name = mf.name().to_owned();
|
||||||
match mf_by_name.entry(name) {
|
match mf_by_name.entry(name) {
|
||||||
BEntry::Vacant(entry) => {
|
BEntry::Vacant(entry) => {
|
||||||
entry.insert(mf);
|
entry.insert(mf);
|
||||||
|
@ -166,8 +166,8 @@ impl RegistryCore {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (lp1, lp2) in lps1.iter().zip(lps2.iter()) {
|
for (lp1, lp2) in lps1.iter().zip(lps2.iter()) {
|
||||||
if lp1.get_value() != lp2.get_value() {
|
if lp1.value() != lp2.value() {
|
||||||
return lp1.get_value().cmp(lp2.get_value());
|
return lp1.value().cmp(lp2.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,17 +177,17 @@ impl RegistryCore {
|
||||||
// here, even for inconsistent metrics. So sort equal metrics
|
// here, even for inconsistent metrics. So sort equal metrics
|
||||||
// by their timestamp, with missing timestamps (implying "now")
|
// by their timestamp, with missing timestamps (implying "now")
|
||||||
// coming last.
|
// coming last.
|
||||||
m1.get_timestamp_ms().cmp(&m2.get_timestamp_ms())
|
m1.timestamp_ms().cmp(&m2.timestamp_ms())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write out MetricFamilies sorted by their name.
|
// Write out MetricFamilies sorted by their name.
|
||||||
mf_by_name
|
mf_by_name
|
||||||
.into_iter()
|
.into_values()
|
||||||
.map(|(_, mut m)| {
|
.map(|mut m| {
|
||||||
// Add registry namespace prefix, if any.
|
// Add registry namespace prefix, if any.
|
||||||
if let Some(ref namespace) = self.prefix {
|
if let Some(ref namespace) = self.prefix {
|
||||||
let prefixed = format!("{}_{}", namespace, m.get_name());
|
let prefixed = format!("{}_{}", namespace, m.name());
|
||||||
m.set_name(prefixed);
|
m.set_name(prefixed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,9 +204,9 @@ impl RegistryCore {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for metric in m.mut_metric().iter_mut() {
|
for metric in m.mut_metric().iter_mut() {
|
||||||
let mut labels: Vec<_> = metric.take_label().into();
|
let mut labels: Vec<_> = metric.take_label();
|
||||||
labels.append(&mut pairs.clone());
|
labels.append(&mut pairs.clone());
|
||||||
metric.set_label(labels.into());
|
metric.set_label(labels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m
|
m
|
||||||
|
@ -341,7 +341,10 @@ mod tests {
|
||||||
use crate::counter::{Counter, CounterVec};
|
use crate::counter::{Counter, CounterVec};
|
||||||
use crate::desc::Desc;
|
use crate::desc::Desc;
|
||||||
use crate::metrics::{Collector, Opts};
|
use crate::metrics::{Collector, Opts};
|
||||||
|
#[cfg(feature = "protobuf")]
|
||||||
use crate::proto;
|
use crate::proto;
|
||||||
|
#[cfg(feature = "protobuf")]
|
||||||
|
use crate::proto_ext::MessageFieldExt;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_registry() {
|
fn test_registry() {
|
||||||
|
@ -401,9 +404,9 @@ mod tests {
|
||||||
|
|
||||||
let mfs = r.gather();
|
let mfs = r.gather();
|
||||||
assert_eq!(mfs.len(), 3);
|
assert_eq!(mfs.len(), 3);
|
||||||
assert_eq!(mfs[0].get_name(), "test_2_counter");
|
assert_eq!(mfs[0].name(), "test_2_counter");
|
||||||
assert_eq!(mfs[1].get_name(), "test_a_counter");
|
assert_eq!(mfs[1].name(), "test_a_counter");
|
||||||
assert_eq!(mfs[2].get_name(), "test_b_counter");
|
assert_eq!(mfs[2].name(), "test_b_counter");
|
||||||
|
|
||||||
let r = Registry::new();
|
let r = Registry::new();
|
||||||
let opts = Opts::new("test", "test help")
|
let opts = Opts::new("test", "test help")
|
||||||
|
@ -473,7 +476,7 @@ mod tests {
|
||||||
|
|
||||||
let mfs = r.gather();
|
let mfs = r.gather();
|
||||||
assert_eq!(mfs.len(), 1);
|
assert_eq!(mfs.len(), 1);
|
||||||
assert_eq!(mfs[0].get_name(), "common_prefix_test_a_counter");
|
assert_eq!(mfs[0].name(), "common_prefix_test_a_counter");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -493,8 +496,8 @@ mod tests {
|
||||||
|
|
||||||
let mfs = r.gather();
|
let mfs = r.gather();
|
||||||
assert_eq!(mfs.len(), 2);
|
assert_eq!(mfs.len(), 2);
|
||||||
assert_eq!(mfs[0].get_name(), "test_a_counter");
|
assert_eq!(mfs[0].name(), "test_a_counter");
|
||||||
assert_eq!(mfs[1].get_name(), "test_vec");
|
assert_eq!(mfs[1].name(), "test_vec");
|
||||||
|
|
||||||
let mut needle = proto::LabelPair::default();
|
let mut needle = proto::LabelPair::default();
|
||||||
needle.set_name("tkey".to_string());
|
needle.set_name("tkey".to_string());
|
||||||
|
|
13
src/value.rs
13
src/value.rs
|
@ -37,11 +37,11 @@ pub struct Value<P: Atomic> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Atomic> Value<P> {
|
impl<P: Atomic> Value<P> {
|
||||||
pub fn new<D: Describer>(
|
pub fn new<D: Describer, V: AsRef<str>>(
|
||||||
describer: &D,
|
describer: &D,
|
||||||
val_type: ValueType,
|
val_type: ValueType,
|
||||||
val: P::T,
|
val: P::T,
|
||||||
label_values: &[&str],
|
label_values: &[V],
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let desc = describer.describe()?;
|
let desc = describer.describe()?;
|
||||||
let label_pairs = make_label_pairs(&desc, label_values)?;
|
let label_pairs = make_label_pairs(&desc, label_values)?;
|
||||||
|
@ -85,8 +85,7 @@ impl<P: Atomic> Value<P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn metric(&self) -> Metric {
|
pub fn metric(&self) -> Metric {
|
||||||
let mut m = Metric::default();
|
let mut m = Metric::from_label(self.label_pairs.clone());
|
||||||
m.set_label(from_vec!(self.label_pairs.clone()));
|
|
||||||
|
|
||||||
let val = self.get();
|
let val = self.get();
|
||||||
match self.val_type {
|
match self.val_type {
|
||||||
|
@ -110,12 +109,12 @@ impl<P: Atomic> Value<P> {
|
||||||
m.set_name(self.desc.fq_name.clone());
|
m.set_name(self.desc.fq_name.clone());
|
||||||
m.set_help(self.desc.help.clone());
|
m.set_help(self.desc.help.clone());
|
||||||
m.set_field_type(self.val_type.metric_type());
|
m.set_field_type(self.val_type.metric_type());
|
||||||
m.set_metric(from_vec!(vec![self.metric()]));
|
m.set_metric(vec![self.metric()]);
|
||||||
m
|
m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_label_pairs(desc: &Desc, label_values: &[&str]) -> Result<Vec<LabelPair>> {
|
pub fn make_label_pairs<V: AsRef<str>>(desc: &Desc, label_values: &[V]) -> Result<Vec<LabelPair>> {
|
||||||
if desc.variable_labels.len() != label_values.len() {
|
if desc.variable_labels.len() != label_values.len() {
|
||||||
return Err(Error::InconsistentCardinality {
|
return Err(Error::InconsistentCardinality {
|
||||||
expect: desc.variable_labels.len(),
|
expect: desc.variable_labels.len(),
|
||||||
|
@ -136,7 +135,7 @@ pub fn make_label_pairs(desc: &Desc, label_values: &[&str]) -> Result<Vec<LabelP
|
||||||
for (i, n) in desc.variable_labels.iter().enumerate() {
|
for (i, n) in desc.variable_labels.iter().enumerate() {
|
||||||
let mut label_pair = LabelPair::default();
|
let mut label_pair = LabelPair::default();
|
||||||
label_pair.set_name(n.clone());
|
label_pair.set_name(n.clone());
|
||||||
label_pair.set_value(label_values[i].to_owned());
|
label_pair.set_value(label_values[i].as_ref().to_owned());
|
||||||
label_pairs.push(label_pair);
|
label_pairs.push(label_pair);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
200
src/vec.rs
200
src/vec.rs
|
@ -2,7 +2,7 @@
|
||||||
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::Hasher;
|
use std::hash::{BuildHasher, Hasher};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
|
@ -11,6 +11,7 @@ use parking_lot::RwLock;
|
||||||
use crate::desc::{Desc, Describer};
|
use crate::desc::{Desc, Describer};
|
||||||
use crate::errors::{Error, Result};
|
use crate::errors::{Error, Result};
|
||||||
use crate::metrics::{Collector, Metric};
|
use crate::metrics::{Collector, Metric};
|
||||||
|
use crate::nohash::BuildNoHashHasher;
|
||||||
use crate::proto::{MetricFamily, MetricType};
|
use crate::proto::{MetricFamily, MetricType};
|
||||||
|
|
||||||
/// An interface for building a metric vector.
|
/// An interface for building a metric vector.
|
||||||
|
@ -21,12 +22,13 @@ pub trait MetricVecBuilder: Send + Sync + Clone {
|
||||||
type P: Describer + Sync + Send + Clone;
|
type P: Describer + Sync + Send + Clone;
|
||||||
|
|
||||||
/// `build` builds a [`Metric`] with option and corresponding label names.
|
/// `build` builds a [`Metric`] with option and corresponding label names.
|
||||||
fn build(&self, _: &Self::P, _: &[&str]) -> Result<Self::M>;
|
fn build<V: AsRef<str>>(&self, _: &Self::P, _: &[V]) -> Result<Self::M>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct MetricVecCore<T: MetricVecBuilder> {
|
pub(crate) struct MetricVecCore<T: MetricVecBuilder> {
|
||||||
pub children: RwLock<HashMap<u64, T::M>>,
|
// the key is pre-hashed, and so we use a no-hash hasher to avoid hashing again.
|
||||||
|
pub children: RwLock<HashMap<u64, T::M, BuildNoHashHasher>>,
|
||||||
pub desc: Desc,
|
pub desc: Desc,
|
||||||
pub metric_type: MetricType,
|
pub metric_type: MetricType,
|
||||||
pub new_metric: T,
|
pub new_metric: T,
|
||||||
|
@ -45,11 +47,14 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
|
||||||
for child in children.values() {
|
for child in children.values() {
|
||||||
metrics.push(child.metric());
|
metrics.push(child.metric());
|
||||||
}
|
}
|
||||||
m.set_metric(from_vec!(metrics));
|
m.set_metric(metrics);
|
||||||
m
|
m
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_metric_with_label_values(&self, vals: &[&str]) -> Result<T::M> {
|
pub fn get_metric_with_label_values<V>(&self, vals: &[V]) -> Result<T::M>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
let h = self.hash_label_values(vals)?;
|
let h = self.hash_label_values(vals)?;
|
||||||
|
|
||||||
if let Some(metric) = self.children.read().get(&h).cloned() {
|
if let Some(metric) = self.children.read().get(&h).cloned() {
|
||||||
|
@ -59,7 +64,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
|
||||||
self.get_or_create_metric(h, vals)
|
self.get_or_create_metric(h, vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_metric_with(&self, labels: &HashMap<&str, &str>) -> Result<T::M> {
|
pub fn get_metric_with<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<T::M>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
let h = self.hash_labels(labels)?;
|
let h = self.hash_labels(labels)?;
|
||||||
|
|
||||||
if let Some(metric) = self.children.read().get(&h).cloned() {
|
if let Some(metric) = self.children.read().get(&h).cloned() {
|
||||||
|
@ -70,7 +78,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
|
||||||
self.get_or_create_metric(h, &vals)
|
self.get_or_create_metric(h, &vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_label_values(&self, vals: &[&str]) -> Result<()> {
|
pub fn delete_label_values<V>(&self, vals: &[V]) -> Result<()>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
let h = self.hash_label_values(vals)?;
|
let h = self.hash_label_values(vals)?;
|
||||||
|
|
||||||
let mut children = self.children.write();
|
let mut children = self.children.write();
|
||||||
|
@ -81,7 +92,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&self, labels: &HashMap<&str, &str>) -> Result<()> {
|
pub fn delete<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<()>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
let h = self.hash_labels(labels)?;
|
let h = self.hash_labels(labels)?;
|
||||||
|
|
||||||
let mut children = self.children.write();
|
let mut children = self.children.write();
|
||||||
|
@ -97,7 +111,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
|
||||||
self.children.write().clear();
|
self.children.write().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hash_label_values(&self, vals: &[&str]) -> Result<u64> {
|
pub(crate) fn hash_label_values<V>(&self, vals: &[V]) -> Result<u64>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
if vals.len() != self.desc.variable_labels.len() {
|
if vals.len() != self.desc.variable_labels.len() {
|
||||||
return Err(Error::InconsistentCardinality {
|
return Err(Error::InconsistentCardinality {
|
||||||
expect: self.desc.variable_labels.len(),
|
expect: self.desc.variable_labels.len(),
|
||||||
|
@ -107,13 +124,16 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
|
||||||
|
|
||||||
let mut h = FnvHasher::default();
|
let mut h = FnvHasher::default();
|
||||||
for val in vals {
|
for val in vals {
|
||||||
h.write(val.as_bytes());
|
h.write(val.as_ref().as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(h.finish())
|
Ok(h.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_labels(&self, labels: &HashMap<&str, &str>) -> Result<u64> {
|
fn hash_labels<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<u64>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
if labels.len() != self.desc.variable_labels.len() {
|
if labels.len() != self.desc.variable_labels.len() {
|
||||||
return Err(Error::InconsistentCardinality {
|
return Err(Error::InconsistentCardinality {
|
||||||
expect: self.desc.variable_labels.len(),
|
expect: self.desc.variable_labels.len(),
|
||||||
|
@ -124,7 +144,7 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
|
||||||
let mut h = FnvHasher::default();
|
let mut h = FnvHasher::default();
|
||||||
for name in &self.desc.variable_labels {
|
for name in &self.desc.variable_labels {
|
||||||
match labels.get(&name.as_ref()) {
|
match labels.get(&name.as_ref()) {
|
||||||
Some(val) => h.write(val.as_bytes()),
|
Some(val) => h.write(val.as_ref().as_bytes()),
|
||||||
None => {
|
None => {
|
||||||
return Err(Error::Msg(format!(
|
return Err(Error::Msg(format!(
|
||||||
"label name {} missing in label map",
|
"label name {} missing in label map",
|
||||||
|
@ -137,11 +157,17 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
|
||||||
Ok(h.finish())
|
Ok(h.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_label_values<'a>(&self, labels: &'a HashMap<&str, &str>) -> Result<Vec<&'a str>> {
|
fn get_label_values<'a, V, S: BuildHasher>(
|
||||||
|
&'a self,
|
||||||
|
labels: &'a HashMap<&str, V, S>,
|
||||||
|
) -> Result<Vec<&'a str>>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
let mut values = Vec::new();
|
let mut values = Vec::new();
|
||||||
for name in &self.desc.variable_labels {
|
for name in &self.desc.variable_labels {
|
||||||
match labels.get(&name.as_ref()) {
|
match labels.get(&name.as_ref()) {
|
||||||
Some(val) => values.push(*val),
|
Some(val) => values.push(val.as_ref()),
|
||||||
None => {
|
None => {
|
||||||
return Err(Error::Msg(format!(
|
return Err(Error::Msg(format!(
|
||||||
"label name {} missing in label map",
|
"label name {} missing in label map",
|
||||||
|
@ -153,7 +179,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
|
||||||
Ok(values)
|
Ok(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_or_create_metric(&self, hash: u64, label_values: &[&str]) -> Result<T::M> {
|
fn get_or_create_metric<V>(&self, hash: u64, label_values: &[V]) -> Result<T::M>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
let mut children = self.children.write();
|
let mut children = self.children.write();
|
||||||
// Check exist first.
|
// Check exist first.
|
||||||
if let Some(metric) = children.get(&hash).cloned() {
|
if let Some(metric) = children.get(&hash).cloned() {
|
||||||
|
@ -188,7 +217,7 @@ impl<T: MetricVecBuilder> MetricVec<T> {
|
||||||
pub fn create(metric_type: MetricType, new_metric: T, opts: T::P) -> Result<MetricVec<T>> {
|
pub fn create(metric_type: MetricType, new_metric: T, opts: T::P) -> Result<MetricVec<T>> {
|
||||||
let desc = opts.describe()?;
|
let desc = opts.describe()?;
|
||||||
let v = MetricVecCore {
|
let v = MetricVecCore {
|
||||||
children: RwLock::new(HashMap::new()),
|
children: RwLock::new(HashMap::default()),
|
||||||
desc,
|
desc,
|
||||||
metric_type,
|
metric_type,
|
||||||
new_metric,
|
new_metric,
|
||||||
|
@ -221,7 +250,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
|
||||||
/// an alternative to avoid that type of mistake. For higher label numbers, the
|
/// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
/// latter has a much more readable (albeit more verbose) syntax, but it comes
|
/// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
/// with a performance overhead (for creating and processing the Labels map).
|
/// with a performance overhead (for creating and processing the Labels map).
|
||||||
pub fn get_metric_with_label_values(&self, vals: &[&str]) -> Result<T::M> {
|
pub fn get_metric_with_label_values<V>(&self, vals: &[V]) -> Result<T::M>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
self.v.get_metric_with_label_values(vals)
|
self.v.get_metric_with_label_values(vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +269,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
|
||||||
/// This method is used for the same purpose as
|
/// This method is used for the same purpose as
|
||||||
/// `get_metric_with_label_values`. See there for pros and cons of the two
|
/// `get_metric_with_label_values`. See there for pros and cons of the two
|
||||||
/// methods.
|
/// methods.
|
||||||
pub fn get_metric_with(&self, labels: &HashMap<&str, &str>) -> Result<T::M> {
|
pub fn get_metric_with<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<T::M>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
self.v.get_metric_with(labels)
|
self.v.get_metric_with(labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,14 +289,20 @@ impl<T: MetricVecBuilder> MetricVec<T> {
|
||||||
/// ).unwrap();
|
/// ).unwrap();
|
||||||
/// vec.with_label_values(&["404", "POST"]).inc()
|
/// vec.with_label_values(&["404", "POST"]).inc()
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with_label_values(&self, vals: &[&str]) -> T::M {
|
pub fn with_label_values<V>(&self, vals: &[V]) -> T::M
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
self.get_metric_with_label_values(vals).unwrap()
|
self.get_metric_with_label_values(vals).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `with` works as `get_metric_with`, but panics if an error occurs. The method allows
|
/// `with` works as `get_metric_with`, but panics if an error occurs. The method allows
|
||||||
/// neat syntax like:
|
/// neat syntax like:
|
||||||
/// httpReqs.with(Labels{"status":"404", "method":"POST"}).inc()
|
/// httpReqs.with(Labels{"status":"404", "method":"POST"}).inc()
|
||||||
pub fn with(&self, labels: &HashMap<&str, &str>) -> T::M {
|
pub fn with<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> T::M
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
self.get_metric_with(labels).unwrap()
|
self.get_metric_with(labels).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +318,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
|
||||||
/// alternative to avoid that type of mistake. For higher label numbers, the
|
/// alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
/// latter has a much more readable (albeit more verbose) syntax, but it comes
|
/// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
/// with a performance overhead (for creating and processing the Labels map).
|
/// with a performance overhead (for creating and processing the Labels map).
|
||||||
pub fn remove_label_values(&self, vals: &[&str]) -> Result<()> {
|
pub fn remove_label_values<V>(&self, vals: &[V]) -> Result<()>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
self.v.delete_label_values(vals)
|
self.v.delete_label_values(vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +333,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
|
||||||
///
|
///
|
||||||
/// This method is used for the same purpose as `delete_label_values`. See
|
/// This method is used for the same purpose as `delete_label_values`. See
|
||||||
/// there for pros and cons of the two methods.
|
/// there for pros and cons of the two methods.
|
||||||
pub fn remove(&self, labels: &HashMap<&str, &str>) -> Result<()> {
|
pub fn remove<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<()>
|
||||||
|
where
|
||||||
|
V: AsRef<str> + std::fmt::Debug,
|
||||||
|
{
|
||||||
self.v.delete(labels)
|
self.v.delete(labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,6 +395,40 @@ mod tests {
|
||||||
assert!(vec.remove(&labels3).is_err());
|
assert!(vec.remove(&labels3).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_counter_vec_with_owned_labels() {
|
||||||
|
let vec = CounterVec::new(
|
||||||
|
Opts::new("test_couter_vec", "test counter vec help"),
|
||||||
|
&["l1", "l2"],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let v1 = "v1".to_string();
|
||||||
|
let v2 = "v2".to_string();
|
||||||
|
|
||||||
|
let mut labels = HashMap::new();
|
||||||
|
labels.insert("l1", v1.clone());
|
||||||
|
labels.insert("l2", v2.clone());
|
||||||
|
assert!(vec.remove(&labels).is_err());
|
||||||
|
|
||||||
|
vec.with(&labels).inc();
|
||||||
|
assert!(vec.remove(&labels).is_ok());
|
||||||
|
assert!(vec.remove(&labels).is_err());
|
||||||
|
|
||||||
|
let mut labels2 = HashMap::new();
|
||||||
|
labels2.insert("l1", v2.clone());
|
||||||
|
labels2.insert("l2", v1.clone());
|
||||||
|
|
||||||
|
vec.with(&labels).inc();
|
||||||
|
assert!(vec.remove(&labels2).is_err());
|
||||||
|
|
||||||
|
vec.with(&labels).inc();
|
||||||
|
|
||||||
|
let mut labels3 = HashMap::new();
|
||||||
|
labels3.insert("l1", v1.clone());
|
||||||
|
assert!(vec.remove(&labels3).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_counter_vec_with_label_values() {
|
fn test_counter_vec_with_label_values() {
|
||||||
let vec = CounterVec::new(
|
let vec = CounterVec::new(
|
||||||
|
@ -365,6 +446,27 @@ mod tests {
|
||||||
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
|
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_counter_vec_with_owned_label_values() {
|
||||||
|
let vec = CounterVec::new(
|
||||||
|
Opts::new("test_vec", "test counter vec help"),
|
||||||
|
&["l1", "l2"],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let v1 = "v1".to_string();
|
||||||
|
let v2 = "v2".to_string();
|
||||||
|
let v3 = "v3".to_string();
|
||||||
|
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
|
||||||
|
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone()]).is_err());
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gauge_vec_with_labels() {
|
fn test_gauge_vec_with_labels() {
|
||||||
let vec = GaugeVec::new(
|
let vec = GaugeVec::new(
|
||||||
|
@ -388,6 +490,32 @@ mod tests {
|
||||||
assert!(vec.remove(&labels).is_err());
|
assert!(vec.remove(&labels).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gauge_vec_with_owned_labels() {
|
||||||
|
let vec = GaugeVec::new(
|
||||||
|
Opts::new("test_gauge_vec", "test gauge vec help"),
|
||||||
|
&["l1", "l2"],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let v1 = "v1".to_string();
|
||||||
|
let v2 = "v2".to_string();
|
||||||
|
|
||||||
|
let mut labels = HashMap::new();
|
||||||
|
labels.insert("l1", v1.clone());
|
||||||
|
labels.insert("l2", v2.clone());
|
||||||
|
assert!(vec.remove(&labels).is_err());
|
||||||
|
|
||||||
|
vec.with(&labels).inc();
|
||||||
|
vec.with(&labels).dec();
|
||||||
|
vec.with(&labels).add(42.0);
|
||||||
|
vec.with(&labels).sub(42.0);
|
||||||
|
vec.with(&labels).set(42.0);
|
||||||
|
|
||||||
|
assert!(vec.remove(&labels).is_ok());
|
||||||
|
assert!(vec.remove(&labels).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gauge_vec_with_label_values() {
|
fn test_gauge_vec_with_label_values() {
|
||||||
let vec = GaugeVec::new(
|
let vec = GaugeVec::new(
|
||||||
|
@ -410,6 +538,32 @@ mod tests {
|
||||||
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
|
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gauge_vec_with_owned_label_values() {
|
||||||
|
let vec = GaugeVec::new(
|
||||||
|
Opts::new("test_gauge_vec", "test gauge vec help"),
|
||||||
|
&["l1", "l2"],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let v1 = "v1".to_string();
|
||||||
|
let v2 = "v2".to_string();
|
||||||
|
let v3 = "v3".to_string();
|
||||||
|
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
|
||||||
|
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).dec();
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).add(42.0);
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).sub(42.0);
|
||||||
|
vec.with_label_values(&[v1.clone(), v2.clone()]).set(42.0);
|
||||||
|
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone()]).is_err());
|
||||||
|
assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vec_get_metric_with() {
|
fn test_vec_get_metric_with() {
|
||||||
let vec = CounterVec::new(
|
let vec = CounterVec::new(
|
||||||
|
@ -428,7 +582,7 @@ mod tests {
|
||||||
let label_pairs = m.get_label();
|
let label_pairs = m.get_label();
|
||||||
assert_eq!(label_pairs.len(), labels.len());
|
assert_eq!(label_pairs.len(), labels.len());
|
||||||
for lp in label_pairs.iter() {
|
for lp in label_pairs.iter() {
|
||||||
assert_eq!(lp.get_value(), labels[lp.get_name()]);
|
assert_eq!(lp.value(), labels[lp.name()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,13 @@ edition = "2018"
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
syn = { version = "1.0", features = ["full", "extra-traits"] }
|
syn = { version = "2.0", features = ["full", "extra-traits"] }
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.5"
|
||||||
prometheus = { path = "../" }
|
prometheus = { path = "../" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
@ -196,7 +196,7 @@ impl AutoFlushTokensBuilder {
|
||||||
|
|
||||||
let offset_fetchers = builder_contexts
|
let offset_fetchers = builder_contexts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|m| offset_fetcher(m))
|
.map(offset_fetcher)
|
||||||
.collect::<Vec<Tokens>>();
|
.collect::<Vec<Tokens>>();
|
||||||
|
|
||||||
let delegator_tokens = if metric_type.to_string().contains("Counter") {
|
let delegator_tokens = if metric_type.to_string().contains("Counter") {
|
||||||
|
@ -714,7 +714,6 @@ impl<'a> MetricBuilderContext<'a> {
|
||||||
})#local_suffix_call,
|
})#local_suffix_call,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let prev_labels_ident = prev_labels_ident;
|
|
||||||
quote! {
|
quote! {
|
||||||
#name: #member_type::from(
|
#name: #member_type::from(
|
||||||
#(
|
#(
|
||||||
|
|
|
@ -300,7 +300,6 @@ impl<'a> MetricBuilderContext<'a> {
|
||||||
})#local_suffix_call,
|
})#local_suffix_call,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let prev_labels_ident = prev_labels_ident;
|
|
||||||
quote! {
|
quote! {
|
||||||
#name: #member_type::from(
|
#name: #member_type::from(
|
||||||
#(
|
#(
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub fn make_static_metric(input: TokenStream) -> TokenStream {
|
||||||
TokensBuilder::build(body).into()
|
TokensBuilder::build(body).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build auto flush able static metrics.
|
/// Build auto flushable static metrics.
|
||||||
/// refer to https://github.com/tikv/rust-prometheus/tree/master/static-metric for more info.
|
/// refer to https://github.com/tikv/rust-prometheus/tree/master/static-metric for more info.
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn make_auto_flush_static_metric(input: TokenStream) -> TokenStream {
|
pub fn make_auto_flush_static_metric(input: TokenStream) -> TokenStream {
|
||||||
|
@ -52,7 +52,7 @@ pub fn make_auto_flush_static_metric(input: TokenStream) -> TokenStream {
|
||||||
AutoFlushTokensBuilder::build(body).into()
|
AutoFlushTokensBuilder::build(body).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instantiate a auto flush able static metric struct from a HistogramVec or CounterVec.
|
/// Instantiate an auto flushable static metric struct from a HistogramVec or CounterVec.
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn auto_flush_from(input: TokenStream) -> TokenStream {
|
pub fn auto_flush_from(input: TokenStream) -> TokenStream {
|
||||||
let def: AutoFlushFromDef = syn::parse(input).unwrap();
|
let def: AutoFlushFromDef = syn::parse(input).unwrap();
|
||||||
|
|
|
@ -10,6 +10,7 @@ use syn::*;
|
||||||
|
|
||||||
/// Matches `label_enum` keyword.
|
/// Matches `label_enum` keyword.
|
||||||
struct LabelEnum {
|
struct LabelEnum {
|
||||||
|
#[allow(dead_code)]
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue