Compare commits

...

28 Commits

Author SHA1 Message Date
Matheus Cardoso 1d3174bf5d
chore(histogram): Simplify code by using std lib method (#547)
* chore(histogram): Simplify code by using std lib method

Leverage existing standard library method to simplify implementation. No changes to public API or MSRV.

Signed-off-by: Matheus <cardosaum@pm.me>

* refactor(histogram): Remove `duration_to_seconds` function

Signed-off-by: Matheus <cardosaum@pm.me>

* tests(histogram): Remove `duration_to_second` test

After removing the aforementioned method in a previous commit, this test became obsolete.

Signed-off-by: Matheus <cardosaum@pm.me>

---------

Signed-off-by: Matheus <cardosaum@pm.me>
Co-authored-by: Luca Bruno <lucab@lucabruno.net>
2025-04-16 09:37:45 +02:00
Matheus Cardoso 53d306635f
chore(registry): Fix linter warning (#548)
Signed-off-by: Matheus <cardosaum@pm.me>
2025-04-14 09:55:35 +02:00
Luca Bruno e07efb4f37
prometheus: release 0.14.0 (#545)
Signed-off-by: Luca BRUNO <lucab@lucabruno.net>
2025-03-27 16:33:36 +08:00
Liam Gray 26e46ec03a
Hashing improvements (#532)
* perf: optimise CounterVec hashing, enable other hashers

Signed-off-by: Liam Gray <hoxxep@gmail.com>

* Remove nohash-hasher dependency

Signed-off-by: Liam Gray <hoxxep@gmail.com>

* Restrict nohash visibility explicitly

Signed-off-by: Liam Gray <hoxxep@gmail.com>

---------

Signed-off-by: Liam Gray <hoxxep@gmail.com>
2025-03-21 10:44:25 +01:00
dependabot[bot] e17c5ced2b
build(deps): update procfs requirement from ^0.16 to ^0.17 (#543)
Updates the requirements on [procfs](https://github.com/eminence/procfs) to permit the latest version.
- [Release notes](https://github.com/eminence/procfs/releases)
- [Commits](https://github.com/eminence/procfs/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: procfs
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-20 09:06:54 +01:00
dependabot[bot] e5809b7ab9
build(deps): update hyper requirement from ^0.14 to ^1.4 (#524)
* build(deps): update hyper requirement from ^0.14 to ^1.4

Updates the requirements on [hyper](https://github.com/hyperium/hyper) to permit the latest version.
- [Release notes](https://github.com/hyperium/hyper/releases)
- [Changelog](https://github.com/hyperium/hyper/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/hyper/compare/v0.14.0...v1.4.0)

---
updated-dependencies:
- dependency-name: hyper
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* examples: fix hyper server example

Signed-off-by: Luca BRUNO <lucab@lucabruno.net>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Luca BRUNO <lucab@lucabruno.net>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Luca BRUNO <lucab@lucabruno.net>
2025-03-19 16:16:45 +01:00
Sander van Harmelen 4a0e282888
Use AsRef<str> for owned label values (#537)
Signed-off-by: Sander van Harmelen <sander@vanharmelen.nl>
Co-authored-by: Luca Bruno <lucab@lucabruno.net>
2025-03-19 10:52:55 +01:00
Sven Kanoldt c3865f3c40
cargo: upgrade to protobuf 3.7 (#541)
* feat!: upgrade to protobuf 3.7

- keep some of the `get_` and `set_` methods around and mark them as deprecated
- keep api changes to a minimal

Signed-off-by: Sven Kanoldt <sven+oss@d34dl0ck.me>

* fix: fix all issues related to api combatibility when the protobuf feature is not enabled

Signed-off-by: Sven Kanoldt <sven+oss@d34dl0ck.me>

* implement review feedback and close all minor remarks

Signed-off-by: Sven Kanoldt <sven+oss@d34dl0ck.me>

* fix ci errors

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>

---------

Signed-off-by: Sven Kanoldt <sven+oss@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
2025-03-19 10:21:43 +01:00
Antonio Nuno Monteiro 7e4e6f2d33
docs: fix `register_histogram_vec_with_registry` docstring (#528)
Signed-off-by: Antonio Nuno Monteiro <anmonteiro@gmail.com>
Co-authored-by: Luca Bruno <lucab@lucabruno.net>
2025-03-14 15:54:08 +01:00
Sander van Harmelen 5b62f4b78b
Fix LSP and Clippy warnings and errors (#540)
Signed-off-by: Sander van Harmelen <sander@vanharmelen.nl>
Co-authored-by: Luca Bruno <lucab@lucabruno.net>
2025-03-13 17:49:22 +01:00
Luca Bruno 52d76fc2d8
cargo: bump MSRV to 1.81 (#539)
Signed-off-by: Luca BRUNO <lucab@lucabruno.net>
2025-03-13 00:00:20 +08:00
Kai Ren 3bd0e82f1f
Upgrade `thiserror` crate from 1.0 to 2.0 version (#534)
Signed-off-by: Kai Ren <tyranron@gmail.com>
2025-03-12 09:15:40 +01:00
John Vandenberg 45c43c9130
README.md: Document protobuf feature (#531)
Signed-off-by: John Vandenberg <jayvdb@gmail.com>
2024-12-13 10:41:36 +01:00
Eval Exec f0c9bc271b
static-metric: fix typos in docstrings (#479)
It should be `flashable` instead of `flash able`

Signed-off-by: Eval EXEC <execvy@gmail.com>
2024-05-06 16:00:46 +02:00
Luca Bruno 04fce2f3bf
prometheus: release 0.13.4 (#520)
Signed-off-by: Luca BRUNO <lucab@lucabruno.net>
2024-05-04 18:51:47 +02:00
dependabot[bot] 6e435db331
build(deps): update reqwest requirement from ^0.11 to ^0.12 (#516)
Updates the requirements on [reqwest](https://github.com/seanmonstar/reqwest) to permit the latest version.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Luca Bruno <luca.bruno@coreos.com>
2024-05-03 19:24:25 +02:00
Matt Weber 439e3b8c14
Prevent `clippy::ignored_unit_patterns` in macro expansions (#497)
This is a pedantic lint added in 1.73.0. Because it occurs inside a
macro expansion, the lint is triggered from user code. The justification
given by the lint definition is:

> Matching with `()` explicitly instead of `_` outlines the fact that
> the pattern contains no data. Also it would detect a type change that
> `_` would ignore.

Removing the lint requires a trivial change. It does introduce the
possibility for compilation errors in the event the return type of the
function currently returning `()` changes, but that seems like more of a
benefit than a drawback. In these cases, it seems unlikely that the
return type in question will change in the future.

The user experience can be seen by linting the examples:

```
% cargo +nightly clippy --examples -- -A clippy::all -W clippy::ignored_unit_patterns -Z macro-backtrace
   Compiling prometheus v0.13.3 (/home/matt/src/rust-prometheus)
...
warning: matching over `()` is more explicit
   --> /home/matt/src/rust-prometheus/src/macros.rs:217:58
    |
214 |   macro_rules! register_counter {
    |   -----------------------------
    |   |
    |   in this expansion of `register_counter!` (#1)
    |   in this expansion of `register_counter!` (#2)
    |   in this expansion of `register_counter!` (#3)
...
217 |           $crate::register(Box::new(counter.clone())).map(|_| counter)
    |                                                            ^
...
221 |           register_counter!(@of_type Counter, $OPTS)
    |           ------------------------------------------ in this macro invocation (#3)
...
225 |           register_counter!(opts!($NAME, $HELP))
    |           -------------------------------------- in this macro invocation (#2)
    |
   ::: examples/example_push.rs:16:40
    |
16  |       static ref PUSH_COUNTER: Counter = register_counter!(
    |  ________________________________________-
17  | |         "example_push_total",
18  | |         "Total number of prometheus client pushed."
19  | |     )
    | |_____- in this macro invocation (#1)
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns
    = note: requested on the command line with `-W clippy::ignored-unit-patterns`
...
```

Signed-off-by: Matt Weber <30441572+mweber15@users.noreply.github.com>
Co-authored-by: Luca Bruno <luca.bruno@coreos.com>
2024-05-03 18:49:46 +02:00
Luca Bruno bf696d642c
ci: bump MSRV to fix test jobs (#519)
* ci: bump MSRV to fix test jobs

Signed-off-by: Luca BRUNO <lucab@lucabruno.net>

* src: fix new linting warnings

Signed-off-by: Luca BRUNO <lucab@lucabruno.net>

---------

Signed-off-by: Luca BRUNO <lucab@lucabruno.net>
2024-05-03 18:34:36 +02:00
Tamo b7e874524f
Let the cargo.toml know which example requires which features (#511)
Signed-off-by: Tamo <tamo@meilisearch.com>
2024-05-03 10:37:22 +02:00
Seth Westphal f49c724df0
cargo: update all dependencies (#504)
* Update dependencies.
Signed-off-by: Seth Westphal <westy92@gmail.com>

* Updates.

Signed-off-by: Seth Westphal <westy92@gmail.com>

---------

Signed-off-by: Seth Westphal <westy92@gmail.com>
2023-11-23 11:56:11 +01:00
Bruce Mitchener 76a634587a
ci: Update to `actions/checkout@v4`. (#499)
The main thing here is an update on the GitHub side for what version
of Node is being used to stay ahead of their deprecation schedule.

Signed-off-by: Bruce Mitchener <bruce.mitchener@gmail.com>
2023-11-20 11:35:10 +01:00
Bruce Mitchener a72d8d7db8
Bump MSRV to 1.65 to fix CI. (#505)
Signed-off-by: Bruce Mitchener <bruce.mitchener@gmail.com>
2023-11-20 10:27:48 +01:00
Bruce Mitchener 7a9adcacd0
ci: Update badge info to not refer to Travis CI (#500)
Signed-off-by: Bruce Mitchener <bruce.mitchener@gmail.com>
2023-11-20 16:32:10 +08:00
Croxx 6e81890773
bump MSRV to 1.60.0 (#491)
Signed-off-by: MrCroxx <mrcroxx@outlook.com>
2023-05-17 10:28:32 +02:00
nickelc 36acf4753d
Replace unmaintained/outdated github actions (#476)
The toolchain is now installed with `dtolnay/rust-toolchain`.

Signed-off-by: Constantin Nickel <constantin.nickel@gmail.com>
2023-02-01 14:46:31 +00:00
Kai Ren a7b8f09b8f
pulling_gauge: fix build with --no-default-features (#473)
* Add CI checks for `--no-default-features`

Signed-off-by: tyranron <tyranron@gmail.com>

* Fix broken compilation

Signed-off-by: tyranron <tyranron@gmail.com>

Signed-off-by: tyranron <tyranron@gmail.com>
2022-12-22 10:59:36 +00:00
Jan Berktold 8b462b1945
Add PullingGauge (#405)
Signed-off-by: Jan Berktold <jberktold@roblox.com>

Signed-off-by: Jan Berktold <jberktold@roblox.com>
2022-11-15 10:49:12 +00:00
Luca Bruno 422b3afcf4
ci: add a job for minimum toolchain (MSRV) (#467)
This adds a new CI job to verify building with a minimum supported
Rust version (MSRV), currently set at 1.57.
This is done in preparation for a future edition-2021 switch.

Signed-off-by: Luca BRUNO <luca.bruno@coreos.com>

Signed-off-by: Luca BRUNO <luca.bruno@coreos.com>
2022-10-24 07:04:02 +00:00
34 changed files with 2077 additions and 1716 deletions

View File

@ -9,7 +9,9 @@ on:
env:
CARGO_TERM_COLOR: always
# Pinned toolchain for linting and benchmarks
ACTIONS_LINTS_TOOLCHAIN: 1.63.0
ACTIONS_LINTS_TOOLCHAIN: 1.81.0
# Minimum supported Rust version (MSRV)
ACTION_MSRV_TOOLCHAIN: 1.81.0
EXTRA_FEATURES: "protobuf push process"
jobs:
@ -18,16 +20,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: "stable"
default: true
uses: dtolnay/rust-toolchain@stable
- name: cargo build
run: cargo build
- name: cargo test
run: cargo test
- name: cargo test (no default features)
run: cargo test --no-default-features
- name: cargo test (extra features)
run: cargo test --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
- name: cargo package
@ -43,12 +44,11 @@ jobs:
- "nightly"
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.channel }}
default: true
- name: cargo build
run: cargo build
- name: cargo test
@ -57,33 +57,51 @@ jobs:
run: cargo build -p prometheus-static-metric --examples --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
- name: cargo test (static-metric)
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:
name: "Lints, pinned toolchain"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env['ACTIONS_LINTS_TOOLCHAIN'] }}
default: true
components: rustfmt, clippy
- name: cargo fmt (check)
run: cargo fmt --all -- --check -l
- name: cargo clippy
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:
name: "Benchmarks (criterion)"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env['ACTIONS_LINTS_TOOLCHAIN'] }}
default: true
- name: cargo bench (prometheus)
run: cargo bench -p prometheus
- name: cargo bench (prometheus-static-metric)

View File

@ -1,5 +1,43 @@
# 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)

View File

@ -9,17 +9,15 @@ license = "Apache-2.0"
name = "prometheus"
readme = "README.md"
repository = "https://github.com/tikv/rust-prometheus"
version = "0.13.3"
[badges]
travis-ci = { repository = "pingcap/rust-prometheus" }
rust-version = "1.81"
version = "0.14.0"
[package.metadata.docs.rs]
features = ["nightly"]
[features]
default = ["protobuf"]
gen = ["protobuf-codegen-pure"]
gen = ["protobuf-codegen"]
nightly = ["libc"]
process = ["libc", "procfs"]
push = ["reqwest", "libc", "protobuf"]
@ -30,22 +28,23 @@ fnv = "^1.0"
lazy_static = "^1.4"
libc = { version = "^0.2", optional = true }
parking_lot = "^0.12"
protobuf = { version = "^2.0", optional = true }
protobuf = { version = "^3.7.2", optional = true }
memchr = "^2.3"
reqwest = { version = "^0.11", features = ["blocking"], optional = true }
thiserror = "^1.0"
reqwest = { version = "^0.12", features = ["blocking"], optional = true }
thiserror = "^2.0"
[target.'cfg(target_os = "linux")'.dependencies]
procfs = { version = "^0.14", optional = true, default-features = false }
procfs = { version = "^0.17", optional = true, default-features = false }
[dev-dependencies]
criterion = "0.4"
criterion = "0.5"
getopts = "^0.2"
hyper = { version = "^0.14", features = ["server", "http1", "tcp"] }
tokio = { version = "^1.0", features = ["macros", "rt-multi-thread"] }
hyper = { version = "^1.6", features = ["http1", "server"] }
hyper-util = { version = "^0.1", features = ["http1", "server", "tokio"] }
tokio = { version = "^1.0", features = ["macros", "net", "rt-multi-thread"] }
[build-dependencies]
protobuf-codegen-pure = { version = "^2.0", optional = true }
protobuf-codegen = { version = "^3.7.2", optional = true }
[workspace]
members = ["static-metric"]
@ -73,3 +72,11 @@ harness = false
[[bench]]
name = "text_encoder"
harness = false
[[example]]
name = "example_push"
required-features = ["push"]
[[example]]
name = "example_process_collector"
required-features = ["process"]

View File

@ -1,6 +1,6 @@
# Prometheus Rust client library
[![Build Status](https://travis-ci.org/tikv/rust-prometheus.svg?branch=master)](https://travis-ci.org/pingcap/rust-prometheus)
[![Build Status](https://github.com/tikv/rust-prometheus/actions/workflows/rust.yml/badge.svg)](https://github.com/tikv/rust-prometheus/actions/workflows/rust.yml)
[![docs.rs](https://docs.rs/prometheus/badge.svg)](https://docs.rs/prometheus)
[![crates.io](https://img.shields.io/crates/v/prometheus.svg)](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):
- `protobuf`: Protobuf support, enabled by default.
- `gen`: To generate protobuf client with the latest protobuf version instead of
using the pre-generated client.

View File

@ -11,7 +11,8 @@
// See the License for the specific language governing permissions and
// 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 std::collections::HashMap;
use std::sync::{atomic, Arc};
@ -24,7 +25,11 @@ fn bench_counter_with_label_values(c: &mut Criterion) {
)
.unwrap();
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("one", "eins");
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_concurrent_write,
bench_counter_with_mapped_labels,
bench_counter_with_mapped_labels_fnv,
bench_counter_with_prepared_mapped_labels,
bench_int_counter_no_labels,
bench_int_counter_no_labels_concurrent_write,

View File

@ -2,13 +2,12 @@
#[cfg(feature = "gen")]
fn generate_protobuf_binding_file() {
protobuf_codegen_pure::run(protobuf_codegen_pure::Args {
out_dir: "proto",
input: &["proto/proto_model.proto"],
includes: &["proto"],
..Default::default()
})
.unwrap();
protobuf_codegen::Codegen::new()
.out_dir("proto")
.inputs(["proto/proto_model.proto"])
.includes(["proto"])
.run()
.expect("Protobuf codegen failed");
}
#[cfg(not(feature = "gen"))]

View File

@ -1,14 +1,20 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
use hyper::{
header::CONTENT_TYPE,
service::{make_service_fn, service_fn},
Body, Request, Response, Server,
};
use prometheus::{Counter, Encoder, Gauge, HistogramVec, TextEncoder};
use std::net::SocketAddr;
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 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! {
static ref HTTP_COUNTER: Counter = register_counter!(opts!(
@ -31,22 +37,20 @@ lazy_static! {
.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();
HTTP_COUNTER.inc();
let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["all"]).start_timer();
let metric_families = prometheus::gather();
let mut buffer = vec![];
encoder.encode(&metric_families, &mut buffer).unwrap();
HTTP_BODY_GAUGE.set(buffer.len() as f64);
let body = encoder.encode_to_string(&metric_families)?;
HTTP_BODY_GAUGE.set(body.len() as f64);
let response = Response::builder()
.status(200)
.header(CONTENT_TYPE, encoder.format_type())
.body(Body::from(buffer))
.unwrap();
.body(body)?;
timer.observe_duration();
@ -54,15 +58,18 @@ async fn serve_req(_req: Request<Body>) -> Result<Response<Body>, hyper::Error>
}
#[tokio::main]
async fn main() {
let addr = ([127, 0, 0, 1], 9898).into();
async fn main() -> Result<(), BoxedErr> {
let addr: SocketAddr = ([127, 0, 0, 1], 9898).into();
println!("Listening on http://{}", addr);
let listener = TcpListener::bind(addr).await?;
let serve_future = Server::bind(&addr).serve(make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(serve_req))
}));
loop {
let (stream, _) = listener.accept().await?;
let io = TokioIo::new(stream);
if let Err(err) = serve_future.await {
eprintln!("server error: {}", err);
let service = service_fn(serve_req);
if let Err(err) = http1::Builder::new().serve_connection(io, service).await {
eprintln!("server error: {:?}", err);
};
}
}

View File

@ -1,6 +1,5 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
#[cfg(all(feature = "process", target_os = "linux"))]
fn main() {
use std::thread;
use std::time::Duration;
@ -21,11 +20,3 @@ fn main() {
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"#
);
}

View File

@ -1,7 +1,5 @@
// 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::thread;
use std::time;
@ -25,7 +23,6 @@ lazy_static! {
.unwrap();
}
#[cfg(feature = "push")]
fn main() {
let args: Vec<String> = env::args().collect();
let program = args[0].clone();
@ -68,11 +65,3 @@ fn main() {
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"#
);
}

3
proto/mod.rs Normal file
View File

@ -0,0 +1,3 @@
// @generated
pub mod proto_model;

File diff suppressed because it is too large Load Diff

View File

@ -241,8 +241,8 @@ impl AtomicU64 {
#[cfg(test)]
mod test {
use std::f64;
use std::f64::consts::PI;
use std::f64::{self, EPSILON};
use super::*;
@ -251,7 +251,7 @@ mod test {
let table: Vec<f64> = vec![0.0, 1.0, PI, f64::MIN, f64::MAX];
for f in table {
assert!((f - AtomicF64::new(f).get()).abs() < EPSILON);
assert!((f - AtomicF64::new(f).get()).abs() < f64::EPSILON);
}
}

View File

@ -44,10 +44,10 @@ impl<P: Atomic> GenericCounter<P> {
/// Create a [`GenericCounter`] with the `opts` options.
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)?;
Ok(Self { v: Arc::new(v) })
}
@ -126,7 +126,7 @@ impl<P: Atomic> MetricVecBuilder for CounterVecBuilder<P> {
type M = GenericCounter<P>;
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)
}
}
@ -323,10 +323,12 @@ impl<P: Atomic> Clone for GenericLocalCounterVec<P> {
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::f64::EPSILON;
use std::f64;
use super::*;
use crate::metrics::{Collector, Opts};
#[cfg(feature = "protobuf")]
use crate::proto_ext::MessageFieldExt;
#[test]
fn test_counter() {
@ -343,7 +345,7 @@ mod tests {
assert_eq!(mfs.len(), 1);
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_counter().get_value() as u64, 43);
@ -363,12 +365,12 @@ mod tests {
assert_eq!(mfs.len(), 1);
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_counter().get_value() as u64, 12);
counter.reset();
assert_eq!(counter.get() as u64, 0);
assert_eq!(counter.get(), 0);
}
#[test]
@ -414,9 +416,9 @@ mod tests {
local_counter.reset();
counter.reset();
assert_eq!(counter.get() as u64, 0);
assert_eq!(counter.get(), 0);
local_counter.flush();
assert_eq!(counter.get() as u64, 0);
assert_eq!(counter.get(), 0);
}
#[test]
@ -450,6 +452,40 @@ mod tests {
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]
fn test_int_counter_vec() {
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());
}
#[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]
fn test_counter_vec_local() {
let vec = CounterVec::new(
@ -502,48 +559,48 @@ mod tests {
assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_err());
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!((vec.with_label_values(&["v1", "v2"]).get() - 0.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) <= f64::EPSILON);
local_vec_1.flush();
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.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) <= f64::EPSILON);
local_vec_1.flush();
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.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) <= f64::EPSILON);
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!((vec.with_label_values(&["v1", "v2"]).get() - 23.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) <= f64::EPSILON);
local_vec_1.flush();
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 34.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) <= f64::EPSILON);
// When calling `remove_label_values`, it is "flushed" immediately.
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!((vec.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) <= f64::EPSILON);
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", "v3"]).is_err());
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!((vec.with_label_values(&["v1", "v2"]).get() - 0.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) <= f64::EPSILON);
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_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_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]

View File

@ -253,8 +253,7 @@ mod tests {
vec![name.into()],
HashMap::new(),
)
.err()
.expect(format!("expected error for {}", name).as_ref());
.expect_err(format!("expected error for {}", name).as_ref());
match res {
Error::Msg(msg) => assert_eq!(msg, format!("'{}' is not a valid label name", name)),
other => panic!("{}", other),
@ -268,8 +267,7 @@ mod tests {
let mut labels = HashMap::new();
labels.insert(name.into(), "value".into());
let res = Desc::new("name".into(), "help".into(), vec![], labels)
.err()
.expect(format!("expected error for {}", name).as_ref());
.expect_err(format!("expected error for {}", name).as_ref());
match res {
Error::Msg(msg) => assert_eq!(msg, format!("'{}' is not a valid label name", name)),
other => panic!("{}", other),
@ -281,8 +279,7 @@ mod tests {
fn test_invalid_metric_name() {
for &name in &["-dash", "9gag", "has space"] {
let res = Desc::new(name.into(), "help".into(), vec![], HashMap::new())
.err()
.expect(format!("expected error for {}", name).as_ref());
.expect_err(format!("expected error for {}", name).as_ref());
match res {
Error::Msg(msg) => {
assert_eq!(msg, format!("'{}' is not a valid metric name", name))

View File

@ -30,7 +30,7 @@ fn check_metric_family(mf: &MetricFamily) -> Result<()> {
if mf.get_metric().is_empty() {
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)));
}
Ok(())

View File

@ -6,6 +6,8 @@ use std::io::{self, Write};
use crate::errors::Result;
use crate::histogram::BUCKET_LABEL;
use crate::proto::{self, MetricFamily, MetricType};
#[cfg(feature = "protobuf")]
use crate::proto_ext::MessageFieldExt;
use super::{check_metric_family, Encoder};
@ -55,8 +57,8 @@ impl TextEncoder {
check_metric_family(mf)?;
// Write `# HELP` header.
let name = mf.get_name();
let help = mf.get_help();
let name = mf.name();
let help = mf.help();
if !help.is_empty() {
writer.write_all("# HELP ")?;
writer.write_all(name)?;
@ -87,14 +89,14 @@ impl TextEncoder {
let mut inf_seen = false;
for b in h.get_bucket() {
let upper_bound = b.get_upper_bound();
let upper_bound = b.upper_bound();
write_sample(
writer,
name,
Some("_bucket"),
m,
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() {
inf_seen = true;
@ -125,18 +127,18 @@ impl TextEncoder {
MetricType::SUMMARY => {
let s = m.get_summary();
for q in s.get_quantile() {
for q in s.get_quantile().iter() {
write_sample(
writer,
name,
None,
m,
Some((QUANTILE, &q.get_quantile().to_string())),
q.get_value(),
Some((QUANTILE, &q.quantile().to_string())),
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(
writer,
@ -144,7 +146,7 @@ impl TextEncoder {
Some("_count"),
m,
None,
s.get_sample_count() as f64,
s.sample_count() as f64,
)?;
}
MetricType::UNTYPED => {
@ -191,7 +193,7 @@ fn write_sample(
writer.write_all(" ")?;
writer.write_all(&value.to_string())?;
let timestamp = mc.get_timestamp_ms();
let timestamp = mc.timestamp_ms();
if timestamp != 0 {
writer.write_all(" ")?;
writer.write_all(&timestamp.to_string())?;
@ -221,9 +223,9 @@ fn label_pairs_to_text(
let mut separator = "{";
for lp in pairs {
writer.write_all(separator)?;
writer.write_all(lp.get_name())?;
writer.write_all(lp.name())?;
writer.write_all("=\"")?;
writer.write_all(&escape_string(lp.get_value(), true))?;
writer.write_all(&escape_string(lp.value(), true))?;
writer.write_all("\"")?;
separator = ",";
@ -423,11 +425,11 @@ test_histogram_count{a="1"} 1
quantile2.set_quantile(100.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();
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 encoder = TextEncoder::new();

View File

@ -22,10 +22,10 @@ pub enum Error {
/// An error containing a [`std::io::Error`].
#[error("Io error: {0}")]
Io(#[from] std::io::Error),
/// An error containing a [`protobuf::error::ProtobufError`].
/// An error containing a [`protobuf::Error`].
#[cfg(feature = "protobuf")]
#[error("Protobuf error: {0}")]
Protobuf(#[from] protobuf::error::ProtobufError),
Protobuf(#[from] protobuf::Error),
}
/// A specialized Result type for prometheus.

View File

@ -43,10 +43,10 @@ impl<P: Atomic> GenericGauge<P> {
/// Create a [`GenericGauge`] with the `opts` options.
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)?;
Ok(Self { v: Arc::new(v) })
}
@ -129,7 +129,7 @@ impl<P: Atomic> MetricVecBuilder for GaugeVecBuilder<P> {
type M = GenericGauge<P>;
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)
}
}
@ -166,6 +166,8 @@ mod tests {
use super::*;
use crate::metrics::{Collector, Opts};
#[cfg(feature = "protobuf")]
use crate::proto_ext::MessageFieldExt;
#[test]
fn test_gauge() {
@ -188,7 +190,7 @@ mod tests {
assert_eq!(mfs.len(), 1);
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_gauge().get_value() as u64, 42);
}
@ -216,6 +218,29 @@ mod tests {
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]
fn test_gauge_vec_with_label_values() {
let vec = GaugeVec::new(
@ -237,4 +262,30 @@ mod tests {
assert!(vec.remove_label_values(&["v1"]).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());
}
}

View File

@ -284,11 +284,11 @@ impl ShardAndCount {
/// A histogram supports two main execution paths:
///
/// 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
/// collect operation) which snapshots the state of the histogram and exposes it
/// as a Protobuf struct.
/// collect operation) which snapshots the state of the histogram and exposes
/// it as a Protobuf struct.
///
/// If an observe and a collect operation interleave, the latter could be
/// exposing a snapshot of the histogram that does not uphold all histogram
@ -327,14 +327,14 @@ pub struct 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()?;
for name in &desc.variable_labels {
check_bucket_label(name)?;
}
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)?;
@ -453,7 +453,7 @@ impl HistogramCore {
b.set_upper_bound(*upper_bound);
buckets.push(b);
}
h.set_bucket(from_vec!(buckets));
h.set_bucket(buckets);
// Update the hot shard.
hot_shard.count.inc_by(overall_count);
@ -542,7 +542,7 @@ impl Instant {
#[inline]
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 {
/// `with_opts` creates a [`Histogram`] with the `opts` options.
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,
label_values: &[&str],
label_values: &[V],
) -> Result<Histogram> {
let core = HistogramCore::new(opts, label_values)?;
@ -750,8 +750,7 @@ impl Histogram {
impl Metric for Histogram {
fn metric(&self) -> proto::Metric {
let mut m = proto::Metric::default();
m.set_label(from_vec!(self.core.label_pairs.clone()));
let mut m = proto::Metric::from_label(self.core.label_pairs.clone());
let h = self.core.proto();
m.set_histogram(h);
@ -770,7 +769,7 @@ impl Collector for Histogram {
m.set_name(self.core.desc.fq_name.clone());
m.set_help(self.core.desc.help.clone());
m.set_field_type(proto::MetricType::HISTOGRAM);
m.set_metric(from_vec!(vec![self.metric()]));
m.set_metric(vec![self.metric()]);
vec![m]
}
@ -783,7 +782,7 @@ impl MetricVecBuilder for HistogramVecBuilder {
type M = Histogram;
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)
}
}
@ -882,13 +881,6 @@ pub fn exponential_buckets(start: f64, factor: f64, count: usize) -> Result<Vec<
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)]
pub struct LocalHistogramCore {
histogram: Histogram,
@ -1207,7 +1199,7 @@ impl Clone for LocalHistogramVec {
#[cfg(test)]
mod tests {
use std::f64::{EPSILON, INFINITY};
use std::f64;
use std::thread;
use std::time::Duration;
@ -1237,7 +1229,7 @@ mod tests {
assert_eq!(mfs.len(), 1);
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);
let proto_histogram = m.get_histogram();
assert_eq!(proto_histogram.get_sample_count(), 3);
@ -1251,11 +1243,11 @@ mod tests {
assert_eq!(mfs.len(), 1);
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);
let proto_histogram = m.get_histogram();
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())
}
@ -1287,7 +1279,7 @@ mod tests {
let m = mf.get_metric().get(0).unwrap();
let proto_histogram = m.get_histogram();
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]
@ -1311,7 +1303,11 @@ mod tests {
(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, 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 {
@ -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]
fn test_histogram_vec_with_label_values() {
let vec = HistogramVec::new(
@ -1386,6 +1372,27 @@ mod tests {
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]
fn test_histogram_vec_with_opts_buckets() {
let labels = ["l1", "l2"];
@ -1405,7 +1412,7 @@ mod tests {
let proto_histogram = m.get_histogram();
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())
}
@ -1421,7 +1428,7 @@ mod tests {
let m = histogram.metric();
let proto_histogram = m.get_histogram();
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);
@ -1456,7 +1463,7 @@ mod tests {
let ms = vec.collect()[0].take_metric();
let proto_histogram = ms[0].get_histogram();
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_sum = proto.get_sample_sum() as u64;
// 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 {
break;

View File

@ -129,23 +129,12 @@ This library supports four features:
#[path = "../proto/proto_model.rs"]
pub mod proto;
#[cfg(feature = "protobuf")]
macro_rules! from_vec {
($e: expr) => {
::protobuf::RepeatedField::from_vec($e)
};
}
#[cfg(not(feature = "protobuf"))]
#[path = "plain_model.rs"]
pub mod proto;
#[cfg(not(feature = "protobuf"))]
macro_rules! from_vec {
($e: expr) => {
$e
};
}
#[cfg(feature = "protobuf")]
mod proto_ext;
#[macro_use]
mod macros;
@ -158,6 +147,8 @@ mod errors;
mod gauge;
mod histogram;
mod metrics;
mod nohash;
mod pulling_gauge;
#[cfg(feature = "push")]
mod push;
mod registry;
@ -219,6 +210,7 @@ pub use self::histogram::DEFAULT_BUCKETS;
pub use self::histogram::{exponential_buckets, linear_buckets};
pub use self::histogram::{Histogram, HistogramOpts, HistogramTimer, HistogramVec};
pub use self::metrics::Opts;
pub use self::pulling_gauge::PullingGauge;
#[cfg(feature = "push")]
pub use self::push::{
hostname_grouping_key, push_add_collector, push_add_metrics, push_collector, push_metrics,

View File

@ -43,7 +43,7 @@ fn test_labels_without_trailing_comma() {
"foo" => "bar"
};
assert_eq!(labels.len(), 2);
assert!(labels.get("test").is_some());
assert!(labels.contains_key("test"));
assert_eq!(*(labels.get("test").unwrap()), "hello");
}
@ -84,6 +84,7 @@ macro_rules! opts {
let opts = $crate::Opts::new($NAME, $HELP);
let lbs = HashMap::<String, String>::new();
$(
#[allow(clippy::redundant_locals)]
let mut lbs = lbs;
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",},);
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");
let opts = opts!(
@ -114,7 +115,7 @@ fn test_opts_trailing_comma() {
labels! {"ans" => "42",},
);
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");
}
@ -191,7 +192,7 @@ fn test_histogram_opts_trailing_comma() {
assert_eq!(opts.common_opts.name, name);
assert_eq!(opts.common_opts.help, help);
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");
}
@ -214,7 +215,7 @@ fn test_histogram_opts_trailing_comma() {
macro_rules! register_counter {
(@of_type $TYPE:ident, $OPTS:expr) => {{
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 $(,)?) => {{
@ -260,7 +261,7 @@ fn test_register_counter_trailing_comma() {
macro_rules! register_counter_with_registry {
(@of_type $TYPE: ident, $OPTS:expr, $REGISTRY:expr) => {{
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 $(,)?) => {{
@ -361,14 +362,14 @@ fn test_register_int_counter() {
macro_rules! __register_counter_vec {
($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr) => {{
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) => {{
let counter_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap();
$REGISTRY
.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 {
($TYPE:ident, $OPTS:expr) => {{
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) => {{
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 {
($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr $(,)?) => {{
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 $(,)?) => {{
let gauge_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap();
$REGISTRY
.register(Box::new(gauge_vec.clone()))
.map(|_| gauge_vec)
.map(|()| gauge_vec)
}};
}
@ -917,7 +918,7 @@ macro_rules! register_histogram {
($HOPTS:expr $(,)?) => {{
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();
$REGISTRY
.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 {
($HOPTS:expr, $LABELS_NAMES:expr $(,)?) => {{
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 $(,)?) => {{
@ -1060,7 +1061,7 @@ fn test_register_histogram_vec_trailing_comma() {
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
///
@ -1094,7 +1095,7 @@ macro_rules! register_histogram_vec_with_registry {
let histogram_vec = $crate::HistogramVec::new($HOPTS, $LABELS_NAMES).unwrap();
$REGISTRY
.register(Box::new(histogram_vec.clone()))
.map(|_| histogram_vec)
.map(|()| histogram_vec)
}};
($NAME:expr, $HELP:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{

View File

@ -177,7 +177,7 @@ impl Describer for Opts {
impl Ord for LabelPair {
fn cmp(&self, other: &LabelPair) -> Ordering {
self.get_name().cmp(other.get_name())
self.name().cmp(other.name())
}
}

23
src/nohash.rs Normal file
View File

@ -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;
}
}

View File

@ -29,7 +29,13 @@ impl LabelPair {
self.name = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.name()` instead")]
pub fn get_name(&self) -> &str {
self.name()
}
/// Returns the name of this label pair.
pub fn name(&self) -> &str {
&self.name
}
@ -37,7 +43,13 @@ impl LabelPair {
self.value = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.value()` instead")]
pub fn get_value(&self) -> &str {
self.value()
}
/// Returns the value of this label pair.
pub fn value(&self) -> &str {
&self.value
}
}
@ -99,7 +111,13 @@ impl Quantile {
self.quantile = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.quantile()` instead")]
pub fn get_quantile(&self) -> f64 {
self.quantile()
}
/// Returns the quantile of this quantile.
pub fn quantile(&self) -> f64 {
self.quantile
}
@ -107,7 +125,13 @@ impl Quantile {
self.value = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.value()` instead")]
pub fn get_value(&self) -> f64 {
self.value()
}
/// Returns the value of this quantile.
pub fn value(&self) -> f64 {
self.value
}
}
@ -129,15 +153,27 @@ impl Summary {
self.sample_count = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.sample_count()` instead")]
pub fn get_sample_count(&self) -> u64 {
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) {
self.sample_sum = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.sample_sum()` instead")]
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
}
@ -232,7 +268,13 @@ impl Bucket {
self.cumulative_count = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.cumulative_count()` instead")]
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
}
@ -240,7 +282,13 @@ impl Bucket {
self.upper_bound = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.upper_bound()` instead")]
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
}
}
@ -263,6 +311,22 @@ impl Metric {
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>) {
self.label = v;
}
@ -331,7 +395,13 @@ impl Metric {
self.timestamp_ms = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.timestamp_ms()` instead")]
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
}
}
@ -373,7 +443,13 @@ impl MetricFamily {
self.name = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.name()` instead")]
pub fn get_name(&self) -> &str {
self.name()
}
/// Returns the name of this metric family.
pub fn name(&self) -> &str {
&self.name
}
@ -381,7 +457,13 @@ impl MetricFamily {
self.help = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.help()` instead")]
pub fn get_help(&self) -> &str {
self.help()
}
/// Returns the help text of this metric family.
pub fn help(&self) -> &str {
&self.help
}

265
src/proto_ext.rs Normal file
View File

@ -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))
}
}

99
src/pulling_gauge.rs Normal file
View File

@ -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());
}
}

View File

@ -277,9 +277,9 @@ mod tests {
let mut l = proto::LabelPair::new();
l.set_name(case.0.to_owned());
let mut m = proto::Metric::new();
m.set_label(from_vec!(vec![l]));
m.set_label(vec![l]);
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);
assert!(format!("{}", res.unwrap_err()).contains(case.1));
}

View File

@ -94,7 +94,7 @@ impl RegistryCore {
let mut id_set = Vec::new();
let mut collector_id: u64 = 0;
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);
collector_id = collector_id.wrapping_add(desc.id);
}
@ -127,7 +127,7 @@ impl RegistryCore {
continue;
}
let name = mf.get_name().to_owned();
let name = mf.name().to_owned();
match mf_by_name.entry(name) {
BEntry::Vacant(entry) => {
entry.insert(mf);
@ -166,8 +166,8 @@ impl RegistryCore {
}
for (lp1, lp2) in lps1.iter().zip(lps2.iter()) {
if lp1.get_value() != lp2.get_value() {
return lp1.get_value().cmp(lp2.get_value());
if lp1.value() != lp2.value() {
return lp1.value().cmp(lp2.value());
}
}
@ -177,17 +177,17 @@ impl RegistryCore {
// here, even for inconsistent metrics. So sort equal metrics
// by their timestamp, with missing timestamps (implying "now")
// 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.
mf_by_name
.into_iter()
.map(|(_, mut m)| {
.into_values()
.map(|mut m| {
// Add registry namespace prefix, if any.
if let Some(ref namespace) = self.prefix {
let prefixed = format!("{}_{}", namespace, m.get_name());
let prefixed = format!("{}_{}", namespace, m.name());
m.set_name(prefixed);
}
@ -204,9 +204,9 @@ impl RegistryCore {
.collect();
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());
metric.set_label(labels.into());
metric.set_label(labels);
}
}
m
@ -341,7 +341,10 @@ mod tests {
use crate::counter::{Counter, CounterVec};
use crate::desc::Desc;
use crate::metrics::{Collector, Opts};
#[cfg(feature = "protobuf")]
use crate::proto;
#[cfg(feature = "protobuf")]
use crate::proto_ext::MessageFieldExt;
#[test]
fn test_registry() {
@ -401,9 +404,9 @@ mod tests {
let mfs = r.gather();
assert_eq!(mfs.len(), 3);
assert_eq!(mfs[0].get_name(), "test_2_counter");
assert_eq!(mfs[1].get_name(), "test_a_counter");
assert_eq!(mfs[2].get_name(), "test_b_counter");
assert_eq!(mfs[0].name(), "test_2_counter");
assert_eq!(mfs[1].name(), "test_a_counter");
assert_eq!(mfs[2].name(), "test_b_counter");
let r = Registry::new();
let opts = Opts::new("test", "test help")
@ -473,7 +476,7 @@ mod tests {
let mfs = r.gather();
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]
@ -493,8 +496,8 @@ mod tests {
let mfs = r.gather();
assert_eq!(mfs.len(), 2);
assert_eq!(mfs[0].get_name(), "test_a_counter");
assert_eq!(mfs[1].get_name(), "test_vec");
assert_eq!(mfs[0].name(), "test_a_counter");
assert_eq!(mfs[1].name(), "test_vec");
let mut needle = proto::LabelPair::default();
needle.set_name("tkey".to_string());

View File

@ -37,11 +37,11 @@ pub struct Value<P: Atomic> {
}
impl<P: Atomic> Value<P> {
pub fn new<D: Describer>(
pub fn new<D: Describer, V: AsRef<str>>(
describer: &D,
val_type: ValueType,
val: P::T,
label_values: &[&str],
label_values: &[V],
) -> Result<Self> {
let desc = describer.describe()?;
let label_pairs = make_label_pairs(&desc, label_values)?;
@ -85,8 +85,7 @@ impl<P: Atomic> Value<P> {
}
pub fn metric(&self) -> Metric {
let mut m = Metric::default();
m.set_label(from_vec!(self.label_pairs.clone()));
let mut m = Metric::from_label(self.label_pairs.clone());
let val = self.get();
match self.val_type {
@ -110,12 +109,12 @@ impl<P: Atomic> Value<P> {
m.set_name(self.desc.fq_name.clone());
m.set_help(self.desc.help.clone());
m.set_field_type(self.val_type.metric_type());
m.set_metric(from_vec!(vec![self.metric()]));
m.set_metric(vec![self.metric()]);
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() {
return Err(Error::InconsistentCardinality {
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() {
let mut label_pair = LabelPair::default();
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);
}

View File

@ -2,7 +2,7 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
use std::collections::HashMap;
use std::hash::Hasher;
use std::hash::{BuildHasher, Hasher};
use std::sync::Arc;
use fnv::FnvHasher;
@ -11,6 +11,7 @@ use parking_lot::RwLock;
use crate::desc::{Desc, Describer};
use crate::errors::{Error, Result};
use crate::metrics::{Collector, Metric};
use crate::nohash::BuildNoHashHasher;
use crate::proto::{MetricFamily, MetricType};
/// An interface for building a metric vector.
@ -21,12 +22,13 @@ pub trait MetricVecBuilder: Send + Sync + Clone {
type P: Describer + Sync + Send + Clone;
/// `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)]
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 metric_type: MetricType,
pub new_metric: T,
@ -45,11 +47,14 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
for child in children.values() {
metrics.push(child.metric());
}
m.set_metric(from_vec!(metrics));
m.set_metric(metrics);
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)?;
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)
}
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)?;
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)
}
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 mut children = self.children.write();
@ -81,7 +92,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
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 mut children = self.children.write();
@ -97,7 +111,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
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() {
return Err(Error::InconsistentCardinality {
expect: self.desc.variable_labels.len(),
@ -107,13 +124,16 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
let mut h = FnvHasher::default();
for val in vals {
h.write(val.as_bytes());
h.write(val.as_ref().as_bytes());
}
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() {
return Err(Error::InconsistentCardinality {
expect: self.desc.variable_labels.len(),
@ -124,7 +144,7 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
let mut h = FnvHasher::default();
for name in &self.desc.variable_labels {
match labels.get(&name.as_ref()) {
Some(val) => h.write(val.as_bytes()),
Some(val) => h.write(val.as_ref().as_bytes()),
None => {
return Err(Error::Msg(format!(
"label name {} missing in label map",
@ -137,11 +157,17 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
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();
for name in &self.desc.variable_labels {
match labels.get(&name.as_ref()) {
Some(val) => values.push(*val),
Some(val) => values.push(val.as_ref()),
None => {
return Err(Error::Msg(format!(
"label name {} missing in label map",
@ -153,7 +179,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
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();
// Check exist first.
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>> {
let desc = opts.describe()?;
let v = MetricVecCore {
children: RwLock::new(HashMap::new()),
children: RwLock::new(HashMap::default()),
desc,
metric_type,
new_metric,
@ -221,7 +250,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
/// 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
/// 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)
}
@ -237,7 +269,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
/// This method is used for the same purpose as
/// `get_metric_with_label_values`. See there for pros and cons of the two
/// 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)
}
@ -254,14 +289,20 @@ impl<T: MetricVecBuilder> MetricVec<T> {
/// ).unwrap();
/// 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()
}
/// `with` works as `get_metric_with`, but panics if an error occurs. The method allows
/// neat syntax like:
/// 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()
}
@ -277,7 +318,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
/// 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
/// 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)
}
@ -289,7 +333,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
///
/// This method is used for the same purpose as `delete_label_values`. See
/// 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)
}
@ -348,6 +395,40 @@ mod tests {
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]
fn test_counter_vec_with_label_values() {
let vec = CounterVec::new(
@ -365,6 +446,27 @@ mod tests {
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]
fn test_gauge_vec_with_labels() {
let vec = GaugeVec::new(
@ -388,6 +490,32 @@ mod tests {
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]
fn test_gauge_vec_with_label_values() {
let vec = GaugeVec::new(
@ -410,6 +538,32 @@ mod tests {
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]
fn test_vec_get_metric_with() {
let vec = CounterVec::new(
@ -428,7 +582,7 @@ mod tests {
let label_pairs = m.get_label();
assert_eq!(label_pairs.len(), labels.len());
for lp in label_pairs.iter() {
assert_eq!(lp.get_value(), labels[lp.get_name()]);
assert_eq!(lp.value(), labels[lp.name()]);
}
}
}

View File

@ -13,13 +13,13 @@ edition = "2018"
proc-macro = true
[dependencies]
syn = { version = "1.0", features = ["full", "extra-traits"] }
syn = { version = "2.0", features = ["full", "extra-traits"] }
proc-macro2 = "1.0"
quote = "1.0"
lazy_static = "1.4"
[dev-dependencies]
criterion = "0.4"
criterion = "0.5"
prometheus = { path = "../" }
[features]

View File

@ -714,7 +714,6 @@ impl<'a> MetricBuilderContext<'a> {
})#local_suffix_call,
}
} else {
let prev_labels_ident = prev_labels_ident;
quote! {
#name: #member_type::from(
#(

View File

@ -300,7 +300,6 @@ impl<'a> MetricBuilderContext<'a> {
})#local_suffix_call,
}
} else {
let prev_labels_ident = prev_labels_ident;
quote! {
#name: #member_type::from(
#(

View File

@ -44,7 +44,7 @@ pub fn make_static_metric(input: TokenStream) -> TokenStream {
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.
#[proc_macro]
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()
}
/// 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]
pub fn auto_flush_from(input: TokenStream) -> TokenStream {
let def: AutoFlushFromDef = syn::parse(input).unwrap();