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: 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.63.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 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)

View File

@ -1,5 +1,43 @@
# 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 ## 0.13.3
- Bug fix: Prevent ProcessCollector underflow with CPU time counter (#465) - Bug fix: Prevent ProcessCollector underflow with CPU time counter (#465)

View File

@ -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.3" 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.14", optional = true, default-features = false } procfs = { version = "^0.17", optional = true, default-features = false }
[dev-dependencies] [dev-dependencies]
criterion = "0.4" 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"]

View File

@ -1,6 +1,6 @@
# Prometheus Rust client library # 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) [![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) [![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): 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.

View File

@ -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,

View File

@ -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"))]

View File

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

View File

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

View File

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

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)] #[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);
} }
} }

View File

@ -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]

View File

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

View File

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

View File

@ -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(&timestamp.to_string())?; writer.write_all(&timestamp.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();

View File

@ -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.

View File

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

View File

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

View File

@ -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,

View File

@ -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 $(,)?) => {{

View File

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

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

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

View File

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

View File

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

View File

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

View File

@ -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.4" criterion = "0.5"
prometheus = { path = "../" } prometheus = { path = "../" }
[features] [features]

View File

@ -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(
#( #(

View File

@ -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(
#( #(

View File

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