Compare commits

...

54 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
Luca Bruno c092b95879
prometheus: release 0.13.3 (#466)
Signed-off-by: Luca BRUNO <luca.bruno@coreos.com>

Signed-off-by: Luca BRUNO <luca.bruno@coreos.com>
2022-10-21 07:02:08 +00:00
Anthony Ramine a11df02f27
Prevent underflow with CPU time counter (#465)
Signed-off-by: Anthony Ramine <nox@nox.paris>

Signed-off-by: Anthony Ramine <nox@nox.paris>
2022-10-20 10:30:32 +00:00
dependabot[bot] fc591fdee0
build(deps): update criterion requirement from 0.3 to 0.4 (#459)
Updates the requirements on [criterion](https://github.com/bheisler/criterion.rs) to permit the latest version.
- [Release notes](https://github.com/bheisler/criterion.rs/releases)
- [Changelog](https://github.com/bheisler/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bheisler/criterion.rs/compare/0.3.0...0.4.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-12 08:33:02 +00:00
dependabot[bot] e38ac6b4f2
build(deps): update procfs requirement from ^0.12 to ^0.14 (#452)
* build(deps): update procfs requirement from ^0.12 to ^0.14

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.12.0...v0.14.0)

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

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

* process: update to new procfs-0.14 API

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

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Luca BRUNO <luca.bruno@coreos.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Luca BRUNO <luca.bruno@coreos.com>
2022-09-09 15:22:53 +00:00
Luca Bruno 4e552eede7
prometheus: release 0.13.2 (#457)
* ci: update linting toolchain, fix new warnings

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

* prometheus: release 0.13.2

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

Signed-off-by: Luca BRUNO <luca.bruno@coreos.com>
2022-09-08 07:35:29 +00:00
Frank Denis 5ca37fe4fe
process_collector: fix compilation on 32-bit targets (#446)
The crate didn't compile on 32-bit targets any more due to
libc::sysconf() returning a 32-bit value on those targets.

Signed-off-by: Frank Denis <github@pureftpd.org>
2022-06-27 13:12:15 +00:00
Luca Bruno 935d968333
prometheus: release 0.13.1 (#440)
Signed-off-by: Luca BRUNO <luca.bruno@coreos.com>
2022-05-19 09:09:19 +00:00
keepsimple1 f44caa5f04
fix doc for encode (#433)
Signed-off-by: Han Xu <keepsimple@gmail.com>
2022-04-08 07:11:24 +00:00
Jaseem Abid 6cfacdd47e
Derive default instead of obvious manual impl (#437)
Signed-off-by: Jaseem Abid <me@jabid.in>
2022-03-10 08:27:30 +00:00
Jaseem Abid 117b0df83e
Fix crates.io badge (#436)
Not sure what happened to http://meritbadge.herokuapp.com, but it's not running anymore and points to default heroku error page.

Sheilds.io has a crates.io integration, so this should just work out of the box.

```
![crates.io](https://img.shields.io/crates/v/$CRATE.svg)
```

Example [![crates.io](https://img.shields.io/crates/v/prometheus.svg)](https://crates.io/crates/prometheus)

Signed-off-by: Jaseem Abid <me@jabid.in>
2022-03-02 16:57:15 +00:00
dependabot[bot] ea68788420
build(deps): update parking_lot requirement from ^0.11 to ^0.12 (#434) 2022-02-05 23:23:22 +08:00
Wu Aoxiang ac86a26422
feat(process): ProcessCollector use IntGauge to provide better performance, close #429 (#430)
Signed-off-by: wuaoxiang <wuaoxiang@stargraph.cn>

Co-authored-by: Luca Bruno <luca.bruno@coreos.com>
2022-01-03 10:30:50 +00:00
Bruce Mitchener 8038e9e7bf
Fix broken doc links. (#426)
Signed-off-by: Bruce Mitchener <bruce.mitchener@gmail.com>

Co-authored-by: Luca Bruno <luca.bruno@coreos.com>
2022-01-03 10:00:27 +00:00
Bruce Mitchener 0a63d51460
clippy: Remove needless borrow. (#427)
Signed-off-by: Bruce Mitchener <bruce.mitchener@gmail.com>
2022-01-03 09:45:45 +00:00
dependabot[bot] e3e2ffdd56
build(deps): update procfs requirement from ^0.11 to ^0.12 (#428)
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.11.0...v0.12.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>
2021-12-13 08:51:04 +00:00
dependabot[bot] d1528e11ab
build(deps): update procfs requirement from ^0.10 to ^0.11 (#418)
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.10.0...v0.11.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>
2021-10-15 07:54:57 +00:00
Jeremy Rodi 99b606469f
Fix re-export of TEXT_FORMAT to not require protobuf (#416)
A minor change, but previously the root-level re-export of
TEXT_FORMAT would require cfg(feature = "protobuf"), despite
the text export format not requiring protobufs.  Since this is
the only location where TEXT_FORMAT is made public, it should
be exported regardless of protobuf feature availability.

Signed-off-by: Jeremy Rodi <me@telios.dev>
2021-10-11 07:42:14 +00:00
Luca Bruno fe0a284cbf
prometheus: release 0.13.0 (#415)
Signed-off-by: Luca BRUNO <luca.bruno@coreos.com>
2021-09-28 06:56:29 +00:00
Hyeonu Park 3451d052a4
Allow trailing comma on macros (#390)
Signed-off-by: Hyeonu Park <nemo1275@gmail.com>
2021-09-13 08:45:39 +00:00
dependabot[bot] 1e92ec7df2
build(deps): update procfs requirement from ^0.9 to ^0.10 (#408)
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.9.0...v0.10.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>
2021-08-23 07:44:07 +00:00
Moustafa Baiou 1533b1a3f3
feat(macros): add macros for custom registry (#396)
Signed-off-by: Moustafa Baiou <moustafa@ditto.live>
2021-08-11 07:46:25 +00:00
Alin Sinpalean fab7e7640b
Export thread count from `process_collector` (#401)
This simply retrieves and exports the value of the  field from the
already populated  that CPU, memory and start time are
sourced.

Signed-off-by: Alin Sinpalean <alin.sinpalean@gmail.com>
2021-08-09 07:27:50 +00:00
Michael Sproul a93ed8cd6e
Avoid panics from `Instant::elapsed` (#406)
Signed-off-by: Michael Sproul <michael@sigmaprime.io>
2021-07-31 14:45:43 +08:00
Aleksey Kladov 9bbd908db0
Add convenience TextEncoder functions to encode directly to string (#402)
* Convenience TextEncoder functions for working with strings

Signed-off-by: Aleksey Kladov <aleksey.kladov@gmail.com>

* Avoid accidentally quadratic issue in TextEncoder::encode_utf8

Signed-off-by: Aleksey Kladov <aleksey.kladov@gmail.com>
2021-07-27 11:04:26 +00:00
Luca Bruno bb90a19e6d
ci: update linting toolchain to 1.53 (#403)
* ci: update linting toolchain to 1.53

This updates the linting toolchain to latest stable version and
fixes a couple of new warnings.

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

* ci: move remaining Travis jobs to GitHub workflows

This removes the previous Travis configuration, moving all
remaining jobs into the existing GitHub workflow.

Signed-off-by: Luca BRUNO <luca.bruno@coreos.com>
2021-07-19 07:22:29 +00:00
JmPotato 1e7736f8bc
Clean up the use of macro_use and extern crate (#398)
* Clean up the use of macro_use and extern crate

Signed-off-by: JmPotato <ghzpotato@gmail.com>

* Fix the build

Signed-off-by: JmPotato <ghzpotato@gmail.com>
2021-03-23 10:53:57 +00:00
57 changed files with 3210 additions and 2026 deletions

View File

@ -9,38 +9,99 @@ on:
env:
CARGO_TERM_COLOR: always
# Pinned toolchain for linting and benchmarks
ACTIONS_LINTS_TOOLCHAIN: 1.47.0
ACTIONS_LINTS_TOOLCHAIN: 1.81.0
# Minimum supported Rust version (MSRV)
ACTION_MSRV_TOOLCHAIN: 1.81.0
EXTRA_FEATURES: "protobuf push process"
jobs:
tests-stable:
name: "Tests, stable toolchain"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install toolchain
uses: dtolnay/rust-toolchain@stable
- name: cargo build
run: cargo build
- name: cargo test
run: cargo test
- name: cargo test (no default features)
run: cargo test --no-default-features
- name: cargo test (extra features)
run: cargo test --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
- name: cargo package
run : cargo package && cargo package --manifest-path static-metric/Cargo.toml
tests-other-channels:
name: "Tests, unstable toolchain"
runs-on: ubuntu-latest
continue-on-error: true
strategy:
matrix:
channel:
- "beta"
- "nightly"
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.channel }}
- name: cargo build
run: cargo build
- name: cargo test
run: cargo test
- name: cargo build (static-metric)
run: cargo build -p prometheus-static-metric --examples --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
- name: cargo test (static-metric)
run: cargo test -p prometheus-static-metric --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
build-msrv:
name: "Build, minimum toolchain"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env['ACTION_MSRV_TOOLCHAIN'] }}
- run: cargo build
- run: cargo test --no-run
- run: cargo build --no-default-features
- run: cargo test --no-default-features --no-run
- run: cargo build --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
- run: cargo test --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
linting:
name: "Lints, pinned toolchain"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env['ACTIONS_LINTS_TOOLCHAIN'] }}
default: true
toolchain: ${{ env['ACTIONS_LINTS_TOOLCHAIN'] }}
components: rustfmt, clippy
- name: cargo fmt (check)
run: cargo fmt --all -- --check -l
- name: cargo clippy
run: cargo clippy --all -- -D clippy
- name: cargo package
run : cargo package && cargo package --manifest-path static-metric/Cargo.toml
run: cargo clippy --all
- name: cargo clippy (no default features)
run: cargo clippy --all --no-default-features
- name: cargo clippy (extra features)
run: cargo clippy --all --no-default-features --features="${{ env['EXTRA_FEATURES'] }}"
criterion:
name: "Benchmarks (criterion)"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env['ACTIONS_LINTS_TOOLCHAIN'] }}
default: true
- name: cargo bench (prometheus)
run: cargo bench -p prometheus
- name: cargo bench (prometheus-static-metric)

View File

@ -1,41 +0,0 @@
language: rust
sudo: false
os:
- linux
- osx
addons:
apt:
packages:
- gcc-multilib
env:
matrix:
- FEATURES="protobuf push process"
- FEATURES=""
global:
- RUSTFLAGS=--deny=warnings
- ARCH=i686
# Must be present for matrix.
rust:
matrix:
fast_finish: true
include:
- rust: nightly
- rust: nightly
env: FEATURES="nightly protobuf push process"
- rust: beta
- rust: stable
allow_failures:
- rust: nightly
script:
- cargo test --no-default-features --features="$FEATURES"
- |
if [ $TRAVIS_RUST_VERSION = 'nightly' ]; then
cargo build -p prometheus-static-metric --examples --no-default-features --features="$FEATURES"
cargo test -p prometheus-static-metric --no-default-features --features="$FEATURES"
fi

View File

@ -1,5 +1,87 @@
# Changelog
## 0.14.0
- API change: Use `AsRef<str>` for owned label values (#537)
- Improvement: Hashing improvements (#532)
- Dependency upgrade: Update `hyper` to 1.6 (#524)
- Dependency upgrade: Update `procfs` to 0.17 (#543)
- Dependency upgrade: Update `protobuf` to 3.7.2 for RUSTSEC-2024-0437 (#541)
- Dependency upgrade: Update `thiserror` to 2.0 (#534)
- Internal change: Fix LSP and Clippy warnings (#540)
- Internal change: Bump MSRV to 1.81 (#539)
- Documentation: Fix `register_histogram_vec_with_registry` docstring (#528)
- Documentation: Fix typos in static-metric docstrings (#479)
- Documentation: Add missing `protobuf` feature to README list (#531)
## 0.13.4
- Improvement: Add PullingGauge (#405)
- Improvement: Let cargo know which example requires which features (#511)
- Bug fix: Prevent `clippy::ignored_unit_patterns` in macro expansions (#497)
- Internal change: Add CI job for minimum toolchain (MSRV) (#467)
- Internal change: Update CI to `actions/checkout@v4` (#499)
- Internal change: Update dependencies
## 0.13.3
- Bug fix: Prevent ProcessCollector underflow with CPU time counter (#465)
- Internal change: Update dependencies
## 0.13.2
- Bug fix: Fix compilation on 32-bit targets (#446)
## 0.13.1
- Improvement: ProcessCollector use IntGauge to provide better performance (#430)
- Bug fix: Fix re-export of TEXT_FORMAT to not require protobuf (#416)
- Bug fix: Fix doc for encode (#433)
- Bug fix: Fix broken doc links (#426)
- Bug fix: Fix crates.io badge (#436)
- Internal change: Derive default instead of obvious manual impl (#437)
- Internal change: Remove needless borrow (#427)
- Internal change: Update dependencies
## 0.13.0
- Bug fix: Avoid panics from `Instant::elapsed` (#406)
- Improvement: Allow trailing comma on macros (#390)
- Improvement: Add macros for custom registry (#396)
- Improvement: Export thread count from `process_collector` (#401)
- Improvement: Add convenience TextEncoder functions to encode directly to string (#402)
- Internal change: Clean up the use of macro_use and extern crate (#398)
- Internal change: Update dependencies
## 0.12.0
- Improvement: Fix format string in panic!() calls (#391)

View File

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

View File

@ -1,8 +1,8 @@
# Prometheus Rust client library
[![Build Status](https://travis-ci.org/tikv/rust-prometheus.svg?branch=master)](https://travis-ci.org/pingcap/rust-prometheus)
[![Build Status](https://github.com/tikv/rust-prometheus/actions/workflows/rust.yml/badge.svg)](https://github.com/tikv/rust-prometheus/actions/workflows/rust.yml)
[![docs.rs](https://docs.rs/prometheus/badge.svg)](https://docs.rs/prometheus)
[![crates.io](http://meritbadge.herokuapp.com/prometheus)](https://crates.io/crates/prometheus)
[![crates.io](https://img.shields.io/crates/v/prometheus.svg)](https://crates.io/crates/prometheus)
This is the [Rust](https://www.rust-lang.org) client library for
[Prometheus](http://prometheus.io). The main data structures and APIs are ported
@ -18,6 +18,8 @@ Find the latest documentation at <https://docs.rs/prometheus>.
This crate provides several optional components which can be enabled via [Cargo `[features]`](https://doc.rust-lang.org/cargo/reference/features.html):
- `protobuf`: Protobuf support, enabled by default.
- `gen`: To generate protobuf client with the latest protobuf version instead of
using the pre-generated client.

View File

@ -11,7 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use criterion::{criterion_group, criterion_main, Criterion};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use fnv::FnvBuildHasher;
use prometheus::{Counter, CounterVec, IntCounter, Opts};
use std::collections::HashMap;
use std::sync::{atomic, Arc};
@ -24,7 +25,11 @@ fn bench_counter_with_label_values(c: &mut Criterion) {
)
.unwrap();
c.bench_function("counter_with_label_values", |b| {
b.iter(|| counter.with_label_values(&["eins", "zwei", "drei"]).inc())
b.iter(|| {
counter
.with_label_values(&black_box(["eins", "zwei", "drei"]))
.inc()
})
});
}
@ -41,7 +46,25 @@ fn bench_counter_with_mapped_labels(c: &mut Criterion) {
labels.insert("two", "zwei");
labels.insert("one", "eins");
labels.insert("three", "drei");
counter.with(&labels).inc();
counter.with(&black_box(labels)).inc();
})
});
}
fn bench_counter_with_mapped_labels_fnv(c: &mut Criterion) {
let counter = CounterVec::new(
Opts::new("benchmark_counter", "A counter to benchmark it."),
&["one", "two", "three"],
)
.unwrap();
c.bench_function("counter_with_mapped_labels_fnv", |b| {
b.iter(|| {
let mut labels = HashMap::with_capacity_and_hasher(3, FnvBuildHasher::default());
labels.insert("two", "zwei");
labels.insert("one", "eins");
labels.insert("three", "drei");
counter.with(&black_box(labels)).inc();
})
});
}
@ -192,6 +215,7 @@ criterion_group!(
bench_counter_with_label_values,
bench_counter_with_label_values_concurrent_write,
bench_counter_with_mapped_labels,
bench_counter_with_mapped_labels_fnv,
bench_counter_with_prepared_mapped_labels,
bench_int_counter_no_labels,
bench_int_counter_no_labels_concurrent_write,

View File

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

View File

@ -3,14 +3,12 @@
//! This examples shows how to use multiple and custom registries,
//! and how to perform registration across function boundaries.
#[macro_use]
extern crate lazy_static;
extern crate prometheus;
use std::collections::HashMap;
use prometheus::{Encoder, IntCounter, Registry};
use lazy_static::lazy_static;
lazy_static! {
static ref DEFAULT_COUNTER: IntCounter = IntCounter::new("default", "generic counter").unwrap();
static ref CUSTOM_COUNTER: IntCounter = IntCounter::new("custom", "dedicated counter").unwrap();

View File

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

View File

@ -1,12 +1,12 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate prometheus;
use prometheus::{IntCounter, IntCounterVec, IntGauge, IntGaugeVec};
use lazy_static::lazy_static;
use prometheus::{
register_int_counter, register_int_counter_vec, register_int_gauge, register_int_gauge_vec,
};
lazy_static! {
static ref A_INT_COUNTER: IntCounter =
register_int_counter!("A_int_counter", "foobar").unwrap();

View File

@ -1,6 +1,5 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
#[cfg(all(feature = "process", target_os = "linux"))]
fn main() {
use std::thread;
use std::time::Duration;
@ -21,11 +20,3 @@ fn main() {
thread::sleep(Duration::from_secs(1));
}
}
#[cfg(any(not(feature = "process"), not(target_os = "linux")))]
fn main() {
println!(
r#"Please enable feature "process", try:
cargo run --features="process" --example example_process_collector"#
);
}

View File

@ -1,12 +1,5 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
#![cfg_attr(not(feature = "push"), allow(unused_imports, dead_code))]
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate prometheus;
use std::env;
use std::thread;
use std::time;
@ -14,6 +7,9 @@ use std::time;
use getopts::Options;
use prometheus::{Counter, Histogram};
use lazy_static::lazy_static;
use prometheus::{labels, register_counter, register_histogram};
lazy_static! {
static ref PUSH_COUNTER: Counter = register_counter!(
"example_push_total",
@ -27,7 +23,6 @@ lazy_static! {
.unwrap();
}
#[cfg(feature = "push")]
fn main() {
let args: Vec<String> = env::args().collect();
let program = args[0].clone();
@ -70,11 +65,3 @@ fn main() {
println!("Okay, please check the Pushgateway.");
}
#[cfg(not(feature = "push"))]
fn main() {
println!(
r#"Please enable feature "push", try:
cargo run --features="push" --example example_push"#
);
}

3
proto/mod.rs Normal file
View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -134,7 +134,7 @@ impl<T: 'static + MayFlush, D: HistogramDelegator<T>> AFLocalHistogram<T, D> {
}
impl<M: 'static + MayFlush, D: HistogramDelegator<M>> AFLocalHistogram<M, D> {
/// Add a single observation to the [`Histogram`](::Histogram).
/// Add a single observation to the [`Histogram`](crate::Histogram).
pub fn observe(&self, v: f64) {
self.delegator.get_root_metric().with(|m| {
let local = self.delegator.get_local(m);
@ -175,7 +175,7 @@ impl<M: 'static + MayFlush, D: HistogramDelegator<M>> AFLocalHistogram<M, D> {
.with(|m| self.delegator.get_local(m).clear())
}
/// Flush the local metrics to the [`Histogram`](::Histogram) metric.
/// Flush the local metrics to the [`Histogram`](crate::Histogram) metric.
pub fn flush(&self) {
self.delegator
.get_root_metric()

View File

@ -44,10 +44,10 @@ impl<P: Atomic> GenericCounter<P> {
/// Create a [`GenericCounter`] with the `opts` options.
pub fn with_opts(opts: Opts) -> Result<Self> {
Self::with_opts_and_label_values(&opts, &[])
Self::with_opts_and_label_values::<&str>(&opts, &[])
}
fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result<Self> {
fn with_opts_and_label_values<V: AsRef<str>>(opts: &Opts, label_values: &[V]) -> Result<Self> {
let v = Value::new(opts, ValueType::Counter, P::T::from_i64(0), label_values)?;
Ok(Self { v: Arc::new(v) })
}
@ -126,7 +126,7 @@ impl<P: Atomic> MetricVecBuilder for CounterVecBuilder<P> {
type M = GenericCounter<P>;
type P = Opts;
fn build(&self, opts: &Opts, vals: &[&str]) -> Result<Self::M> {
fn build<V: AsRef<str>>(&self, opts: &Opts, vals: &[V]) -> Result<Self::M> {
Self::M::with_opts_and_label_values(opts, vals)
}
}
@ -323,10 +323,12 @@ impl<P: Atomic> Clone for GenericLocalCounterVec<P> {
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::f64::EPSILON;
use std::f64;
use super::*;
use crate::metrics::{Collector, Opts};
#[cfg(feature = "protobuf")]
use crate::proto_ext::MessageFieldExt;
#[test]
fn test_counter() {
@ -343,7 +345,7 @@ mod tests {
assert_eq!(mfs.len(), 1);
let mf = mfs.pop().unwrap();
let m = mf.get_metric().get(0).unwrap();
let m = mf.get_metric().first().unwrap();
assert_eq!(m.get_label().len(), 2);
assert_eq!(m.get_counter().get_value() as u64, 43);
@ -363,12 +365,12 @@ mod tests {
assert_eq!(mfs.len(), 1);
let mf = mfs.pop().unwrap();
let m = mf.get_metric().get(0).unwrap();
let m = mf.get_metric().first().unwrap();
assert_eq!(m.get_label().len(), 0);
assert_eq!(m.get_counter().get_value() as u64, 12);
counter.reset();
assert_eq!(counter.get() as u64, 0);
assert_eq!(counter.get(), 0);
}
#[test]
@ -414,9 +416,9 @@ mod tests {
local_counter.reset();
counter.reset();
assert_eq!(counter.get() as u64, 0);
assert_eq!(counter.get(), 0);
local_counter.flush();
assert_eq!(counter.get() as u64, 0);
assert_eq!(counter.get(), 0);
}
#[test]
@ -450,6 +452,40 @@ mod tests {
assert!(vec.remove(&labels3).is_err());
}
#[test]
fn test_counter_vec_with_owned_labels() {
let vec = CounterVec::new(
Opts::new("test_couter_vec", "test counter vec help"),
&["l1", "l2"],
)
.unwrap();
let v1 = "v1".to_string();
let v2 = "v2".to_string();
let mut labels = HashMap::new();
labels.insert("l1", v1.clone());
labels.insert("l2", v2.clone());
assert!(vec.remove(&labels).is_err());
vec.with(&labels).inc();
assert!(vec.remove(&labels).is_ok());
assert!(vec.remove(&labels).is_err());
let mut labels2 = HashMap::new();
labels2.insert("l1", v2.clone());
labels2.insert("l2", v1.clone());
vec.with(&labels).inc();
assert!(vec.remove(&labels2).is_err());
vec.with(&labels).inc();
let mut labels3 = HashMap::new();
labels3.insert("l1", v1.clone());
assert!(vec.remove(&labels3).is_err());
}
#[test]
fn test_int_counter_vec() {
let vec = IntCounterVec::new(Opts::new("foo", "bar"), &["l1", "l2"]).unwrap();
@ -489,6 +525,27 @@ mod tests {
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
}
#[test]
fn test_counter_vec_with_owned_label_values() {
let vec = CounterVec::new(
Opts::new("test_vec", "test counter vec help"),
&["l1", "l2"],
)
.unwrap();
let v1 = "v1".to_string();
let v2 = "v2".to_string();
let v3 = "v3".to_string();
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
assert!(vec.remove_label_values(&[v1.clone()]).is_err());
assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
}
#[test]
fn test_counter_vec_local() {
let vec = CounterVec::new(
@ -502,48 +559,48 @@ mod tests {
assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_err());
local_vec_1.with_label_values(&["v1", "v2"]).inc_by(23.0);
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
local_vec_1.flush();
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
local_vec_1.flush();
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
local_vec_1.with_label_values(&["v1", "v2"]).inc_by(11.0);
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 11.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 11.0) <= f64::EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
local_vec_1.flush();
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 34.0) <= EPSILON);
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 34.0) <= f64::EPSILON);
// When calling `remove_label_values`, it is "flushed" immediately.
assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_ok());
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
local_vec_1.with_label_values(&["v1", "v2"]).inc();
assert!(local_vec_1.remove_label_values(&["v1"]).is_err());
assert!(local_vec_1.remove_label_values(&["v1", "v3"]).is_err());
local_vec_1.with_label_values(&["v1", "v2"]).inc_by(13.0);
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 14.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 14.0) <= f64::EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
local_vec_2.with_label_values(&["v1", "v2"]).inc_by(7.0);
assert!((local_vec_2.with_label_values(&["v1", "v2"]).get() - 7.0) <= EPSILON);
assert!((local_vec_2.with_label_values(&["v1", "v2"]).get() - 7.0) <= f64::EPSILON);
local_vec_1.flush();
local_vec_2.flush();
assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= f64::EPSILON);
local_vec_1.flush();
local_vec_2.flush();
assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= EPSILON);
assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= f64::EPSILON);
}
#[test]

View File

@ -27,7 +27,7 @@ fn is_valid_ident<F: FnMut(char) -> bool>(input: &str, mut charset_validator: F)
zeroth
.and_then(|zeroth| {
if charset_validator(zeroth) {
Some(chars.all(|c| charset_validator(c) || c.is_digit(10)))
Some(chars.all(|c| charset_validator(c) || c.is_ascii_digit()))
} else {
None
}
@ -253,8 +253,7 @@ mod tests {
vec![name.into()],
HashMap::new(),
)
.err()
.expect(format!("expected error for {}", name).as_ref());
.expect_err(format!("expected error for {}", name).as_ref());
match res {
Error::Msg(msg) => assert_eq!(msg, format!("'{}' is not a valid label name", name)),
other => panic!("{}", other),
@ -268,8 +267,7 @@ mod tests {
let mut labels = HashMap::new();
labels.insert(name.into(), "value".into());
let res = Desc::new("name".into(), "help".into(), vec![], labels)
.err()
.expect(format!("expected error for {}", name).as_ref());
.expect_err(format!("expected error for {}", name).as_ref());
match res {
Error::Msg(msg) => assert_eq!(msg, format!("'{}' is not a valid label name", name)),
other => panic!("{}", other),
@ -281,8 +279,7 @@ mod tests {
fn test_invalid_metric_name() {
for &name in &["-dash", "9gag", "has space"] {
let res = Desc::new(name.into(), "help".into(), vec![], HashMap::new())
.err()
.expect(format!("expected error for {}", name).as_ref());
.expect_err(format!("expected error for {}", name).as_ref());
match res {
Error::Msg(msg) => {
assert_eq!(msg, format!("'{}' is not a valid metric name", name))

View File

@ -16,12 +16,11 @@ use crate::proto::MetricFamily;
/// An interface for encoding metric families into an underlying wire protocol.
pub trait Encoder {
/// `encode` converts a slice of MetricFamily proto messages into target
/// format and writes the resulting lines to `writer`. It returns the number
/// of bytes written and any error encountered. This function does not
/// perform checks on the content of the metric and label names,
/// i.e. invalid metric or label names will result in invalid text format
/// format and writes the resulting lines to `writer`. This function does not
/// perform checks on the content of the metrics and label names,
/// i.e. invalid metrics or label names will result in invalid text format
/// output.
fn encode<W: Write>(&self, _: &[MetricFamily], _: &mut W) -> Result<()>;
fn encode<W: Write>(&self, mfs: &[MetricFamily], writer: &mut W) -> Result<()>;
/// `format_type` returns target format.
fn format_type(&self) -> &str;
@ -31,7 +30,7 @@ fn check_metric_family(mf: &MetricFamily) -> Result<()> {
if mf.get_metric().is_empty() {
return Err(Error::Msg(format!("MetricFamily has no metrics: {:?}", mf)));
}
if mf.get_name().is_empty() {
if mf.name().is_empty() {
return Err(Error::Msg(format!("MetricFamily has no name: {:?}", mf)));
}
Ok(())

View File

@ -1,11 +1,13 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
use std::borrow::Cow;
use std::io::Write;
use std::io::{self, Write};
use crate::errors::Result;
use crate::histogram::BUCKET_LABEL;
use crate::proto::{self, MetricFamily, MetricType};
#[cfg(feature = "protobuf")]
use crate::proto_ext::MessageFieldExt;
use super::{check_metric_family, Encoder};
@ -25,33 +27,54 @@ impl TextEncoder {
pub fn new() -> TextEncoder {
TextEncoder
}
}
/// Appends metrics to a given `String` buffer.
///
/// This is a convenience wrapper around `<TextEncoder as Encoder>::encode`.
pub fn encode_utf8(&self, metric_families: &[MetricFamily], buf: &mut String) -> Result<()> {
// Note: it's important to *not* re-validate UTF8-validity for the
// entirety of `buf`. Otherwise, repeatedly appending metrics to the
// same `buf` will lead to quadratic behavior. That's why we use
// `WriteUtf8` abstraction to skip the validation.
self.encode_impl(metric_families, &mut StringBuf(buf))?;
Ok(())
}
/// Converts metrics to `String`.
///
/// This is a convenience wrapper around `<TextEncoder as Encoder>::encode`.
pub fn encode_to_string(&self, metric_families: &[MetricFamily]) -> Result<String> {
let mut buf = String::new();
self.encode_utf8(metric_families, &mut buf)?;
Ok(buf)
}
impl Encoder for TextEncoder {
fn encode<W: Write>(&self, metric_families: &[MetricFamily], writer: &mut W) -> Result<()> {
fn encode_impl(
&self,
metric_families: &[MetricFamily],
writer: &mut dyn WriteUtf8,
) -> Result<()> {
for mf in metric_families {
// Fail-fast checks.
check_metric_family(mf)?;
// Write `# HELP` header.
let name = mf.get_name();
let help = mf.get_help();
let name = mf.name();
let help = mf.help();
if !help.is_empty() {
writer.write_all(b"# HELP ")?;
writer.write_all(name.as_bytes())?;
writer.write_all(b" ")?;
writer.write_all(escape_string(help, false).as_bytes())?;
writer.write_all(b"\n")?;
writer.write_all("# HELP ")?;
writer.write_all(name)?;
writer.write_all(" ")?;
writer.write_all(&escape_string(help, false))?;
writer.write_all("\n")?;
}
// Write `# TYPE` header.
let metric_type = mf.get_field_type();
let lowercase_type = format!("{:?}", metric_type).to_lowercase();
writer.write_all(b"# TYPE ")?;
writer.write_all(name.as_bytes())?;
writer.write_all(b" ")?;
writer.write_all(lowercase_type.as_bytes())?;
writer.write_all(b"\n")?;
writer.write_all("# TYPE ")?;
writer.write_all(name)?;
writer.write_all(" ")?;
writer.write_all(&lowercase_type)?;
writer.write_all("\n")?;
for m in mf.get_metric() {
match metric_type {
@ -66,14 +89,14 @@ impl Encoder for TextEncoder {
let mut inf_seen = false;
for b in h.get_bucket() {
let upper_bound = b.get_upper_bound();
let upper_bound = b.upper_bound();
write_sample(
writer,
name,
Some("_bucket"),
m,
Some((BUCKET_LABEL, &upper_bound.to_string())),
b.get_cumulative_count() as f64,
b.cumulative_count() as f64,
)?;
if upper_bound.is_sign_positive() && upper_bound.is_infinite() {
inf_seen = true;
@ -104,18 +127,18 @@ impl Encoder for TextEncoder {
MetricType::SUMMARY => {
let s = m.get_summary();
for q in s.get_quantile() {
for q in s.get_quantile().iter() {
write_sample(
writer,
name,
None,
m,
Some((QUANTILE, &q.get_quantile().to_string())),
q.get_value(),
Some((QUANTILE, &q.quantile().to_string())),
q.value(),
)?;
}
write_sample(writer, name, Some("_sum"), m, None, s.get_sample_sum())?;
write_sample(writer, name, Some("_sum"), m, None, s.sample_sum())?;
write_sample(
writer,
@ -123,7 +146,7 @@ impl Encoder for TextEncoder {
Some("_count"),
m,
None,
s.get_sample_count() as f64,
s.sample_count() as f64,
)?;
}
MetricType::UNTYPED => {
@ -135,6 +158,12 @@ impl Encoder for TextEncoder {
Ok(())
}
}
impl Encoder for TextEncoder {
fn encode<W: Write>(&self, metric_families: &[MetricFamily], writer: &mut W) -> Result<()> {
self.encode_impl(metric_families, &mut *writer)
}
fn format_type(&self) -> &str {
TEXT_FORMAT
@ -147,30 +176,30 @@ impl Encoder for TextEncoder {
/// not required), and the value. The function returns the number of bytes
/// written and any error encountered.
fn write_sample(
writer: &mut dyn Write,
writer: &mut dyn WriteUtf8,
name: &str,
name_postfix: Option<&str>,
mc: &proto::Metric,
additional_label: Option<(&str, &str)>,
value: f64,
) -> Result<()> {
writer.write_all(name.as_bytes())?;
writer.write_all(name)?;
if let Some(postfix) = name_postfix {
writer.write_all(postfix.as_bytes())?;
writer.write_all(postfix)?;
}
label_pairs_to_text(mc.get_label(), additional_label, writer)?;
writer.write_all(b" ")?;
writer.write_all(value.to_string().as_bytes())?;
writer.write_all(" ")?;
writer.write_all(&value.to_string())?;
let timestamp = mc.get_timestamp_ms();
let timestamp = mc.timestamp_ms();
if timestamp != 0 {
writer.write_all(b" ")?;
writer.write_all(timestamp.to_string().as_bytes())?;
writer.write_all(" ")?;
writer.write_all(&timestamp.to_string())?;
}
writer.write_all(b"\n")?;
writer.write_all("\n")?;
Ok(())
}
@ -185,32 +214,32 @@ fn write_sample(
fn label_pairs_to_text(
pairs: &[proto::LabelPair],
additional_label: Option<(&str, &str)>,
writer: &mut dyn Write,
writer: &mut dyn WriteUtf8,
) -> Result<()> {
if pairs.is_empty() && additional_label.is_none() {
return Ok(());
}
let mut separator = b"{";
let mut separator = "{";
for lp in pairs {
writer.write_all(separator)?;
writer.write_all(lp.get_name().as_bytes())?;
writer.write_all(b"=\"")?;
writer.write_all(escape_string(lp.get_value(), true).as_bytes())?;
writer.write_all(b"\"")?;
writer.write_all(lp.name())?;
writer.write_all("=\"")?;
writer.write_all(&escape_string(lp.value(), true))?;
writer.write_all("\"")?;
separator = b",";
separator = ",";
}
if let Some((name, value)) = additional_label {
writer.write_all(separator)?;
writer.write_all(name.as_bytes())?;
writer.write_all(b"=\"")?;
writer.write_all(escape_string(value, true).as_bytes())?;
writer.write_all(b"\"")?;
writer.write_all(name)?;
writer.write_all("=\"")?;
writer.write_all(&escape_string(value, true))?;
writer.write_all("\"")?;
}
writer.write_all(b"}")?;
writer.write_all("}")?;
Ok(())
}
@ -259,6 +288,27 @@ fn escape_string(v: &str, include_double_quote: bool) -> Cow<'_, str> {
}
}
trait WriteUtf8 {
fn write_all(&mut self, text: &str) -> io::Result<()>;
}
impl<W: Write> WriteUtf8 for W {
fn write_all(&mut self, text: &str) -> io::Result<()> {
Write::write_all(self, text.as_bytes())
}
}
/// Coherence forbids to impl `WriteUtf8` directly on `String`, need this
/// wrapper as a work-around.
struct StringBuf<'a>(&'a mut String);
impl WriteUtf8 for StringBuf<'_> {
fn write_all(&mut self, text: &str) -> io::Result<()> {
self.0.push_str(text);
Ok(())
}
}
#[cfg(test)]
mod tests {
@ -375,11 +425,11 @@ test_histogram_count{a="1"} 1
quantile2.set_quantile(100.0);
quantile2.set_value(5.0);
summary.set_quantile(from_vec!(vec!(quantile1, quantile2)));
summary.set_quantile(vec![quantile1, quantile2]);
let mut metric = Metric::default();
metric.set_summary(summary);
metric_family.set_metric(from_vec!(vec!(metric)));
metric_family.set_metric(vec![metric]);
let mut writer = Vec::<u8>::new();
let encoder = TextEncoder::new();
@ -395,4 +445,25 @@ test_summary_count 5
"##;
assert_eq!(ans, str::from_utf8(writer.as_slice()).unwrap());
}
#[test]
fn test_text_encoder_to_string() {
let counter_opts = Opts::new("test_counter", "test help")
.const_label("a", "1")
.const_label("b", "2");
let counter = Counter::with_opts(counter_opts).unwrap();
counter.inc();
let mf = counter.collect();
let encoder = TextEncoder::new();
let txt = encoder.encode_to_string(&mf);
let txt = txt.unwrap();
let counter_ans = r##"# HELP test_counter test help
# TYPE test_counter counter
test_counter{a="1",b="2"} 1
"##;
assert_eq!(counter_ans, txt.as_str());
}
}

View File

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

View File

@ -43,10 +43,10 @@ impl<P: Atomic> GenericGauge<P> {
/// Create a [`GenericGauge`] with the `opts` options.
pub fn with_opts(opts: Opts) -> Result<Self> {
Self::with_opts_and_label_values(&opts, &[])
Self::with_opts_and_label_values::<&str>(&opts, &[])
}
fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result<Self> {
fn with_opts_and_label_values<V: AsRef<str>>(opts: &Opts, label_values: &[V]) -> Result<Self> {
let v = Value::new(opts, ValueType::Gauge, P::T::from_i64(0), label_values)?;
Ok(Self { v: Arc::new(v) })
}
@ -129,7 +129,7 @@ impl<P: Atomic> MetricVecBuilder for GaugeVecBuilder<P> {
type M = GenericGauge<P>;
type P = Opts;
fn build(&self, opts: &Opts, vals: &[&str]) -> Result<Self::M> {
fn build<V: AsRef<str>>(&self, opts: &Opts, vals: &[V]) -> Result<Self::M> {
Self::M::with_opts_and_label_values(opts, vals)
}
}
@ -166,6 +166,8 @@ mod tests {
use super::*;
use crate::metrics::{Collector, Opts};
#[cfg(feature = "protobuf")]
use crate::proto_ext::MessageFieldExt;
#[test]
fn test_gauge() {
@ -188,7 +190,7 @@ mod tests {
assert_eq!(mfs.len(), 1);
let mf = mfs.pop().unwrap();
let m = mf.get_metric().get(0).unwrap();
let m = mf.get_metric().first().unwrap();
assert_eq!(m.get_label().len(), 2);
assert_eq!(m.get_gauge().get_value() as u64, 42);
}
@ -216,6 +218,29 @@ mod tests {
assert!(vec.remove(&labels).is_err());
}
#[test]
fn test_gauge_vec_with_owned_labels() {
let vec = GaugeVec::new(
Opts::new("test_gauge_vec", "test gauge vec help"),
&["l1", "l2"],
)
.unwrap();
let mut labels = HashMap::new();
labels.insert("l1", "v1");
labels.insert("l2", "v2");
assert!(vec.remove(&labels).is_err());
vec.with(&labels).inc();
vec.with(&labels).dec();
vec.with(&labels).add(42.0);
vec.with(&labels).sub(42.0);
vec.with(&labels).set(42.0);
assert!(vec.remove(&labels).is_ok());
assert!(vec.remove(&labels).is_err());
}
#[test]
fn test_gauge_vec_with_label_values() {
let vec = GaugeVec::new(
@ -237,4 +262,30 @@ mod tests {
assert!(vec.remove_label_values(&["v1"]).is_err());
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
}
#[test]
fn test_gauge_vec_with_owned_label_values() {
let vec = GaugeVec::new(
Opts::new("test_gauge_vec", "test gauge vec help"),
&["l1", "l2"],
)
.unwrap();
let v1 = "v1".to_string();
let v2 = "v2".to_string();
let v3 = "v3".to_string();
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
vec.with_label_values(&[v1.clone(), v2.clone()]).dec();
vec.with_label_values(&[v1.clone(), v2.clone()]).add(42.0);
vec.with_label_values(&[v1.clone(), v2.clone()]).sub(42.0);
vec.with_label_values(&[v1.clone(), v2.clone()]).set(42.0);
assert!(vec.remove_label_values(&[v1.clone()]).is_err());
assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
}
}

View File

@ -284,11 +284,11 @@ impl ShardAndCount {
/// A histogram supports two main execution paths:
///
/// 1. `observe` which increases the overall observation counter, updates the
/// observation sum and increases a single bucket counter.
/// observation sum and increases a single bucket counter.
///
/// 2. `proto` (aka. collecting the metric, from now on referred to as the
/// collect operation) which snapshots the state of the histogram and exposes it
/// as a Protobuf struct.
/// collect operation) which snapshots the state of the histogram and exposes
/// it as a Protobuf struct.
///
/// If an observe and a collect operation interleave, the latter could be
/// exposing a snapshot of the histogram that does not uphold all histogram
@ -327,14 +327,14 @@ pub struct HistogramCore {
}
impl HistogramCore {
pub fn new(opts: &HistogramOpts, label_values: &[&str]) -> Result<HistogramCore> {
pub fn new<V: AsRef<str>>(opts: &HistogramOpts, label_values: &[V]) -> Result<HistogramCore> {
let desc = opts.describe()?;
for name in &desc.variable_labels {
check_bucket_label(name)?;
}
for pair in &desc.const_label_pairs {
check_bucket_label(pair.get_name())?;
check_bucket_label(pair.name())?;
}
let label_pairs = make_label_pairs(&desc, label_values)?;
@ -453,7 +453,7 @@ impl HistogramCore {
b.set_upper_bound(*upper_bound);
buckets.push(b);
}
h.set_bucket(from_vec!(buckets));
h.set_bucket(buckets);
// Update the hot shard.
hot_shard.count.inc_by(overall_count);
@ -516,8 +516,9 @@ impl Instant {
}
pub fn elapsed(&self) -> Duration {
match &*self {
Instant::Monotonic(i) => i.elapsed(),
match self {
// We use `saturating_duration_since` to avoid panics caused by non-monotonic clocks.
Instant::Monotonic(i) => StdInstant::now().saturating_duration_since(*i),
// It is different from `Instant::Monotonic`, the resolution here is millisecond.
// The processors in an SMP system do not start all at exactly the same time
@ -541,7 +542,7 @@ impl Instant {
#[inline]
pub fn elapsed_sec(&self) -> f64 {
duration_to_seconds(self.elapsed())
self.elapsed().as_secs_f64()
}
}
@ -673,12 +674,12 @@ pub struct Histogram {
impl Histogram {
/// `with_opts` creates a [`Histogram`] with the `opts` options.
pub fn with_opts(opts: HistogramOpts) -> Result<Histogram> {
Histogram::with_opts_and_label_values(&opts, &[])
Histogram::with_opts_and_label_values::<&str>(&opts, &[])
}
fn with_opts_and_label_values(
fn with_opts_and_label_values<V: AsRef<str>>(
opts: &HistogramOpts,
label_values: &[&str],
label_values: &[V],
) -> Result<Histogram> {
let core = HistogramCore::new(opts, label_values)?;
@ -749,8 +750,7 @@ impl Histogram {
impl Metric for Histogram {
fn metric(&self) -> proto::Metric {
let mut m = proto::Metric::default();
m.set_label(from_vec!(self.core.label_pairs.clone()));
let mut m = proto::Metric::from_label(self.core.label_pairs.clone());
let h = self.core.proto();
m.set_histogram(h);
@ -769,7 +769,7 @@ impl Collector for Histogram {
m.set_name(self.core.desc.fq_name.clone());
m.set_help(self.core.desc.help.clone());
m.set_field_type(proto::MetricType::HISTOGRAM);
m.set_metric(from_vec!(vec![self.metric()]));
m.set_metric(vec![self.metric()]);
vec![m]
}
@ -782,7 +782,7 @@ impl MetricVecBuilder for HistogramVecBuilder {
type M = Histogram;
type P = HistogramOpts;
fn build(&self, opts: &HistogramOpts, vals: &[&str]) -> Result<Histogram> {
fn build<V: AsRef<str>>(&self, opts: &HistogramOpts, vals: &[V]) -> Result<Histogram> {
Histogram::with_opts_and_label_values(opts, vals)
}
}
@ -881,13 +881,6 @@ pub fn exponential_buckets(start: f64, factor: f64, count: usize) -> Result<Vec<
Ok(buckets)
}
/// `duration_to_seconds` converts Duration to seconds.
#[inline]
pub fn duration_to_seconds(d: Duration) -> f64 {
let nanos = f64::from(d.subsec_nanos()) / 1e9;
d.as_secs() as f64 + nanos
}
#[derive(Clone, Debug)]
pub struct LocalHistogramCore {
histogram: Histogram,
@ -1206,7 +1199,7 @@ impl Clone for LocalHistogramVec {
#[cfg(test)]
mod tests {
use std::f64::{EPSILON, INFINITY};
use std::f64;
use std::thread;
use std::time::Duration;
@ -1236,7 +1229,7 @@ mod tests {
assert_eq!(mfs.len(), 1);
let mf = mfs.pop().unwrap();
let m = mf.get_metric().get(0).unwrap();
let m = mf.get_metric().first().unwrap();
assert_eq!(m.get_label().len(), 2);
let proto_histogram = m.get_histogram();
assert_eq!(proto_histogram.get_sample_count(), 3);
@ -1250,11 +1243,11 @@ mod tests {
assert_eq!(mfs.len(), 1);
let mf = mfs.pop().unwrap();
let m = mf.get_metric().get(0).unwrap();
let m = mf.get_metric().first().unwrap();
assert_eq!(m.get_label().len(), 0);
let proto_histogram = m.get_histogram();
assert_eq!(proto_histogram.get_sample_count(), 0);
assert!((proto_histogram.get_sample_sum() - 0.0) < EPSILON);
assert!((proto_histogram.get_sample_sum() - 0.0) < f64::EPSILON);
assert_eq!(proto_histogram.get_bucket().len(), buckets.len())
}
@ -1286,7 +1279,7 @@ mod tests {
let m = mf.get_metric().get(0).unwrap();
let proto_histogram = m.get_histogram();
assert_eq!(proto_histogram.get_sample_count(), 3);
assert!((proto_histogram.get_sample_sum() - 0.0) > EPSILON);
assert!((proto_histogram.get_sample_sum() - 0.0) > f64::EPSILON);
}
#[test]
@ -1310,7 +1303,11 @@ mod tests {
(vec![], true, DEFAULT_BUCKETS.len()),
(vec![-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0], true, 7),
(vec![-2.0, -1.0, -0.5, 10.0, 0.5, 1.0, 2.0], false, 7),
(vec![-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, INFINITY], true, 6),
(
vec![-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, f64::INFINITY],
true,
6,
),
];
for (buckets, is_ok, length) in table {
@ -1359,16 +1356,6 @@ mod tests {
}
}
#[test]
fn test_duration_to_seconds() {
let tbls = vec![(1000, 1.0), (1100, 1.1), (100_111, 100.111)];
for (millis, seconds) in tbls {
let d = Duration::from_millis(millis);
let v = duration_to_seconds(d);
assert!((v - seconds).abs() < EPSILON);
}
}
#[test]
fn test_histogram_vec_with_label_values() {
let vec = HistogramVec::new(
@ -1385,6 +1372,27 @@ mod tests {
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
}
#[test]
fn test_histogram_vec_with_owned_label_values() {
let vec = HistogramVec::new(
HistogramOpts::new("test_histogram_vec", "test histogram vec help"),
&["l1", "l2"],
)
.unwrap();
let v1 = "v1".to_string();
let v2 = "v2".to_string();
let v3 = "v3".to_string();
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
vec.with_label_values(&[v1.clone(), v2.clone()])
.observe(1.0);
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
assert!(vec.remove_label_values(&[v1.clone()]).is_err());
assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
}
#[test]
fn test_histogram_vec_with_opts_buckets() {
let labels = ["l1", "l2"];
@ -1404,7 +1412,7 @@ mod tests {
let proto_histogram = m.get_histogram();
assert_eq!(proto_histogram.get_sample_count(), 1);
assert!((proto_histogram.get_sample_sum() - 1.0) < EPSILON);
assert!((proto_histogram.get_sample_sum() - 1.0) < f64::EPSILON);
assert_eq!(proto_histogram.get_bucket().len(), buckets.len())
}
@ -1420,7 +1428,7 @@ mod tests {
let m = histogram.metric();
let proto_histogram = m.get_histogram();
assert_eq!(proto_histogram.get_sample_count(), count);
assert!((proto_histogram.get_sample_sum() - sum) < EPSILON);
assert!((proto_histogram.get_sample_sum() - sum) < f64::EPSILON);
};
local.observe(1.0);
@ -1455,7 +1463,7 @@ mod tests {
let ms = vec.collect()[0].take_metric();
let proto_histogram = ms[0].get_histogram();
assert_eq!(proto_histogram.get_sample_count(), count);
assert!((proto_histogram.get_sample_sum() - sum) < EPSILON);
assert!((proto_histogram.get_sample_sum() - sum) < f64::EPSILON);
};
{
@ -1515,7 +1523,7 @@ mod tests {
sample_count = proto.get_sample_count();
sample_sum = proto.get_sample_sum() as u64;
// There is only one bucket thus the `[0]`.
cumulative_count = proto.get_bucket()[0].get_cumulative_count();
cumulative_count = proto.get_bucket()[0].cumulative_count();
if sample_count != cumulative_count {
break;

View File

@ -53,10 +53,11 @@ This crate supports staticly built metrics. You can use it with
some metrics.
```rust
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate prometheus;
use prometheus::{self, IntCounter, TextEncoder, Encoder};
use lazy_static::lazy_static;
use prometheus::register_int_counter;
lazy_static! {
static ref HIGH_FIVE_COUNTER: IntCounter =
register_int_counter!("highfives", "Number of high fives received").unwrap();
@ -71,11 +72,12 @@ By default, this registers with a default registry. To make a report, you can ca
[`Encoder`](trait.Encoder.html) and report to Promethus.
```
# #[macro_use] extern crate lazy_static;
#[macro_use] extern crate prometheus;
# use prometheus::IntCounter;
use prometheus::{self, TextEncoder, Encoder};
use lazy_static::lazy_static;
use prometheus::register_int_counter;
// Register & measure some metrics.
# lazy_static! {
# static ref HIGH_FIVE_COUNTER: IntCounter =
@ -127,28 +129,12 @@ This library supports four features:
#[path = "../proto/proto_model.rs"]
pub mod proto;
#[cfg(feature = "protobuf")]
macro_rules! from_vec {
($e: expr) => {
::protobuf::RepeatedField::from_vec($e)
};
}
#[cfg(not(feature = "protobuf"))]
#[path = "plain_model.rs"]
pub mod proto;
#[cfg(not(feature = "protobuf"))]
macro_rules! from_vec {
($e: expr) => {
$e
};
}
#[macro_use]
extern crate cfg_if;
#[macro_use]
extern crate lazy_static;
#[cfg(feature = "protobuf")]
mod proto_ext;
#[macro_use]
mod macros;
@ -161,6 +147,8 @@ mod errors;
mod gauge;
mod histogram;
mod metrics;
mod nohash;
mod pulling_gauge;
#[cfg(feature = "push")]
mod push;
mod registry;
@ -214,13 +202,15 @@ pub use self::encoder::Encoder;
pub use self::encoder::ProtobufEncoder;
pub use self::encoder::TextEncoder;
#[cfg(feature = "protobuf")]
pub use self::encoder::{PROTOBUF_FORMAT, TEXT_FORMAT};
pub use self::encoder::PROTOBUF_FORMAT;
pub use self::encoder::TEXT_FORMAT;
pub use self::errors::{Error, Result};
pub use self::gauge::{Gauge, GaugeVec, IntGauge, IntGaugeVec};
pub use self::histogram::DEFAULT_BUCKETS;
pub use self::histogram::{exponential_buckets, linear_buckets};
pub use self::histogram::{Histogram, HistogramOpts, HistogramTimer, HistogramVec};
pub use self::metrics::Opts;
pub use self::pulling_gauge::PullingGauge;
#[cfg(feature = "push")]
pub use self::push::{
hostname_grouping_key, push_add_collector, push_add_metrics, push_collector, push_metrics,

File diff suppressed because it is too large Load Diff

View File

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

23
src/nohash.rs Normal file
View File

@ -0,0 +1,23 @@
use std::hash::{BuildHasherDefault, Hasher};
/// Inspired by nohash-hasher, but we avoid the crate dependency because it's in public archive.
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct NoHashHasher(u64);
pub(crate) type BuildNoHashHasher = BuildHasherDefault<NoHashHasher>;
impl Hasher for NoHashHasher {
#[inline]
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, _bytes: &[u8]) {
panic!("Invalid use of NoHashHasher");
}
#[inline]
fn write_u64(&mut self, i: u64) {
self.0 = i;
}
}

View File

@ -29,7 +29,13 @@ impl LabelPair {
self.name = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.name()` instead")]
pub fn get_name(&self) -> &str {
self.name()
}
/// Returns the name of this label pair.
pub fn name(&self) -> &str {
&self.name
}
@ -37,7 +43,13 @@ impl LabelPair {
self.value = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.value()` instead")]
pub fn get_value(&self) -> &str {
self.value()
}
/// Returns the value of this label pair.
pub fn value(&self) -> &str {
&self.value
}
}
@ -99,7 +111,13 @@ impl Quantile {
self.quantile = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.quantile()` instead")]
pub fn get_quantile(&self) -> f64 {
self.quantile()
}
/// Returns the quantile of this quantile.
pub fn quantile(&self) -> f64 {
self.quantile
}
@ -107,7 +125,13 @@ impl Quantile {
self.value = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.value()` instead")]
pub fn get_value(&self) -> f64 {
self.value()
}
/// Returns the value of this quantile.
pub fn value(&self) -> f64 {
self.value
}
}
@ -129,15 +153,27 @@ impl Summary {
self.sample_count = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.sample_count()` instead")]
pub fn get_sample_count(&self) -> u64 {
self.sample_count
}
/// Returns the sample count of this summary.
pub fn sample_count(&self) -> u64 {
self.sample_count
}
pub fn set_sample_sum(&mut self, v: f64) {
self.sample_sum = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.sample_sum()` instead")]
pub fn get_sample_sum(&self) -> f64 {
self.sample_sum()
}
/// Returns the sample sum of this summary.
pub fn sample_sum(&self) -> f64 {
self.sample_sum
}
@ -232,7 +268,13 @@ impl Bucket {
self.cumulative_count = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.cumulative_count()` instead")]
pub fn get_cumulative_count(&self) -> u64 {
self.cumulative_count()
}
/// Returns the cumulative count of this bucket.
pub fn cumulative_count(&self) -> u64 {
self.cumulative_count
}
@ -240,7 +282,13 @@ impl Bucket {
self.upper_bound = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.upper_bound()` instead")]
pub fn get_upper_bound(&self) -> f64 {
self.upper_bound()
}
/// Returns the upper bound of this bucket.
pub fn upper_bound(&self) -> f64 {
self.upper_bound
}
}
@ -263,6 +311,22 @@ impl Metric {
Default::default()
}
/// Creates a new metric with the specified label pairs.
pub fn from_label(label: Vec<LabelPair>) -> Self {
Metric {
label,
..Default::default()
}
}
/// Creates a new metric with the specified gauge value.
pub fn from_gauge(gauge: Gauge) -> Self {
Metric {
gauge: gauge.into(),
..Default::default()
}
}
pub fn set_label(&mut self, v: Vec<LabelPair>) {
self.label = v;
}
@ -331,7 +395,13 @@ impl Metric {
self.timestamp_ms = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.timestamp_ms()` instead")]
pub fn get_timestamp_ms(&self) -> i64 {
self.timestamp_ms()
}
/// Returns the timestamp of this metric.
pub fn timestamp_ms(&self) -> i64 {
self.timestamp_ms
}
}
@ -373,7 +443,13 @@ impl MetricFamily {
self.name = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.name()` instead")]
pub fn get_name(&self) -> &str {
self.name()
}
/// Returns the name of this metric family.
pub fn name(&self) -> &str {
&self.name
}
@ -381,7 +457,13 @@ impl MetricFamily {
self.help = v;
}
#[deprecated(since = "0.14.0", note = "Please use `.help()` instead")]
pub fn get_help(&self) -> &str {
self.help()
}
/// Returns the help text of this metric family.
pub fn help(&self) -> &str {
&self.help
}

View File

@ -4,33 +4,34 @@
//!
//! This module only supports **Linux** platform.
use std::sync::Mutex;
use lazy_static::lazy_static;
use crate::counter::Counter;
use crate::counter::IntCounter;
use crate::desc::Desc;
use crate::gauge::Gauge;
use crate::gauge::IntGauge;
use crate::metrics::{Collector, Opts};
use crate::proto;
/// The `pid_t` data type represents process IDs.
pub use libc::pid_t;
/// Six metrics per ProcessCollector.
const METRICS_NUMBER: usize = 6;
/// Seven metrics per ProcessCollector.
const METRICS_NUMBER: usize = 7;
/// A collector which exports the current state of
/// process metrics including cpu, memory and file descriptor usage as well as
/// the process start time for the given process id.
/// A collector which exports the current state of process metrics including
/// CPU, memory and file descriptor usage, thread count, as well as the process
/// start time for the given process id.
#[derive(Debug)]
pub struct ProcessCollector {
pid: pid_t,
descs: Vec<Desc>,
cpu_total: Mutex<Counter>,
open_fds: Gauge,
max_fds: Gauge,
vsize: Gauge,
rss: Gauge,
start_time: Gauge,
cpu_total: IntCounter,
open_fds: IntGauge,
max_fds: IntGauge,
vsize: IntGauge,
rss: IntGauge,
start_time: IntGauge,
threads: IntGauge,
}
impl ProcessCollector {
@ -39,7 +40,7 @@ impl ProcessCollector {
let namespace = namespace.into();
let mut descs = Vec::new();
let cpu_total = Counter::with_opts(
let cpu_total = IntCounter::with_opts(
Opts::new(
"process_cpu_seconds_total",
"Total user and system CPU time spent in \
@ -50,14 +51,14 @@ impl ProcessCollector {
.unwrap();
descs.extend(cpu_total.desc().into_iter().cloned());
let open_fds = Gauge::with_opts(
let open_fds = IntGauge::with_opts(
Opts::new("process_open_fds", "Number of open file descriptors.")
.namespace(namespace.clone()),
)
.unwrap();
descs.extend(open_fds.desc().into_iter().cloned());
let max_fds = Gauge::with_opts(
let max_fds = IntGauge::with_opts(
Opts::new(
"process_max_fds",
"Maximum number of open file descriptors.",
@ -67,7 +68,7 @@ impl ProcessCollector {
.unwrap();
descs.extend(max_fds.desc().into_iter().cloned());
let vsize = Gauge::with_opts(
let vsize = IntGauge::with_opts(
Opts::new(
"process_virtual_memory_bytes",
"Virtual memory size in bytes.",
@ -77,7 +78,7 @@ impl ProcessCollector {
.unwrap();
descs.extend(vsize.desc().into_iter().cloned());
let rss = Gauge::with_opts(
let rss = IntGauge::with_opts(
Opts::new(
"process_resident_memory_bytes",
"Resident memory size in bytes.",
@ -87,7 +88,7 @@ impl ProcessCollector {
.unwrap();
descs.extend(rss.desc().into_iter().cloned());
let start_time = Gauge::with_opts(
let start_time = IntGauge::with_opts(
Opts::new(
"process_start_time_seconds",
"Start time of the process since unix epoch \
@ -96,17 +97,31 @@ impl ProcessCollector {
.namespace(namespace.clone()),
)
.unwrap();
// proc_start_time init once because it is immutable
if let Ok(boot_time) = procfs::boot_time_secs() {
if let Ok(stat) = procfs::process::Process::myself().and_then(|p| p.stat()) {
start_time.set(stat.starttime as i64 / *CLK_TCK + boot_time as i64);
}
}
descs.extend(start_time.desc().into_iter().cloned());
let threads = IntGauge::with_opts(
Opts::new("process_threads", "Number of OS threads in the process.")
.namespace(namespace),
)
.unwrap();
descs.extend(threads.desc().into_iter().cloned());
ProcessCollector {
pid,
descs,
cpu_total: Mutex::new(cpu_total),
cpu_total,
open_fds,
max_fds,
vsize,
rss,
start_time,
threads,
}
}
@ -133,69 +148,64 @@ impl Collector for ProcessCollector {
// file descriptors
if let Ok(fd_count) = p.fd_count() {
self.open_fds.set(fd_count as f64);
self.open_fds.set(fd_count as i64);
}
if let Ok(limits) = p.limits() {
if let procfs::process::LimitValue::Value(max) = limits.max_open_files.soft_limit {
self.max_fds.set(max as f64)
self.max_fds.set(max as i64)
}
}
// memory
self.vsize.set(p.stat.vsize as f64);
self.rss.set(p.stat.rss as f64 * *PAGESIZE);
let mut cpu_total_mfs = None;
if let Ok(stat) = p.stat() {
// memory
self.vsize.set(stat.vsize as i64);
self.rss.set((stat.rss as i64) * *PAGESIZE);
// proc_start_time
if let Some(boot_time) = *BOOT_TIME {
self.start_time
.set(p.stat.starttime as f64 / *CLK_TCK + boot_time);
// cpu
let total = (stat.utime + stat.stime) / *CLK_TCK as u64;
let past = self.cpu_total.get();
// If two threads are collecting metrics at the same time,
// the cpu_total counter may have already been updated,
// and the subtraction may underflow.
self.cpu_total.inc_by(total.saturating_sub(past));
cpu_total_mfs = Some(self.cpu_total.collect());
// threads
self.threads.set(stat.num_threads);
}
// cpu
let cpu_total_mfs = {
let cpu_total = self.cpu_total.lock().unwrap();
let total = (p.stat.utime + p.stat.stime) as f64 / *CLK_TCK;
let past = cpu_total.get();
let delta = total - past;
if delta > 0.0 {
cpu_total.inc_by(delta);
}
cpu_total.collect()
};
// collect MetricFamilys.
let mut mfs = Vec::with_capacity(METRICS_NUMBER);
mfs.extend(cpu_total_mfs);
if let Some(cpu) = cpu_total_mfs {
mfs.extend(cpu);
}
mfs.extend(self.open_fds.collect());
mfs.extend(self.max_fds.collect());
mfs.extend(self.vsize.collect());
mfs.extend(self.rss.collect());
mfs.extend(self.start_time.collect());
mfs.extend(self.threads.collect());
mfs
}
}
lazy_static! {
// getconf CLK_TCK
static ref CLK_TCK: f64 = {
static ref CLK_TCK: i64 = {
unsafe {
libc::sysconf(libc::_SC_CLK_TCK) as f64
}
libc::sysconf(libc::_SC_CLK_TCK)
}.into()
};
// getconf PAGESIZE
static ref PAGESIZE: f64 = {
static ref PAGESIZE: i64 = {
unsafe {
libc::sysconf(libc::_SC_PAGESIZE) as f64
}
libc::sysconf(libc::_SC_PAGESIZE)
}.into()
};
}
lazy_static! {
static ref BOOT_TIME: Option<f64> = procfs::boot_time_secs().ok().map(|i| i as f64);
}
#[cfg(test)]
mod tests {
use super::*;
@ -206,7 +216,7 @@ mod tests {
fn test_process_collector() {
let pc = ProcessCollector::for_self();
{
// Six metrics per process collector.
// Seven metrics per process collector.
let descs = pc.desc();
assert_eq!(descs.len(), super::METRICS_NUMBER);
let mfs = pc.collect();

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

@ -10,6 +10,8 @@ use reqwest::blocking::Client;
use reqwest::header::CONTENT_TYPE;
use reqwest::{Method, StatusCode, Url};
use lazy_static::lazy_static;
use crate::encoder::{Encoder, ProtobufEncoder};
use crate::errors::{Error, Result};
use crate::metrics::Collector;
@ -275,9 +277,9 @@ mod tests {
let mut l = proto::LabelPair::new();
l.set_name(case.0.to_owned());
let mut m = proto::Metric::new();
m.set_label(from_vec!(vec![l]));
m.set_label(vec![l]);
let mut mf = proto::MetricFamily::new();
mf.set_metric(from_vec!(vec![m]));
mf.set_metric(vec![m]);
let res = push_metrics("test", hostname_grouping_key(), "mockurl", vec![mf], None);
assert!(format!("{}", res.unwrap_err()).contains(case.1));
}

View File

@ -12,6 +12,10 @@ use crate::errors::{Error, Result};
use crate::metrics::Collector;
use crate::proto;
use cfg_if::cfg_if;
use lazy_static::lazy_static;
#[derive(Default)]
struct RegistryCore {
pub collectors_by_id: HashMap<u64, Box<dyn Collector>>,
pub dim_hashes_by_name: HashMap<String, u64>,
@ -90,7 +94,7 @@ impl RegistryCore {
let mut id_set = Vec::new();
let mut collector_id: u64 = 0;
for desc in c.desc() {
if id_set.iter().find(|id| **id == desc.id).is_none() {
if !id_set.contains(&desc.id) {
id_set.push(desc.id);
collector_id = collector_id.wrapping_add(desc.id);
}
@ -123,7 +127,7 @@ impl RegistryCore {
continue;
}
let name = mf.get_name().to_owned();
let name = mf.name().to_owned();
match mf_by_name.entry(name) {
BEntry::Vacant(entry) => {
entry.insert(mf);
@ -162,8 +166,8 @@ impl RegistryCore {
}
for (lp1, lp2) in lps1.iter().zip(lps2.iter()) {
if lp1.get_value() != lp2.get_value() {
return lp1.get_value().cmp(lp2.get_value());
if lp1.value() != lp2.value() {
return lp1.value().cmp(lp2.value());
}
}
@ -173,17 +177,17 @@ impl RegistryCore {
// here, even for inconsistent metrics. So sort equal metrics
// by their timestamp, with missing timestamps (implying "now")
// coming last.
m1.get_timestamp_ms().cmp(&m2.get_timestamp_ms())
m1.timestamp_ms().cmp(&m2.timestamp_ms())
});
}
// Write out MetricFamilies sorted by their name.
mf_by_name
.into_iter()
.map(|(_, mut m)| {
.into_values()
.map(|mut m| {
// Add registry namespace prefix, if any.
if let Some(ref namespace) = self.prefix {
let prefixed = format!("{}_{}", namespace, m.get_name());
let prefixed = format!("{}_{}", namespace, m.name());
m.set_name(prefixed);
}
@ -200,9 +204,9 @@ impl RegistryCore {
.collect();
for metric in m.mut_metric().iter_mut() {
let mut labels: Vec<_> = metric.take_label().into();
let mut labels: Vec<_> = metric.take_label();
labels.append(&mut pairs.clone());
metric.set_label(labels.into());
metric.set_label(labels);
}
}
m
@ -213,31 +217,15 @@ impl RegistryCore {
/// A struct for registering Prometheus collectors, collecting their metrics, and gathering
/// them into `MetricFamilies` for exposition.
#[derive(Clone, Debug)]
#[derive(Clone, Default, Debug)]
pub struct Registry {
r: Arc<RwLock<RegistryCore>>,
}
impl Default for Registry {
fn default() -> Registry {
let r = RegistryCore {
collectors_by_id: HashMap::new(),
dim_hashes_by_name: HashMap::new(),
desc_ids: HashSet::new(),
labels: None,
prefix: None,
};
Registry {
r: Arc::new(RwLock::new(r)),
}
}
}
impl Registry {
/// `new` creates a Registry.
pub fn new() -> Registry {
Registry::default()
Default::default()
}
/// Create a new registry, with optional custom prefix and labels.
@ -353,7 +341,10 @@ mod tests {
use crate::counter::{Counter, CounterVec};
use crate::desc::Desc;
use crate::metrics::{Collector, Opts};
#[cfg(feature = "protobuf")]
use crate::proto;
#[cfg(feature = "protobuf")]
use crate::proto_ext::MessageFieldExt;
#[test]
fn test_registry() {
@ -413,9 +404,9 @@ mod tests {
let mfs = r.gather();
assert_eq!(mfs.len(), 3);
assert_eq!(mfs[0].get_name(), "test_2_counter");
assert_eq!(mfs[1].get_name(), "test_a_counter");
assert_eq!(mfs[2].get_name(), "test_b_counter");
assert_eq!(mfs[0].name(), "test_2_counter");
assert_eq!(mfs[1].name(), "test_a_counter");
assert_eq!(mfs[2].name(), "test_b_counter");
let r = Registry::new();
let opts = Opts::new("test", "test help")
@ -485,7 +476,7 @@ mod tests {
let mfs = r.gather();
assert_eq!(mfs.len(), 1);
assert_eq!(mfs[0].get_name(), "common_prefix_test_a_counter");
assert_eq!(mfs[0].name(), "common_prefix_test_a_counter");
}
#[test]
@ -505,8 +496,8 @@ mod tests {
let mfs = r.gather();
assert_eq!(mfs.len(), 2);
assert_eq!(mfs[0].get_name(), "test_a_counter");
assert_eq!(mfs[1].get_name(), "test_vec");
assert_eq!(mfs[0].name(), "test_a_counter");
assert_eq!(mfs[1].name(), "test_vec");
let mut needle = proto::LabelPair::default();
needle.set_name("tkey".to_string());

View File

@ -2,6 +2,8 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::thread;
use std::time::{Duration, Instant};
use lazy_static::lazy_static;
/// Milliseconds since ANCHOR.
static RECENT: AtomicU64 = AtomicU64::new(0);
lazy_static! {

View File

@ -37,11 +37,11 @@ pub struct Value<P: Atomic> {
}
impl<P: Atomic> Value<P> {
pub fn new<D: Describer>(
pub fn new<D: Describer, V: AsRef<str>>(
describer: &D,
val_type: ValueType,
val: P::T,
label_values: &[&str],
label_values: &[V],
) -> Result<Self> {
let desc = describer.describe()?;
let label_pairs = make_label_pairs(&desc, label_values)?;
@ -85,8 +85,7 @@ impl<P: Atomic> Value<P> {
}
pub fn metric(&self) -> Metric {
let mut m = Metric::default();
m.set_label(from_vec!(self.label_pairs.clone()));
let mut m = Metric::from_label(self.label_pairs.clone());
let val = self.get();
match self.val_type {
@ -110,12 +109,12 @@ impl<P: Atomic> Value<P> {
m.set_name(self.desc.fq_name.clone());
m.set_help(self.desc.help.clone());
m.set_field_type(self.val_type.metric_type());
m.set_metric(from_vec!(vec![self.metric()]));
m.set_metric(vec![self.metric()]);
m
}
}
pub fn make_label_pairs(desc: &Desc, label_values: &[&str]) -> Result<Vec<LabelPair>> {
pub fn make_label_pairs<V: AsRef<str>>(desc: &Desc, label_values: &[V]) -> Result<Vec<LabelPair>> {
if desc.variable_labels.len() != label_values.len() {
return Err(Error::InconsistentCardinality {
expect: desc.variable_labels.len(),
@ -136,7 +135,7 @@ pub fn make_label_pairs(desc: &Desc, label_values: &[&str]) -> Result<Vec<LabelP
for (i, n) in desc.variable_labels.iter().enumerate() {
let mut label_pair = LabelPair::default();
label_pair.set_name(n.clone());
label_pair.set_value(label_values[i].to_owned());
label_pair.set_value(label_values[i].as_ref().to_owned());
label_pairs.push(label_pair);
}

View File

@ -2,7 +2,7 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
use std::collections::HashMap;
use std::hash::Hasher;
use std::hash::{BuildHasher, Hasher};
use std::sync::Arc;
use fnv::FnvHasher;
@ -11,6 +11,7 @@ use parking_lot::RwLock;
use crate::desc::{Desc, Describer};
use crate::errors::{Error, Result};
use crate::metrics::{Collector, Metric};
use crate::nohash::BuildNoHashHasher;
use crate::proto::{MetricFamily, MetricType};
/// An interface for building a metric vector.
@ -21,12 +22,13 @@ pub trait MetricVecBuilder: Send + Sync + Clone {
type P: Describer + Sync + Send + Clone;
/// `build` builds a [`Metric`] with option and corresponding label names.
fn build(&self, _: &Self::P, _: &[&str]) -> Result<Self::M>;
fn build<V: AsRef<str>>(&self, _: &Self::P, _: &[V]) -> Result<Self::M>;
}
#[derive(Debug)]
pub(crate) struct MetricVecCore<T: MetricVecBuilder> {
pub children: RwLock<HashMap<u64, T::M>>,
// the key is pre-hashed, and so we use a no-hash hasher to avoid hashing again.
pub children: RwLock<HashMap<u64, T::M, BuildNoHashHasher>>,
pub desc: Desc,
pub metric_type: MetricType,
pub new_metric: T,
@ -45,11 +47,14 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
for child in children.values() {
metrics.push(child.metric());
}
m.set_metric(from_vec!(metrics));
m.set_metric(metrics);
m
}
pub fn get_metric_with_label_values(&self, vals: &[&str]) -> Result<T::M> {
pub fn get_metric_with_label_values<V>(&self, vals: &[V]) -> Result<T::M>
where
V: AsRef<str> + std::fmt::Debug,
{
let h = self.hash_label_values(vals)?;
if let Some(metric) = self.children.read().get(&h).cloned() {
@ -59,7 +64,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
self.get_or_create_metric(h, vals)
}
pub fn get_metric_with(&self, labels: &HashMap<&str, &str>) -> Result<T::M> {
pub fn get_metric_with<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<T::M>
where
V: AsRef<str> + std::fmt::Debug,
{
let h = self.hash_labels(labels)?;
if let Some(metric) = self.children.read().get(&h).cloned() {
@ -70,7 +78,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
self.get_or_create_metric(h, &vals)
}
pub fn delete_label_values(&self, vals: &[&str]) -> Result<()> {
pub fn delete_label_values<V>(&self, vals: &[V]) -> Result<()>
where
V: AsRef<str> + std::fmt::Debug,
{
let h = self.hash_label_values(vals)?;
let mut children = self.children.write();
@ -81,7 +92,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
Ok(())
}
pub fn delete(&self, labels: &HashMap<&str, &str>) -> Result<()> {
pub fn delete<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<()>
where
V: AsRef<str> + std::fmt::Debug,
{
let h = self.hash_labels(labels)?;
let mut children = self.children.write();
@ -97,7 +111,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
self.children.write().clear();
}
pub(crate) fn hash_label_values(&self, vals: &[&str]) -> Result<u64> {
pub(crate) fn hash_label_values<V>(&self, vals: &[V]) -> Result<u64>
where
V: AsRef<str> + std::fmt::Debug,
{
if vals.len() != self.desc.variable_labels.len() {
return Err(Error::InconsistentCardinality {
expect: self.desc.variable_labels.len(),
@ -107,13 +124,16 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
let mut h = FnvHasher::default();
for val in vals {
h.write(val.as_bytes());
h.write(val.as_ref().as_bytes());
}
Ok(h.finish())
}
fn hash_labels(&self, labels: &HashMap<&str, &str>) -> Result<u64> {
fn hash_labels<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<u64>
where
V: AsRef<str> + std::fmt::Debug,
{
if labels.len() != self.desc.variable_labels.len() {
return Err(Error::InconsistentCardinality {
expect: self.desc.variable_labels.len(),
@ -124,7 +144,7 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
let mut h = FnvHasher::default();
for name in &self.desc.variable_labels {
match labels.get(&name.as_ref()) {
Some(val) => h.write(val.as_bytes()),
Some(val) => h.write(val.as_ref().as_bytes()),
None => {
return Err(Error::Msg(format!(
"label name {} missing in label map",
@ -137,11 +157,17 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
Ok(h.finish())
}
fn get_label_values<'a>(&self, labels: &'a HashMap<&str, &str>) -> Result<Vec<&'a str>> {
fn get_label_values<'a, V, S: BuildHasher>(
&'a self,
labels: &'a HashMap<&str, V, S>,
) -> Result<Vec<&'a str>>
where
V: AsRef<str> + std::fmt::Debug,
{
let mut values = Vec::new();
for name in &self.desc.variable_labels {
match labels.get(&name.as_ref()) {
Some(val) => values.push(*val),
Some(val) => values.push(val.as_ref()),
None => {
return Err(Error::Msg(format!(
"label name {} missing in label map",
@ -153,7 +179,10 @@ impl<T: MetricVecBuilder> MetricVecCore<T> {
Ok(values)
}
fn get_or_create_metric(&self, hash: u64, label_values: &[&str]) -> Result<T::M> {
fn get_or_create_metric<V>(&self, hash: u64, label_values: &[V]) -> Result<T::M>
where
V: AsRef<str> + std::fmt::Debug,
{
let mut children = self.children.write();
// Check exist first.
if let Some(metric) = children.get(&hash).cloned() {
@ -188,7 +217,7 @@ impl<T: MetricVecBuilder> MetricVec<T> {
pub fn create(metric_type: MetricType, new_metric: T, opts: T::P) -> Result<MetricVec<T>> {
let desc = opts.describe()?;
let v = MetricVecCore {
children: RwLock::new(HashMap::new()),
children: RwLock::new(HashMap::default()),
desc,
metric_type,
new_metric,
@ -221,7 +250,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
/// an alternative to avoid that type of mistake. For higher label numbers, the
/// latter has a much more readable (albeit more verbose) syntax, but it comes
/// with a performance overhead (for creating and processing the Labels map).
pub fn get_metric_with_label_values(&self, vals: &[&str]) -> Result<T::M> {
pub fn get_metric_with_label_values<V>(&self, vals: &[V]) -> Result<T::M>
where
V: AsRef<str> + std::fmt::Debug,
{
self.v.get_metric_with_label_values(vals)
}
@ -237,7 +269,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
/// This method is used for the same purpose as
/// `get_metric_with_label_values`. See there for pros and cons of the two
/// methods.
pub fn get_metric_with(&self, labels: &HashMap<&str, &str>) -> Result<T::M> {
pub fn get_metric_with<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<T::M>
where
V: AsRef<str> + std::fmt::Debug,
{
self.v.get_metric_with(labels)
}
@ -254,14 +289,20 @@ impl<T: MetricVecBuilder> MetricVec<T> {
/// ).unwrap();
/// vec.with_label_values(&["404", "POST"]).inc()
/// ```
pub fn with_label_values(&self, vals: &[&str]) -> T::M {
pub fn with_label_values<V>(&self, vals: &[V]) -> T::M
where
V: AsRef<str> + std::fmt::Debug,
{
self.get_metric_with_label_values(vals).unwrap()
}
/// `with` works as `get_metric_with`, but panics if an error occurs. The method allows
/// neat syntax like:
/// httpReqs.with(Labels{"status":"404", "method":"POST"}).inc()
pub fn with(&self, labels: &HashMap<&str, &str>) -> T::M {
pub fn with<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> T::M
where
V: AsRef<str> + std::fmt::Debug,
{
self.get_metric_with(labels).unwrap()
}
@ -277,7 +318,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
/// alternative to avoid that type of mistake. For higher label numbers, the
/// latter has a much more readable (albeit more verbose) syntax, but it comes
/// with a performance overhead (for creating and processing the Labels map).
pub fn remove_label_values(&self, vals: &[&str]) -> Result<()> {
pub fn remove_label_values<V>(&self, vals: &[V]) -> Result<()>
where
V: AsRef<str> + std::fmt::Debug,
{
self.v.delete_label_values(vals)
}
@ -289,7 +333,10 @@ impl<T: MetricVecBuilder> MetricVec<T> {
///
/// This method is used for the same purpose as `delete_label_values`. See
/// there for pros and cons of the two methods.
pub fn remove(&self, labels: &HashMap<&str, &str>) -> Result<()> {
pub fn remove<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<()>
where
V: AsRef<str> + std::fmt::Debug,
{
self.v.delete(labels)
}
@ -348,6 +395,40 @@ mod tests {
assert!(vec.remove(&labels3).is_err());
}
#[test]
fn test_counter_vec_with_owned_labels() {
let vec = CounterVec::new(
Opts::new("test_couter_vec", "test counter vec help"),
&["l1", "l2"],
)
.unwrap();
let v1 = "v1".to_string();
let v2 = "v2".to_string();
let mut labels = HashMap::new();
labels.insert("l1", v1.clone());
labels.insert("l2", v2.clone());
assert!(vec.remove(&labels).is_err());
vec.with(&labels).inc();
assert!(vec.remove(&labels).is_ok());
assert!(vec.remove(&labels).is_err());
let mut labels2 = HashMap::new();
labels2.insert("l1", v2.clone());
labels2.insert("l2", v1.clone());
vec.with(&labels).inc();
assert!(vec.remove(&labels2).is_err());
vec.with(&labels).inc();
let mut labels3 = HashMap::new();
labels3.insert("l1", v1.clone());
assert!(vec.remove(&labels3).is_err());
}
#[test]
fn test_counter_vec_with_label_values() {
let vec = CounterVec::new(
@ -365,6 +446,27 @@ mod tests {
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
}
#[test]
fn test_counter_vec_with_owned_label_values() {
let vec = CounterVec::new(
Opts::new("test_vec", "test counter vec help"),
&["l1", "l2"],
)
.unwrap();
let v1 = "v1".to_string();
let v2 = "v2".to_string();
let v3 = "v3".to_string();
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
assert!(vec.remove_label_values(&[v1.clone()]).is_err());
assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
}
#[test]
fn test_gauge_vec_with_labels() {
let vec = GaugeVec::new(
@ -388,6 +490,32 @@ mod tests {
assert!(vec.remove(&labels).is_err());
}
#[test]
fn test_gauge_vec_with_owned_labels() {
let vec = GaugeVec::new(
Opts::new("test_gauge_vec", "test gauge vec help"),
&["l1", "l2"],
)
.unwrap();
let v1 = "v1".to_string();
let v2 = "v2".to_string();
let mut labels = HashMap::new();
labels.insert("l1", v1.clone());
labels.insert("l2", v2.clone());
assert!(vec.remove(&labels).is_err());
vec.with(&labels).inc();
vec.with(&labels).dec();
vec.with(&labels).add(42.0);
vec.with(&labels).sub(42.0);
vec.with(&labels).set(42.0);
assert!(vec.remove(&labels).is_ok());
assert!(vec.remove(&labels).is_err());
}
#[test]
fn test_gauge_vec_with_label_values() {
let vec = GaugeVec::new(
@ -410,6 +538,32 @@ mod tests {
assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
}
#[test]
fn test_gauge_vec_with_owned_label_values() {
let vec = GaugeVec::new(
Opts::new("test_gauge_vec", "test gauge vec help"),
&["l1", "l2"],
)
.unwrap();
let v1 = "v1".to_string();
let v2 = "v2".to_string();
let v3 = "v3".to_string();
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
vec.with_label_values(&[v1.clone(), v2.clone()]).dec();
vec.with_label_values(&[v1.clone(), v2.clone()]).add(42.0);
vec.with_label_values(&[v1.clone(), v2.clone()]).sub(42.0);
vec.with_label_values(&[v1.clone(), v2.clone()]).set(42.0);
assert!(vec.remove_label_values(&[v1.clone()]).is_err());
assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
}
#[test]
fn test_vec_get_metric_with() {
let vec = CounterVec::new(
@ -428,7 +582,7 @@ mod tests {
let label_pairs = m.get_label();
assert_eq!(label_pairs.len(), labels.len());
for lp in label_pairs.iter() {
assert_eq!(lp.get_value(), labels[lp.get_name()]);
assert_eq!(lp.value(), labels[lp.name()]);
}
}
}

View File

@ -7,18 +7,19 @@ description = "Static metric helper utilities for rust-prometheus."
repository = "https://github.com/tikv/rust-prometheus"
homepage = "https://github.com/tikv/rust-prometheus"
documentation = "https://docs.rs/prometheus-static-metric"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
syn = { version = "1.0", features = ["full", "extra-traits"] }
syn = { version = "2.0", features = ["full", "extra-traits"] }
proc-macro2 = "1.0"
quote = "1.0"
lazy_static = "1.4"
[dev-dependencies]
criterion = "0.3"
criterion = "0.5"
prometheus = { path = "../" }
[features]

View File

@ -10,6 +10,8 @@ build:
test:
cargo test --features="${ENABLE_FEATURES}" -- --nocapture
dev: format test
format:
@cargo fmt --all -- --check >/dev/null || cargo fmt --all

View File

@ -46,7 +46,7 @@ introducing a lot of templating code.
- Add to `lib.rs`:
```rust
extern crate prometheus_static_metric;
use prometheus_static_metric;
```
## Example
@ -89,14 +89,10 @@ fn main() {
For heavier scenario that a global shared static-metric might not be effecient enough, you can use `make_auto_flush_static_metric!` macro, which will store data in local thread storage, with a custom rate to flush to global `MetricVec`.
```rust
#[macro_use]
extern crate lazy_static;
extern crate prometheus;
extern crate prometheus_static_metric;
use prometheus::*;
use prometheus_static_metric::auto_flush_from;
use prometheus_static_metric::make_auto_flush_static_metric;
use lazy_static:lazy_static;
use prometheus_static_metric::{auto_flush_from, make_auto_flush_static_metric};
make_auto_flush_static_metric! {

View File

@ -1,11 +1,8 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
extern crate criterion;
extern crate prometheus;
extern crate prometheus_static_metric;
use criterion::{criterion_group, criterion_main, Criterion};
use prometheus::{IntCounter, IntCounterVec, Opts};
use prometheus_static_metric::make_static_metric;
/// Single `IntCounter` performance.

View File

@ -1,12 +1,9 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate prometheus;
extern crate prometheus_static_metric;
use prometheus::IntCounterVec;
use lazy_static::lazy_static;
use prometheus::register_int_counter_vec;
use prometheus_static_metric::make_static_metric;
make_static_metric! {

View File

@ -1,13 +1,10 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
#[macro_use]
extern crate lazy_static;
extern crate prometheus;
extern crate prometheus_static_metric;
use std::cell::Cell;
use prometheus::*;
use lazy_static::lazy_static;
use prometheus_static_metric::make_static_metric;
make_static_metric! {

View File

@ -5,14 +5,10 @@
Use metric enums to reuse possible values of a label.
*/
#[macro_use]
extern crate lazy_static;
extern crate prometheus;
extern crate prometheus_static_metric;
use prometheus::*;
use prometheus_static_metric::auto_flush_from;
use prometheus_static_metric::make_auto_flush_static_metric;
use lazy_static::lazy_static;
use prometheus_static_metric::{auto_flush_from, make_auto_flush_static_metric};
make_auto_flush_static_metric! {
@ -95,11 +91,6 @@ fn main() {
/*
/// Pseudo macro expanded code of make_auto_flush_static_counter
#[macro_use]
extern crate lazy_static;
extern crate prometheus;
extern crate prometheus_static_metric;
use std::cell::Cell;
#[allow(unused_imports)]
@ -110,6 +101,8 @@ use std::mem;
use std::mem::MaybeUninit;
use std::thread::LocalKey;
use lazy_static::lazy_static;
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq)]

View File

@ -5,14 +5,11 @@
Use metric enums to reuse possible values of a label.
*/
#[macro_use]
extern crate lazy_static;
extern crate prometheus;
extern crate prometheus_static_metric;
use prometheus::*;
use prometheus_static_metric::auto_flush_from;
use prometheus_static_metric::make_auto_flush_static_metric;
use lazy_static::lazy_static;
use prometheus_static_metric::{auto_flush_from, make_auto_flush_static_metric};
make_auto_flush_static_metric! {
@ -90,11 +87,6 @@ fn main() {
/*
/// Pseudo macro expanded code of make_auto_flush_static_metric_histogram
#[macro_use]
extern crate lazy_static;
extern crate prometheus;
extern crate prometheus_static_metric;
use std::cell::Cell;
#[allow(unused_imports)]
@ -105,6 +97,8 @@ use std::mem;
use std::mem::MaybeUninit;
use std::thread::LocalKey;
use lazy_static::lazy_static;
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq)]

View File

@ -6,10 +6,8 @@ Use metric enums to reuse possible values of a label.
*/
extern crate prometheus;
extern crate prometheus_static_metric;
use prometheus::{CounterVec, IntCounterVec, Opts};
use prometheus_static_metric::make_static_metric;
make_static_metric! {

View File

@ -7,14 +7,13 @@ by using the `register_static_xxx!` macro provided by this crate.
*/
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate prometheus;
extern crate prometheus_static_metric;
use prometheus::exponential_buckets;
use prometheus_static_metric::*;
use lazy_static::lazy_static;
use prometheus::{register_counter_vec, register_histogram_vec};
use prometheus_static_metric::{
make_static_metric, register_static_counter_vec, register_static_histogram_vec,
};
make_static_metric! {
pub struct HttpRequestStatistics: Counter {

View File

@ -1,9 +1,7 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
extern crate prometheus;
extern crate prometheus_static_metric;
use prometheus::{CounterVec, Opts};
use prometheus_static_metric::make_static_metric;
make_static_metric! {

View File

@ -1,12 +1,9 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate prometheus;
extern crate prometheus_static_metric;
use prometheus::CounterVec;
use lazy_static::lazy_static;
use prometheus::register_counter_vec;
use prometheus_static_metric::make_static_metric;
make_static_metric! {

View File

@ -7,6 +7,8 @@ use proc_macro2::{Span, TokenStream as Tokens};
use quote::{ToTokens, TokenStreamExt};
use syn::{Ident, Visibility};
use quote::quote;
use super::parser::*;
use super::util;
use crate::builder::TokensBuilder;
@ -194,7 +196,7 @@ impl AutoFlushTokensBuilder {
let offset_fetchers = builder_contexts
.iter()
.map(|m| offset_fetcher(m))
.map(offset_fetcher)
.collect::<Vec<Tokens>>();
let delegator_tokens = if metric_type.to_string().contains("Counter") {
@ -712,7 +714,6 @@ impl<'a> MetricBuilderContext<'a> {
})#local_suffix_call,
}
} else {
let prev_labels_ident = prev_labels_ident;
quote! {
#name: #member_type::from(
#(

View File

@ -6,6 +6,8 @@ use syn::parse::{Parse, ParseStream};
use syn::token::*;
use syn::*;
use quote::quote;
pub struct AutoFlushFromDef {
class_name: Ident,
inner_class_name: Ident,

View File

@ -7,6 +7,8 @@ use proc_macro2::{Span, TokenStream as Tokens};
use quote::TokenStreamExt;
use syn::{Ident, Visibility};
use quote::quote;
use super::parser::*;
use super::util;
@ -298,7 +300,6 @@ impl<'a> MetricBuilderContext<'a> {
})#local_suffix_call,
}
} else {
let prev_labels_ident = prev_labels_ident;
quote! {
#name: #member_type::from(
#(

View File

@ -6,10 +6,11 @@ This crate provides staticly built metrics to your Prometheus application.
This is useful since it reduces the amount of branching and processing needed at runtime to collect metrics.
```rust
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate prometheus;
use prometheus::{self, IntCounter, TextEncoder, Encoder};
use lazy_static::lazy_static;
use prometheus::register_int_counter;
lazy_static! {
static ref HIGH_FIVE_COUNTER: IntCounter =
register_int_counter!("highfives", "Number of high fives recieved").unwrap();
@ -21,14 +22,6 @@ assert_eq!(HIGH_FIVE_COUNTER.get(), 1);
Is it reccomended that you consult the [`prometheus` documentation for more information.](https://docs.rs/prometheus/)
*/
extern crate lazy_static;
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate syn;
mod auto_flush_builder;
mod auto_flush_from;
mod builder;
@ -51,7 +44,7 @@ pub fn make_static_metric(input: TokenStream) -> TokenStream {
TokensBuilder::build(body).into()
}
/// Build auto flush able static metrics.
/// Build auto flushable static metrics.
/// refer to https://github.com/tikv/rust-prometheus/tree/master/static-metric for more info.
#[proc_macro]
pub fn make_auto_flush_static_metric(input: TokenStream) -> TokenStream {
@ -59,7 +52,7 @@ pub fn make_auto_flush_static_metric(input: TokenStream) -> TokenStream {
AutoFlushTokensBuilder::build(body).into()
}
/// Instantiate a auto flush able static metric struct from a HistogramVec or CounterVec.
/// Instantiate an auto flushable static metric struct from a HistogramVec or CounterVec.
#[proc_macro]
pub fn auto_flush_from(input: TokenStream) -> TokenStream {
let def: AutoFlushFromDef = syn::parse(input).unwrap();

View File

@ -10,6 +10,7 @@ use syn::*;
/// Matches `label_enum` keyword.
struct LabelEnum {
#[allow(dead_code)]
pub span: Span,
}

View File

@ -5,6 +5,8 @@ use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::*;
use quote::quote;
/// Matches `register_static_xxx_vec!(static_struct_name, name, desc, labels, ...)`.
pub struct RegisterMethodInvoking {
static_struct_name: Ident,

View File

@ -9,8 +9,8 @@ pub fn is_local_metric(metric_type: Ident) -> bool {
pub fn to_non_local_metric_type(metric_type: Ident) -> Ident {
let metric_type_str = metric_type.to_string();
if metric_type_str.starts_with("Local") {
Ident::new(&metric_type_str[5..], Span::call_site())
if let Some(stripped) = metric_type_str.strip_prefix("Local") {
Ident::new(stripped, Span::call_site())
} else {
metric_type
}

View File

@ -1,8 +1,5 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
extern crate prometheus;
extern crate prometheus_static_metric;
use prometheus_static_metric::make_static_metric;
make_static_metric! {

View File

@ -1,8 +1,5 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
extern crate prometheus;
extern crate prometheus_static_metric;
use prometheus::core::Collector;
use prometheus::{Counter, CounterVec, Opts};
use prometheus_static_metric::make_static_metric;