prometheus-0.13.4/.cargo_vcs_info.json0000644000000001360000000000100133140ustar { "git": { "sha1": "04fce2f3bf81920c2607e6572dd9eba309969d22" }, "path_in_vcs": "" }prometheus-0.13.4/.github/ISSUE_TEMPLATE/bug_report.md000064400000000000000000000010551046102023000203220ustar 00000000000000--- name: Bug report about: Found a bug? Help us squash it! --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **System information** * CPU architecture: * Distribution and kernel version: * SELinux on?: * Any other system details we should know?: **Additional context** Add any other context about the problem here. prometheus-0.13.4/.github/ISSUE_TEMPLATE/feature_request.md000064400000000000000000000010711046102023000213530ustar 00000000000000--- name: Feature request about: Help us develop our roadmap and direction. --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. prometheus-0.13.4/.github/ISSUE_TEMPLATE/something-else.md000064400000000000000000000000771046102023000211000ustar 00000000000000--- name: Something Else about: Just give me a text box! --- prometheus-0.13.4/.github/PULL_REQUEST_TEMPLATE/bug_fix.md000064400000000000000000000006751046102023000207000ustar 00000000000000--- name: Bug fix about: A bug squashed. --- **Related bugs:** This bug fix closes issue #???. **Description of problem:** Describe what was causing the related issue to happen. **Description of solution:** Describe the rationale behind the fix. **Checklist:** The CI will check all of these, but you'll need to have done them: * [ ] `cargo fmt -- --check` passes. * [ ] `cargo +nightly clippy` has no warnings. * [ ] `cargo test` passes. prometheus-0.13.4/.github/PULL_REQUEST_TEMPLATE/feature.md000064400000000000000000000010601046102023000206750ustar 00000000000000--- name: Feature about: The dawn of a new era. --- **Related features:** This feature resolves issue #???. **Description of feature:** A short description of the feature implemented. **Implementation:** Describe any pieces of the implementation that deserve further explanation. Detail any gotchas, uncertainties, or open questions about the implementation. **Checklist:** The CI will check all of these, but you'll need to have done them: * [ ] `cargo fmt -- --check` passes. * [ ] `cargo +nightly clippy` has no warnings. * [ ] `cargo test` passes. prometheus-0.13.4/.github/PULL_REQUEST_TEMPLATE/something-else.md000064400000000000000000000001001046102023000221570ustar 00000000000000--- name: Something Else about: Just give me a text box! --- prometheus-0.13.4/.github/dependabot.yml000064400000000000000000000001541046102023000162740ustar 00000000000000version: 2 updates: - package-ecosystem: "cargo" directory: "/" schedule: interval: "daily" prometheus-0.13.4/.github/workflows/rust.yml000064400000000000000000000071461046102023000172310ustar 00000000000000--- name: Rust on: push: branches: [master] pull_request: branches: [master] env: CARGO_TERM_COLOR: always # Pinned toolchain for linting and benchmarks ACTIONS_LINTS_TOOLCHAIN: 1.78.0 # Minimum supported Rust version (MSRV) ACTION_MSRV_TOOLCHAIN: 1.74.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@v4 - name: Install toolchain uses: dtolnay/rust-toolchain@master with: 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 - 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@v4 - name: Install toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ env['ACTIONS_LINTS_TOOLCHAIN'] }} - name: cargo bench (prometheus) run: cargo bench -p prometheus - name: cargo bench (prometheus-static-metric) run: cargo bench -p prometheus-static-metric prometheus-0.13.4/.gitignore000064400000000000000000000004251046102023000140750ustar 00000000000000 # OSX leaves these everywhere on SMB shares ._* # OSX trash .DS_Store # Eclipse files .classpath .project .settings/** # Vim swap files *.swp # Files generated by JetBrains IDEs, e.g. IntelliJ IDEA .idea/ *.iml out/ # Vscode files .vscode/** target tmp /bin Cargo.lock prometheus-0.13.4/CHANGELOG.md000064400000000000000000000075001046102023000137170ustar 00000000000000# Changelog ## 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) - Improvement: Replace regex with memchr (#385) - Improvement: Update reqwest requirement from ^0.10 to ^0.11 (#379) ## 0.11.0 - Improvement: Switch to more efficient `fd_count()` for `process_open_fds` (#357). - API change: Change Integer Counter type from AtomicI64 to AtomicU64 (#365). - Internal change: Update dependencies. ## 0.10.0 - Improvement: Use different generic parameters for name and help at metric construction (#324). - Bug fix: Error instead of panic when constructing histogram with unequal label key and label value length (#326). - Bug fix: Return `Error::AlreadyReg` on duplicate collector registration (#333). - Bug fix: Make Histogram::observe atomic across collects (#314). - Internal change: Replace spin with parking_lot (#318). - Internal change: Optimize metric formatting (#327). - Internal change: Update parking_lot and procfs dependencies (#337). ## 0.9.0 - Add: Implement `encode` function for summary type metrics. ## 0.8.0 - Add: Reset Counters (#261) - Add: Discard Histogram timers (#257) - Add: `observe_closure_duration` for better observing closure duration for local histogram (#296) - Fix a bug that global labels are not handled correctly (#269) - Improve linear bucket accuracy by avoiding accumulating error (#276) - Internal change: Use [thiserror](https://docs.rs/thiserror) to generate the error structure (#285) - Internal change: Switch from procinfo to [procfs](https://docs.rs/procfs) (#290) - Internal change: Update to newer dependencies ## 0.7.0 - Provide immutable interface for local counters. ## 0.6.1 - Fix compile error when ProtoBuf feature is not enabled (#240). ## 0.6.0 - Add: Expose the default registry (#231). - Add: Support common namespace prefix and common labels (#233). ## 0.5.0 - Change: Added TLS and BasicAuthentication support to `push` client. ## 0.4.2 - Change: Update to use protobuf 2.0. ## 0.4.1 - Change: `(Local)(Int)Counter.inc_by` only panics in debug build if the given value is < 0 (#168). ## 0.4.0 - Add: Provides `IntCounter`, `IntCounterVec`, `IntGauge`, `IntGaugeVec`, `LocalIntCounter`, `LocalIntCounterVec` for better performance when metric values are all integers (#158). - Change: When the given value is < 0, `(Local)Counter.inc_by` no longer return errors, instead it will panic (#156). - Improve performance (#161). prometheus-0.13.4/CODE_OF_CONDUCT.md000064400000000000000000000062141046102023000147060ustar 00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at coc@pingcap.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ prometheus-0.13.4/Cargo.lock0000644000001274550000000000100113050ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytes" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "core-foundation" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "core-foundation-sys" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "criterion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "is-terminal", "itertools", "num-traits", "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "either" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encoding_rs" version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "fastrand" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ "foreign-types-shared", ] [[package]] name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-io", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "getopts" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ "unicode-width", ] [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", "http 1.1.0", "indexmap", "slab", "tokio", "tokio-util", "tracing", ] [[package]] name = "half" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", ] [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-body" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http 0.2.12", "pin-project-lite", ] [[package]] name = "http-body" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", "http 1.1.0", ] [[package]] name = "http-body-util" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ "bytes", "futures-core", "http 1.1.0", "http-body 1.0.0", "pin-project-lite", ] [[package]] name = "httparse" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", "socket2", "tokio", "tower-service", "tracing", "want", ] [[package]] name = "hyper" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" dependencies = [ "bytes", "futures-channel", "futures-util", "h2", "http 1.1.0", "http-body 1.0.0", "httparse", "itoa", "pin-project-lite", "smallvec", "tokio", "want", ] [[package]] name = "hyper-tls" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", "hyper 1.3.1", "hyper-util", "native-tls", "tokio", "tokio-native-tls", "tower-service", ] [[package]] name = "hyper-util" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.1.0", "http-body 1.0.0", "hyper 1.3.1", "pin-project-lite", "socket2", "tokio", "tower", "tower-service", "tracing", ] [[package]] name = "idna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "indexmap" version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "ipnet" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", "windows-sys 0.52.0", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "linux-raw-sys" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", "windows-sys 0.48.0", ] [[package]] name = "native-tls" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", "log", "openssl", "openssl-probe", "openssl-sys", "schannel", "security-framework", "security-framework-sys", "tempfile", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "object" version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "openssl" version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", "once_cell", "openssl-macros", "openssl-sys", ] [[package]] name = "openssl-macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "parking_lot" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets 0.52.5", ] [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] [[package]] name = "proc-macro2" version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "procfs" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" dependencies = [ "bitflags 2.5.0", "hex", "lazy_static", "procfs-core", "rustix", ] [[package]] name = "procfs-core" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ "bitflags 2.5.0", "hex", ] [[package]] name = "prometheus" version = "0.13.4" dependencies = [ "cfg-if", "criterion", "fnv", "getopts", "hyper 0.14.28", "lazy_static", "libc", "memchr", "parking_lot", "procfs", "protobuf", "protobuf-codegen-pure", "reqwest", "thiserror", "tokio", ] [[package]] name = "protobuf" version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "protobuf-codegen" version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "033460afb75cf755fcfc16dfaed20b86468082a2ea24e05ac35ab4a099a017d6" dependencies = [ "protobuf", ] [[package]] name = "protobuf-codegen-pure" version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a29399fc94bcd3eeaa951c715f7bea69409b2445356b00519740bcd6ddd865" dependencies = [ "protobuf", "protobuf-codegen", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "redox_syscall" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ "bitflags 2.5.0", ] [[package]] name = "regex" version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ "base64", "bytes", "encoding_rs", "futures-channel", "futures-core", "futures-util", "h2", "http 1.1.0", "http-body 1.0.0", "http-body-util", "hyper 1.3.1", "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", "native-tls", "once_cell", "percent-encoding", "pin-project-lite", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", "winreg", ] [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "rustls-pemfile" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ "base64", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" [[package]] name = "ryu" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "schannel" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "serde" version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_urlencoded" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", "itoa", "ryu", "serde", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "syn" version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sync_wrapper" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "system-configuration" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "tempfile" version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", "rustix", "windows-sys 0.52.0", ] [[package]] name = "thiserror" version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", "socket2", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tokio-native-tls" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", ] [[package]] name = "tokio-util" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", "tracing", ] [[package]] name = "tower" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", "pin-project", "pin-project-lite", "tokio", "tower-layer", "tower-service", "tracing", ] [[package]] name = "tower-layer" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "unicode-bidi" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "url" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "want" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ "try-lock", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi-util" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.5", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm 0.52.5", "windows_aarch64_msvc 0.52.5", "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", "windows_i686_msvc 0.52.5", "windows_x86_64_gnu 0.52.5", "windows_x86_64_gnullvm 0.52.5", "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winreg" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" dependencies = [ "cfg-if", "windows-sys 0.48.0", ] prometheus-0.13.4/Cargo.toml0000644000000047160000000000100113220ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "prometheus" version = "0.13.4" authors = [ "overvenus@gmail.com", "siddontang@gmail.com", "vistaswx@gmail.com", ] description = "Prometheus instrumentation library for Rust applications." homepage = "https://github.com/tikv/rust-prometheus" documentation = "https://docs.rs/prometheus" readme = "README.md" keywords = [ "prometheus", "metrics", ] license = "Apache-2.0" repository = "https://github.com/tikv/rust-prometheus" [package.metadata.docs.rs] features = ["nightly"] [[example]] name = "example_push" required-features = ["push"] [[example]] name = "example_process_collector" required-features = ["process"] [[bench]] name = "atomic" harness = false [[bench]] name = "counter" harness = false [[bench]] name = "desc" harness = false [[bench]] name = "gauge" harness = false [[bench]] name = "histogram" harness = false [[bench]] name = "text_encoder" harness = false [dependencies.cfg-if] version = "^1.0" [dependencies.fnv] version = "^1.0" [dependencies.lazy_static] version = "^1.4" [dependencies.libc] version = "^0.2" optional = true [dependencies.memchr] version = "^2.3" [dependencies.parking_lot] version = "^0.12" [dependencies.protobuf] version = "^2.0" optional = true [dependencies.reqwest] version = "^0.12" features = ["blocking"] optional = true [dependencies.thiserror] version = "^1.0" [dev-dependencies.criterion] version = "0.5" [dev-dependencies.getopts] version = "^0.2" [dev-dependencies.hyper] version = "^0.14" features = [ "server", "http1", "tcp", ] [dev-dependencies.tokio] version = "^1.0" features = [ "macros", "rt-multi-thread", ] [build-dependencies.protobuf-codegen-pure] version = "^2.0" optional = true [features] default = ["protobuf"] gen = ["protobuf-codegen-pure"] nightly = ["libc"] process = [ "libc", "procfs", ] push = [ "reqwest", "libc", "protobuf", ] [target."cfg(target_os = \"linux\")".dependencies.procfs] version = "^0.16" optional = true default-features = false prometheus-0.13.4/Cargo.toml.orig000064400000000000000000000034131046102023000147740ustar 00000000000000[package] authors = ["overvenus@gmail.com", "siddontang@gmail.com", "vistaswx@gmail.com"] description = "Prometheus instrumentation library for Rust applications." documentation = "https://docs.rs/prometheus" edition = "2018" homepage = "https://github.com/tikv/rust-prometheus" keywords = ["prometheus", "metrics"] license = "Apache-2.0" name = "prometheus" readme = "README.md" repository = "https://github.com/tikv/rust-prometheus" version = "0.13.4" [package.metadata.docs.rs] features = ["nightly"] [features] default = ["protobuf"] gen = ["protobuf-codegen-pure"] nightly = ["libc"] process = ["libc", "procfs"] push = ["reqwest", "libc", "protobuf"] [dependencies] cfg-if = "^1.0" fnv = "^1.0" lazy_static = "^1.4" libc = { version = "^0.2", optional = true } parking_lot = "^0.12" protobuf = { version = "^2.0", optional = true } memchr = "^2.3" reqwest = { version = "^0.12", features = ["blocking"], optional = true } thiserror = "^1.0" [target.'cfg(target_os = "linux")'.dependencies] procfs = { version = "^0.16", optional = true, default-features = false } [dev-dependencies] criterion = "0.5" getopts = "^0.2" hyper = { version = "^0.14", features = ["server", "http1", "tcp"] } tokio = { version = "^1.0", features = ["macros", "rt-multi-thread"] } [build-dependencies] protobuf-codegen-pure = { version = "^2.0", optional = true } [workspace] members = ["static-metric"] [[bench]] name = "atomic" harness = false [[bench]] name = "counter" harness = false [[bench]] name = "desc" harness = false [[bench]] name = "gauge" harness = false [[bench]] name = "histogram" harness = false [[bench]] name = "text_encoder" harness = false [[example]] name = "example_push" required-features = ["push"] [[example]] name = "example_process_collector" required-features = ["process"] prometheus-0.13.4/LICENSE000064400000000000000000000261271046102023000131210ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2019 TiKV Project Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. prometheus-0.13.4/Makefile000064400000000000000000000015701046102023000135470ustar 00000000000000.PHONY: all build test dev bench format clean examples gen_proto ENABLE_FEATURES ?= default all: format build test examples build: cargo build --features="${ENABLE_FEATURES}" test: cargo test --features="${ENABLE_FEATURES}" -- --nocapture dev: format test bench: format cargo bench --features=${ENABLE_FEATURES} -- --nocapture format: @cargo fmt --all -- --check >/dev/null || cargo fmt --all clean: cargo clean examples: cargo build --example example_embed cargo build --example example_hyper cargo build --features="push" --example example_push cargo build --features="process" --example example_process_collector gen_proto: @ which protoc >/dev/null || { echo "Please install protoc first"; exit 1; } @ which protoc-gen-rust >/dev/null || { echo "Please install protobuf rust plugin, cargo install protobuf"; exit 1; } protoc --rust_out proto proto/metrics.proto prometheus-0.13.4/README.md000064400000000000000000000032641046102023000133700ustar 00000000000000# Prometheus Rust client library [![Build Status](https://github.com/tikv/rust-prometheus/actions/workflows/rust.yml/badge.svg)](https://github.com/tikv/rust-prometheus/actions/workflows/rust.yml) [![docs.rs](https://docs.rs/prometheus/badge.svg)](https://docs.rs/prometheus) [![crates.io](https://img.shields.io/crates/v/prometheus.svg)](https://crates.io/crates/prometheus) This is the [Rust](https://www.rust-lang.org) client library for [Prometheus](http://prometheus.io). The main data structures and APIs are ported from [Go client](https://github.com/prometheus/client_golang). ## Documentation Find the latest documentation at . ## Advanced ### Crate features This crate provides several optional components which can be enabled via [Cargo `[features]`](https://doc.rust-lang.org/cargo/reference/features.html): - `gen`: To generate protobuf client with the latest protobuf version instead of using the pre-generated client. - `nightly`: Enable nightly only features. - `process`: Enable [process metrics](https://prometheus.io/docs/instrumenting/writing_clientlibs/#process-metrics) support. - `push`: Enable [push metrics](https://prometheus.io/docs/instrumenting/pushing/) support. ### Static Metric When using a `MetricVec` with label values known at compile time prometheus-static-metric reduces the overhead of retrieving the concrete `Metric` from a `MetricVec`. See [static-metric](./static-metric) directory for details. ## Thanks - [brian-brazil](https://github.com/brian-brazil) - [ccmtaylor](https://github.com/ccmtaylor) - [kamalmarhubi](https://github.com/kamalmarhubi) - [lucab](https://github.com/lucab) - [koushiro](https://github.com/koushiro) prometheus-0.13.4/benches/atomic.rs000064400000000000000000000020511046102023000153330ustar 00000000000000// Copyright 2018 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. use criterion::{criterion_group, criterion_main, Criterion}; use prometheus::core::*; fn bench_atomic_f64(c: &mut Criterion) { let val = AtomicF64::new(0.0); c.bench_function("atomic_f64", |b| { b.iter(|| { val.inc_by(12.0); }) }); } fn bench_atomic_i64(c: &mut Criterion) { let val = AtomicI64::new(0); c.bench_function("atomic_i64", |b| { b.iter(|| { val.inc_by(12); }) }); } criterion_group!(benches, bench_atomic_f64, bench_atomic_i64); criterion_main!(benches); prometheus-0.13.4/benches/counter.rs000064400000000000000000000146741046102023000155540ustar 00000000000000// Copyright 2016 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. use criterion::{criterion_group, criterion_main, Criterion}; use prometheus::{Counter, CounterVec, IntCounter, Opts}; use std::collections::HashMap; use std::sync::{atomic, Arc}; use std::thread; fn bench_counter_with_label_values(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_label_values", |b| { b.iter(|| counter.with_label_values(&["eins", "zwei", "drei"]).inc()) }); } fn bench_counter_with_mapped_labels(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", |b| { b.iter(|| { let mut labels = HashMap::with_capacity(3); labels.insert("two", "zwei"); labels.insert("one", "eins"); labels.insert("three", "drei"); counter.with(&labels).inc(); }) }); } fn bench_counter_with_prepared_mapped_labels(c: &mut Criterion) { let counter = CounterVec::new( Opts::new("benchmark_counter", "A counter to benchmark it."), &["one", "two", "three"], ) .unwrap(); let mut labels = HashMap::with_capacity(3); labels.insert("two", "zwei"); labels.insert("one", "eins"); labels.insert("three", "drei"); c.bench_function("counter_with_prepared_mapped_labels", |b| { b.iter(|| { counter.with(&labels).inc(); }) }); } fn bench_counter_no_labels(c: &mut Criterion) { let counter = Counter::new("benchmark_counter", "A counter to benchmark.").unwrap(); c.bench_function("counter_no_labels", |b| b.iter(|| counter.inc())); } fn bench_int_counter_no_labels(c: &mut Criterion) { let counter = IntCounter::new("benchmark_int_counter", "A int_counter to benchmark.").unwrap(); c.bench_function("int_counter_no_labels", |b| b.iter(|| counter.inc())); } fn bench_counter_no_labels_concurrent_nop(c: &mut Criterion) { let signal_exit = Arc::new(atomic::AtomicBool::new(false)); let counter = Counter::new("foo", "bar").unwrap(); let thread_handles: Vec<_> = (0..4) .map(|_| { let signal_exit2 = signal_exit.clone(); thread::spawn(move || { while !signal_exit2.load(atomic::Ordering::Relaxed) { // Do nothing as the control group. } }) }) .collect(); c.bench_function("counter_no_labels_concurrent_nop", |b| { b.iter(|| counter.inc()); }); // Wait for accompanying thread to exit. signal_exit.store(true, atomic::Ordering::Relaxed); for h in thread_handles { h.join().unwrap(); } } fn bench_counter_no_labels_concurrent_write(c: &mut Criterion) { let signal_exit = Arc::new(atomic::AtomicBool::new(false)); let counter = Counter::new("foo", "bar").unwrap(); let thread_handles: Vec<_> = (0..4) .map(|_| { let signal_exit2 = signal_exit.clone(); let counter2 = counter.clone(); thread::spawn(move || { while !signal_exit2.load(atomic::Ordering::Relaxed) { // Update counter concurrently as the normal group. counter2.inc(); } }) }) .collect(); c.bench_function("counter_no_labels_concurrent_write", |b| { b.iter(|| counter.inc()); }); // Wait for accompanying thread to exit. signal_exit.store(true, atomic::Ordering::Relaxed); for h in thread_handles { h.join().unwrap(); } } fn bench_int_counter_no_labels_concurrent_write(c: &mut Criterion) { let signal_exit = Arc::new(atomic::AtomicBool::new(false)); let counter = IntCounter::new("foo", "bar").unwrap(); let thread_handles: Vec<_> = (0..4) .map(|_| { let signal_exit2 = signal_exit.clone(); let counter2 = counter.clone(); thread::spawn(move || { while !signal_exit2.load(atomic::Ordering::Relaxed) { // Update counter concurrently as the normal group. counter2.inc(); } }) }) .collect(); c.bench_function("int_counter_no_labels_concurrent_write", |b| { b.iter(|| counter.inc()); }); // Wait for accompanying thread to exit. signal_exit.store(true, atomic::Ordering::Relaxed); for h in thread_handles { h.join().unwrap(); } } fn bench_counter_with_label_values_concurrent_write(c: &mut Criterion) { let signal_exit = Arc::new(atomic::AtomicBool::new(false)); let counter = CounterVec::new(Opts::new("foo", "bar"), &["one", "two", "three"]).unwrap(); let thread_handles: Vec<_> = (0..4) .map(|_| { let signal_exit2 = signal_exit.clone(); let counter2 = counter.clone(); thread::spawn(move || { while !signal_exit2.load(atomic::Ordering::Relaxed) { counter2.with_label_values(&["eins", "zwei", "drei"]).inc(); } }) }) .collect(); c.bench_function("counter_with_label_values_concurrent_write", |b| { b.iter(|| counter.with_label_values(&["eins", "zwei", "drei"]).inc()); }); // Wait for accompanying thread to exit. signal_exit.store(true, atomic::Ordering::Relaxed); for h in thread_handles { h.join().unwrap(); } } criterion_group!( benches, bench_counter_no_labels, bench_counter_no_labels_concurrent_nop, bench_counter_no_labels_concurrent_write, bench_counter_with_label_values, bench_counter_with_label_values_concurrent_write, bench_counter_with_mapped_labels, bench_counter_with_prepared_mapped_labels, bench_int_counter_no_labels, bench_int_counter_no_labels_concurrent_write, ); criterion_main!(benches); prometheus-0.13.4/benches/desc.rs000064400000000000000000000021001046102023000147700ustar 00000000000000// Copyright 2020 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. use criterion::{black_box, criterion_group, criterion_main, Criterion}; use prometheus::core::Desc; fn description_validation(c: &mut Criterion) { c.bench_function("description_validation", |b| { b.iter(|| { black_box(Desc::new( "api_http_requests_total".to_string(), "not empty help".to_string(), vec!["method".to_string(), "handler".to_string()], Default::default(), )) }); }); } criterion_group!(benches, description_validation); criterion_main!(benches); prometheus-0.13.4/benches/gauge.rs000064400000000000000000000030111046102023000151440ustar 00000000000000// Copyright 2016 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. use criterion::{criterion_group, criterion_main, Criterion}; use prometheus::{Gauge, GaugeVec, IntGauge, Opts}; fn bench_gauge_with_label_values(c: &mut Criterion) { let gauge = GaugeVec::new( Opts::new("benchmark_gauge", "A gauge to benchmark it."), &["one", "two", "three"], ) .unwrap(); c.bench_function("gauge_with_label_values", |b| { b.iter(|| gauge.with_label_values(&["eins", "zwei", "drei"]).inc()) }); } fn bench_gauge_no_labels(c: &mut Criterion) { let gauge = Gauge::new("benchmark_gauge", "A gauge to benchmark.").unwrap(); c.bench_function("gauge_no_labels", |b| b.iter(|| gauge.inc())); } fn bench_int_gauge_no_labels(c: &mut Criterion) { let gauge = IntGauge::new("benchmark_int_gauge", "A int_gauge to benchmark.").unwrap(); c.bench_function("int_gauge_no_labels", |b| b.iter(|| gauge.inc())); } criterion_group!( benches, bench_gauge_with_label_values, bench_gauge_no_labels, bench_int_gauge_no_labels, ); criterion_main!(benches); prometheus-0.13.4/benches/histogram.rs000064400000000000000000000104151046102023000160570ustar 00000000000000// Copyright 2016 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. use criterion::{criterion_group, criterion_main, Criterion}; use prometheus::{core::Collector, Histogram, HistogramOpts, HistogramVec}; use std::sync::{atomic, Arc}; use std::thread; fn bench_histogram_with_label_values(c: &mut Criterion) { let histogram = HistogramVec::new( HistogramOpts::new("benchmark_histogram", "A histogram to benchmark it."), &["one", "two", "three"], ) .unwrap(); c.bench_function("bench_histogram_with_label_values", |b| { b.iter(|| { histogram .with_label_values(&["eins", "zwei", "drei"]) .observe(3.1415) }) }); } fn bench_histogram_no_labels(c: &mut Criterion) { let histogram = Histogram::with_opts(HistogramOpts::new( "benchmark_histogram", "A histogram to benchmark it.", )) .unwrap(); c.bench_function("bench_histogram_no_labels", |b| { b.iter(|| histogram.observe(3.1415)) }); } fn bench_histogram_timer(c: &mut Criterion) { let histogram = Histogram::with_opts(HistogramOpts::new( "benchmark_histogram_timer", "A histogram to benchmark it.", )) .unwrap(); c.bench_function("bench_histogram_timer", |b| { b.iter(|| histogram.start_timer()) }); } fn bench_histogram_local(c: &mut Criterion) { let histogram = Histogram::with_opts(HistogramOpts::new( "benchmark_histogram_local", "A histogram to benchmark it.", )) .unwrap(); let local = histogram.local(); c.bench_function("bench_histogram_local", |b| { b.iter(|| local.observe(3.1415)); }); local.flush(); } fn bench_local_histogram_timer(c: &mut Criterion) { let histogram = Histogram::with_opts(HistogramOpts::new( "benchmark_histogram_local_timer", "A histogram to benchmark it.", )) .unwrap(); let local = histogram.local(); c.bench_function("bench_local_histogram_timer", |b| { b.iter(|| local.start_timer()); }); local.flush(); } fn concurrent_observe_and_collect(c: &mut Criterion) { let signal_exit = Arc::new(atomic::AtomicBool::new(false)); let opts = HistogramOpts::new("test_name", "test help").buckets(vec![1.0]); let histogram = Histogram::with_opts(opts).unwrap(); let mut handlers = vec![]; for _ in 0..4 { let histogram = histogram.clone(); let signal_exit = signal_exit.clone(); handlers.push(thread::spawn(move || { while !signal_exit.load(atomic::Ordering::Relaxed) { for _ in 0..1_000 { histogram.observe(1.0); } histogram.collect(); } })); } c.bench_function("concurrent_observe_and_collect", |b| { b.iter(|| histogram.observe(1.0)); }); signal_exit.store(true, atomic::Ordering::Relaxed); for handler in handlers { handler.join().unwrap(); } } criterion_group!( benches, bench_histogram_with_label_values, bench_histogram_no_labels, bench_histogram_timer, bench_histogram_local, bench_local_histogram_timer, concurrent_observe_and_collect, ); criterion_main!(benches); /* #[bench] #[cfg(feature = "nightly")] fn bench_histogram_coarse_timer(c: &mut Criterion) { let histogram = Histogram::with_opts(HistogramOpts::new( "benchmark_histogram_timer", "A histogram to benchmark it.", )) .unwrap(); b.iter(|| histogram.start_coarse_timer()) } #[bench] #[cfg(feature = "nightly")] fn bench_local_histogram_coarse_timer(c: &mut Criterion) { let histogram = Histogram::with_opts(HistogramOpts::new( "benchmark_histogram_timer", "A histogram to benchmark it.", )) .unwrap(); let local = histogram.local(); b.iter(|| local.start_coarse_timer()); local.flush(); } */ prometheus-0.13.4/benches/text_encoder.rs000064400000000000000000000051351046102023000165500ustar 00000000000000// Copyright 2020 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. use criterion::{criterion_group, criterion_main, Criterion}; use prometheus::{CounterVec, Encoder, HistogramOpts, HistogramVec, Opts, Registry, TextEncoder}; fn bench_text_encoder_without_escaping(c: &mut Criterion) { let registry = registry_with_test_metrics(false); run_text_encoder(c, "text_encoder_without_escaping", registry) } fn bench_text_encoder_with_escaping(c: &mut Criterion) { let registry = registry_with_test_metrics(true); run_text_encoder(c, "text_encoder_with_escaping", registry) } fn registry_with_test_metrics(with_escaping: bool) -> Registry { let registry = Registry::new(); for i in 0..100 { let counter = CounterVec::new( Opts::new( format!("benchmark_counter_{}", i), "A counter to benchmark it.", ), &["one", "two", "three"], ) .unwrap(); registry.register(Box::new(counter.clone())).unwrap(); let histogram = HistogramVec::new( HistogramOpts::new( format!("benchmark_histogram_{}", i), "A histogram to benchmark it.", ), &["one", "two", "three"], ) .unwrap(); registry.register(Box::new(histogram.clone())).unwrap(); for j in 0..100 { let j_string = j.to_string(); let label_values = if with_escaping { ["ei\\ns\n", "zw\"e\"i", &j_string] } else { ["eins", "zwei", &j_string] }; counter.with_label_values(&label_values).inc(); histogram.with_label_values(&label_values).observe(j.into()); } } registry } fn run_text_encoder(c: &mut Criterion, label: &str, registry: Registry) { let mut buffer = vec![]; let encoder = TextEncoder::new(); let metric_families = registry.gather(); c.bench_function(label, |b| { b.iter(|| encoder.encode(&metric_families, &mut buffer).unwrap()); }); } criterion_group!( benches, bench_text_encoder_without_escaping, bench_text_encoder_with_escaping, ); criterion_main!(benches); prometheus-0.13.4/build.rs000064400000000000000000000007221046102023000135520ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. #[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(); } #[cfg(not(feature = "gen"))] fn generate_protobuf_binding_file() {} fn main() { generate_protobuf_binding_file() } prometheus-0.13.4/docs/release.md000064400000000000000000000020431046102023000147750ustar 00000000000000# Release process ## Crate prometheus 1. Create pull request with bumped `version` in `Cargo.toml` and updated `CHANGELOG.md`. 2. Once merged clean your local environment. ```bash cargo clean git clean -fd ``` 3. Tag the release. ```bash tag="v$(sed -En 's/^version = \"(.*)\"$/\1/p' Cargo.toml)" git tag -s "${tag}" -m "${tag}" ``` 4. Publish the release. ```bash cargo publish ``` 5. Push the tag. ```bash git push origin $tag ``` ## Crate prometheus-static-metric 1. Create pull request with bumped `version` in `static-metric/Cargo.toml` and updated `static-metric/CHANGELOG.md`. 2. Once merged clean your local environment. ```bash cd static-metric cargo clean git clean -fd ``` 3. Tag the release. ```bash tag="$(sed -En 's/^name = \"(.*)\"$/\1/p' Cargo.toml | head -n 1)-v$(sed -En 's/^version = \"(.*)\"$/\1/p' Cargo.toml)" git tag -s "${tag}" -m "${tag}" ``` 4. Publish the release. ```bash cargo publish ``` 5. Push the tag. ```bash git push origin $tag ``` prometheus-0.13.4/examples/example_custom_registry.rs000064400000000000000000000040131046102023000212430ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. //! This examples shows how to use multiple and custom registries, //! and how to perform registration across function boundaries. 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(); } fn main() { // Register default metrics. default_metrics(prometheus::default_registry()); // Register custom metrics to a custom registry. let mut labels = HashMap::new(); labels.insert("mykey".to_string(), "myvalue".to_string()); let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); custom_metrics(&custom_registry); // Print metrics for the default registry. let mut buffer = Vec::::new(); let encoder = prometheus::TextEncoder::new(); encoder.encode(&prometheus::gather(), &mut buffer).unwrap(); println!("## Default registry"); println!("{}", String::from_utf8(buffer.clone()).unwrap()); // Print metrics for the custom registry. let mut buffer = Vec::::new(); let encoder = prometheus::TextEncoder::new(); encoder .encode(&custom_registry.gather(), &mut buffer) .unwrap(); println!("## Custom registry"); println!("{}", String::from_utf8(buffer.clone()).unwrap()); } /// Default metrics, to be collected by the default registry. fn default_metrics(registry: &Registry) { registry .register(Box::new(DEFAULT_COUNTER.clone())) .unwrap(); DEFAULT_COUNTER.inc(); assert_eq!(DEFAULT_COUNTER.get(), 1); } /// Custom metrics, to be collected by a dedicated registry. fn custom_metrics(registry: &Registry) { registry.register(Box::new(CUSTOM_COUNTER.clone())).unwrap(); CUSTOM_COUNTER.inc_by(42); assert_eq!(CUSTOM_COUNTER.get(), 42); } prometheus-0.13.4/examples/example_edition_2018.rs000064400000000000000000000017341046102023000201150ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use prometheus::register_counter; /// small example that uses rust 2018 style macro imports #[allow(unused)] fn main() { register_counter!("test_macro_3", "help"); prometheus::register_counter_vec!("errorz", "errors", &["error"]).unwrap(); prometheus::register_gauge!("test_macro_gauge_2", "help"); prometheus::register_gauge_vec!("test_macro_gauge_vec_3", "help", &["a", "b"]); prometheus::register_histogram!("test_macro_histogram_4", "help", vec![1.0, 2.0]); prometheus::register_histogram_vec!( "test_macro_histogram_4", "help", &["a", "b"], vec![1.0, 2.0] ); prometheus::register_int_counter!("meh", "foo"); prometheus::register_int_counter_vec!("errorz", "errors", &["error"]).unwrap(); prometheus::register_int_gauge!("test_macro_gauge_2", "help"); prometheus::register_int_gauge_vec!("test_macro_gauge_vec_4", "help", &["a", "b"]); } prometheus-0.13.4/examples/example_embed.rs000064400000000000000000000061741046102023000170670ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::thread; use std::time::Duration; use prometheus::{Counter, CounterVec, Encoder, Gauge, GaugeVec, Opts, Registry, TextEncoder}; fn main() { let r = Registry::new(); let counter_opts = Opts::new("test_counter", "test counter help") .const_label("a", "1") .const_label("b", "2"); let counter = Counter::with_opts(counter_opts).unwrap(); let counter_vec_opts = Opts::new("test_counter_vec", "test counter vector help") .const_label("a", "1") .const_label("b", "2"); let counter_vec = CounterVec::new(counter_vec_opts, &["c", "d"]).unwrap(); r.register(Box::new(counter.clone())).unwrap(); r.register(Box::new(counter_vec.clone())).unwrap(); let gauge_opts = Opts::new("test_gauge", "test gauge help") .const_label("a", "1") .const_label("b", "2"); let gauge = Gauge::with_opts(gauge_opts).unwrap(); let gauge_vec_opts = Opts::new("test_gauge_vec", "test gauge vector help") .const_label("a", "1") .const_label("b", "2"); let gauge_vec = GaugeVec::new(gauge_vec_opts, &["c", "d"]).unwrap(); r.register(Box::new(gauge.clone())).unwrap(); r.register(Box::new(gauge_vec.clone())).unwrap(); counter.inc(); assert_eq!(counter.get() as u64, 1); counter.inc_by(42.0); assert_eq!(counter.get() as u64, 43); counter_vec.with_label_values(&["3", "4"]).inc(); assert_eq!(counter_vec.with_label_values(&["3", "4"]).get() as u64, 1); counter_vec.with_label_values(&["3", "4"]).inc_by(42.0); assert_eq!(counter_vec.with_label_values(&["3", "4"]).get() as u64, 43); gauge.inc(); assert_eq!(gauge.get() as u64, 1); gauge.add(42.0); assert_eq!(gauge.get() as u64, 43); gauge_vec.with_label_values(&["3", "4"]).inc(); assert_eq!(gauge_vec.with_label_values(&["3", "4"]).get() as u64, 1); gauge_vec.with_label_values(&["3", "4"]).set(42.0); assert_eq!(gauge_vec.with_label_values(&["3", "4"]).get() as u64, 42); let c2 = counter.clone(); let cv2 = counter_vec.clone(); let g2 = gauge.clone(); let gv2 = gauge_vec.clone(); thread::spawn(move || { for _ in 0..10 { thread::sleep(Duration::from_millis(500)); c2.inc(); cv2.with_label_values(&["3", "4"]).inc(); g2.inc(); gv2.with_label_values(&["3", "4"]).inc(); } }); thread::spawn(move || { for _ in 0..5 { thread::sleep(Duration::from_secs(1)); counter.inc(); counter_vec.with_label_values(&["3", "4"]).inc(); gauge.dec(); gauge_vec.with_label_values(&["3", "4"]).set(42.0); } }); // Choose your writer and encoder. let mut buffer = Vec::::new(); let encoder = TextEncoder::new(); for _ in 0..5 { let metric_families = r.gather(); encoder.encode(&metric_families, &mut buffer).unwrap(); // Output to the standard output. println!("{}", String::from_utf8(buffer.clone()).unwrap()); buffer.clear(); thread::sleep(Duration::from_secs(1)); } } prometheus-0.13.4/examples/example_hyper.rs000064400000000000000000000037441046102023000171420ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use hyper::{ header::CONTENT_TYPE, service::{make_service_fn, service_fn}, Body, Request, Response, Server, }; use prometheus::{Counter, Encoder, Gauge, HistogramVec, TextEncoder}; use lazy_static::lazy_static; use prometheus::{labels, opts, register_counter, register_gauge, register_histogram_vec}; lazy_static! { static ref HTTP_COUNTER: Counter = register_counter!(opts!( "example_http_requests_total", "Number of HTTP requests made.", labels! {"handler" => "all",} )) .unwrap(); static ref HTTP_BODY_GAUGE: Gauge = register_gauge!(opts!( "example_http_response_size_bytes", "The HTTP response sizes in bytes.", labels! {"handler" => "all",} )) .unwrap(); static ref HTTP_REQ_HISTOGRAM: HistogramVec = register_histogram_vec!( "example_http_request_duration_seconds", "The HTTP request latencies in seconds.", &["handler"] ) .unwrap(); } async fn serve_req(_req: Request) -> Result, hyper::Error> { 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 response = Response::builder() .status(200) .header(CONTENT_TYPE, encoder.format_type()) .body(Body::from(buffer)) .unwrap(); timer.observe_duration(); Ok(response) } #[tokio::main] async fn main() { let addr = ([127, 0, 0, 1], 9898).into(); println!("Listening on http://{}", addr); let serve_future = Server::bind(&addr).serve(make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(serve_req)) })); if let Err(err) = serve_future.await { eprintln!("server error: {}", err); } } prometheus-0.13.4/examples/example_int_metrics.rs000064400000000000000000000030761046102023000203310ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. 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(); static ref A_INT_COUNTER_VEC: IntCounterVec = register_int_counter_vec!("A_int_counter_vec", "foobar", &["a", "b"]).unwrap(); static ref A_INT_GAUGE: IntGauge = register_int_gauge!("A_int_gauge", "foobar").unwrap(); static ref A_INT_GAUGE_VEC: IntGaugeVec = register_int_gauge_vec!("A_int_gauge_vec", "foobar", &["a", "b"]).unwrap(); } fn main() { A_INT_COUNTER.inc(); A_INT_COUNTER.inc_by(10); assert_eq!(A_INT_COUNTER.get(), 11); A_INT_COUNTER_VEC.with_label_values(&["a", "b"]).inc_by(5); assert_eq!(A_INT_COUNTER_VEC.with_label_values(&["a", "b"]).get(), 5); A_INT_COUNTER_VEC.with_label_values(&["c", "d"]).inc(); assert_eq!(A_INT_COUNTER_VEC.with_label_values(&["c", "d"]).get(), 1); A_INT_GAUGE.set(5); assert_eq!(A_INT_GAUGE.get(), 5); A_INT_GAUGE.dec(); assert_eq!(A_INT_GAUGE.get(), 4); A_INT_GAUGE.add(2); assert_eq!(A_INT_GAUGE.get(), 6); A_INT_GAUGE_VEC.with_label_values(&["a", "b"]).set(10); A_INT_GAUGE_VEC.with_label_values(&["a", "b"]).dec(); A_INT_GAUGE_VEC.with_label_values(&["a", "b"]).sub(2); assert_eq!(A_INT_GAUGE_VEC.with_label_values(&["a", "b"]).get(), 7); } prometheus-0.13.4/examples/example_process_collector.rs000064400000000000000000000011721046102023000215300ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. fn main() { use std::thread; use std::time::Duration; use prometheus::Encoder; // A default ProcessCollector is registered automatically. let mut buffer = Vec::new(); let encoder = prometheus::TextEncoder::new(); for _ in 0..5 { let metric_families = prometheus::gather(); encoder.encode(&metric_families, &mut buffer).unwrap(); // Output to the standard output. println!("{}", String::from_utf8(buffer.clone()).unwrap()); buffer.clear(); thread::sleep(Duration::from_secs(1)); } } prometheus-0.13.4/examples/example_push.rs000064400000000000000000000037061046102023000167700ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::env; use std::thread; 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", "Total number of prometheus client pushed." ) .unwrap(); static ref PUSH_REQ_HISTOGRAM: Histogram = register_histogram!( "example_push_request_duration_seconds", "The push request latencies in seconds." ) .unwrap(); } fn main() { let args: Vec = env::args().collect(); let program = args[0].clone(); let mut opts = Options::new(); opts.optopt( "A", "addr", "prometheus pushgateway address", "default is 127.0.0.1:9091", ); opts.optflag("h", "help", "print this help menu"); let matches = opts.parse(&args).unwrap(); if matches.opt_present("h") || !matches.opt_present("A") { let brief = format!("Usage: {} [options]", program); print!("{}", opts.usage(&brief)); return; } println!("Pushing, please start Pushgateway first."); let address = matches.opt_str("A").unwrap_or("127.0.0.1:9091".to_owned()); for _ in 0..5 { thread::sleep(time::Duration::from_secs(2)); PUSH_COUNTER.inc(); let metric_families = prometheus::gather(); let _timer = PUSH_REQ_HISTOGRAM.start_timer(); // drop as observe prometheus::push_metrics( "example_push", labels! {"instance".to_owned() => "HAL-9000".to_owned(),}, &address, metric_families, Some(prometheus::BasicAuthentication { username: "user".to_owned(), password: "pass".to_owned(), }), ) .unwrap(); } println!("Okay, please check the Pushgateway."); } prometheus-0.13.4/proto/proto_model.proto000064400000000000000000000040021046102023000166530ustar 00000000000000// Copyright 2013 Prometheus Team // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto2"; package io.prometheus.client; option java_package = "io.prometheus.client"; message LabelPair { optional string name = 1; optional string value = 2; } enum MetricType { COUNTER = 0; GAUGE = 1; SUMMARY = 2; UNTYPED = 3; HISTOGRAM = 4; } message Gauge { optional double value = 1; } message Counter { optional double value = 1; } message Quantile { optional double quantile = 1; optional double value = 2; } message Summary { optional uint64 sample_count = 1; optional double sample_sum = 2; repeated Quantile quantile = 3; } message Untyped { optional double value = 1; } message Histogram { optional uint64 sample_count = 1; optional double sample_sum = 2; repeated Bucket bucket = 3; // Ordered in increasing order of upper_bound, +Inf bucket is optional. } message Bucket { optional uint64 cumulative_count = 1; // Cumulative in increasing order. optional double upper_bound = 2; // Inclusive. } message Metric { repeated LabelPair label = 1; optional Gauge gauge = 2; optional Counter counter = 3; optional Summary summary = 4; optional Untyped untyped = 5; optional Histogram histogram = 7; optional int64 timestamp_ms = 6; } message MetricFamily { optional string name = 1; optional string help = 2; optional MetricType type = 3; repeated Metric metric = 4; } prometheus-0.13.4/proto/proto_model.rs000064400000000000000000002410211046102023000161400ustar 00000000000000// This file is generated by rust-protobuf 2.2.5. Do not edit // @generated // https://github.com/Manishearth/rust-clippy/issues/702 #![allow(unknown_lints)] #![allow(clippy)] #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(box_pointers)] #![allow(dead_code)] #![allow(missing_docs)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(trivial_casts)] #![allow(unsafe_code)] #![allow(unused_imports)] #![allow(unused_results)] use protobuf::Message as Message_imported_for_functions; use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions; #[derive(PartialEq,Clone,Default)] pub struct LabelPair { // message fields name: ::protobuf::SingularField<::std::string::String>, value: ::protobuf::SingularField<::std::string::String>, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, } impl LabelPair { pub fn new() -> LabelPair { ::std::default::Default::default() } // optional string name = 1; pub fn clear_name(&mut self) { self.name.clear(); } pub fn has_name(&self) -> bool { self.name.is_some() } // Param is passed by value, moved pub fn set_name(&mut self, v: ::std::string::String) { self.name = ::protobuf::SingularField::some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. pub fn mut_name(&mut self) -> &mut ::std::string::String { if self.name.is_none() { self.name.set_default(); } self.name.as_mut().unwrap() } // Take field pub fn take_name(&mut self) -> ::std::string::String { self.name.take().unwrap_or_else(|| ::std::string::String::new()) } pub fn get_name(&self) -> &str { match self.name.as_ref() { Some(v) => &v, None => "", } } // optional string value = 2; pub fn clear_value(&mut self) { self.value.clear(); } pub fn has_value(&self) -> bool { self.value.is_some() } // Param is passed by value, moved pub fn set_value(&mut self, v: ::std::string::String) { self.value = ::protobuf::SingularField::some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. pub fn mut_value(&mut self) -> &mut ::std::string::String { if self.value.is_none() { self.value.set_default(); } self.value.as_mut().unwrap() } // Take field pub fn take_value(&mut self) -> ::std::string::String { self.value.take().unwrap_or_else(|| ::std::string::String::new()) } pub fn get_value(&self) -> &str { match self.value.as_ref() { Some(v) => &v, None => "", } } } impl ::protobuf::Message for LabelPair { fn is_initialized(&self) -> bool { true } fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { while !is.eof()? { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { ::protobuf::rt::read_singular_string_into(wire_type, is, &mut self.name)?; }, 2 => { ::protobuf::rt::read_singular_string_into(wire_type, is, &mut self.value)?; }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, }; } ::std::result::Result::Ok(()) } // Compute sizes of nested messages #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; if let Some(ref v) = self.name.as_ref() { my_size += ::protobuf::rt::string_size(1, &v); } if let Some(ref v) = self.value.as_ref() { my_size += ::protobuf::rt::string_size(2, &v); } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { if let Some(ref v) = self.name.as_ref() { os.write_string(1, &v)?; } if let Some(ref v) = self.value.as_ref() { os.write_string(2, &v)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } fn get_cached_size(&self) -> u32 { self.cached_size.get() } fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { &self.unknown_fields } fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { &mut self.unknown_fields } fn as_any(&self) -> &::std::any::Any { self as &::std::any::Any } fn as_any_mut(&mut self) -> &mut ::std::any::Any { self as &mut ::std::any::Any } fn into_any(self: Box) -> ::std::boxed::Box<::std::any::Any> { self } fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { Self::descriptor_static() } fn new() -> LabelPair { LabelPair::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, }; unsafe { descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( "name", |m: &LabelPair| { &m.name }, |m: &mut LabelPair| { &mut m.name }, )); fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( "value", |m: &LabelPair| { &m.value }, |m: &mut LabelPair| { &mut m.value }, )); ::protobuf::reflect::MessageDescriptor::new::( "LabelPair", fields, file_descriptor_proto() ) }) } } fn default_instance() -> &'static LabelPair { static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const LabelPair, }; unsafe { instance.get(LabelPair::new) } } } impl ::protobuf::Clear for LabelPair { fn clear(&mut self) { self.clear_name(); self.clear_value(); self.unknown_fields.clear(); } } impl ::std::fmt::Debug for LabelPair { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } impl ::protobuf::reflect::ProtobufValue for LabelPair { fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef<'_> { ::protobuf::reflect::ProtobufValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] pub struct Gauge { // message fields value: ::std::option::Option, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, } impl Gauge { pub fn new() -> Gauge { ::std::default::Default::default() } // optional double value = 1; pub fn clear_value(&mut self) { self.value = ::std::option::Option::None; } pub fn has_value(&self) -> bool { self.value.is_some() } // Param is passed by value, moved pub fn set_value(&mut self, v: f64) { self.value = ::std::option::Option::Some(v); } pub fn get_value(&self) -> f64 { self.value.unwrap_or(0.) } } impl ::protobuf::Message for Gauge { fn is_initialized(&self) -> bool { true } fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { while !is.eof()? { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { if wire_type != ::protobuf::wire_format::WireTypeFixed64 { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_double()?; self.value = ::std::option::Option::Some(tmp); }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, }; } ::std::result::Result::Ok(()) } // Compute sizes of nested messages #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; if let Some(v) = self.value { my_size += 9; } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { if let Some(v) = self.value { os.write_double(1, v)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } fn get_cached_size(&self) -> u32 { self.cached_size.get() } fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { &self.unknown_fields } fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { &mut self.unknown_fields } fn as_any(&self) -> &::std::any::Any { self as &::std::any::Any } fn as_any_mut(&mut self) -> &mut ::std::any::Any { self as &mut ::std::any::Any } fn into_any(self: Box) -> ::std::boxed::Box<::std::any::Any> { self } fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { Self::descriptor_static() } fn new() -> Gauge { Gauge::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, }; unsafe { descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeDouble>( "value", |m: &Gauge| { &m.value }, |m: &mut Gauge| { &mut m.value }, )); ::protobuf::reflect::MessageDescriptor::new::( "Gauge", fields, file_descriptor_proto() ) }) } } fn default_instance() -> &'static Gauge { static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const Gauge, }; unsafe { instance.get(Gauge::new) } } } impl ::protobuf::Clear for Gauge { fn clear(&mut self) { self.clear_value(); self.unknown_fields.clear(); } } impl ::std::fmt::Debug for Gauge { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } impl ::protobuf::reflect::ProtobufValue for Gauge { fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef<'_> { ::protobuf::reflect::ProtobufValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] pub struct Counter { // message fields value: ::std::option::Option, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, } impl Counter { pub fn new() -> Counter { ::std::default::Default::default() } // optional double value = 1; pub fn clear_value(&mut self) { self.value = ::std::option::Option::None; } pub fn has_value(&self) -> bool { self.value.is_some() } // Param is passed by value, moved pub fn set_value(&mut self, v: f64) { self.value = ::std::option::Option::Some(v); } pub fn get_value(&self) -> f64 { self.value.unwrap_or(0.) } } impl ::protobuf::Message for Counter { fn is_initialized(&self) -> bool { true } fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { while !is.eof()? { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { if wire_type != ::protobuf::wire_format::WireTypeFixed64 { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_double()?; self.value = ::std::option::Option::Some(tmp); }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, }; } ::std::result::Result::Ok(()) } // Compute sizes of nested messages #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; if let Some(v) = self.value { my_size += 9; } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { if let Some(v) = self.value { os.write_double(1, v)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } fn get_cached_size(&self) -> u32 { self.cached_size.get() } fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { &self.unknown_fields } fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { &mut self.unknown_fields } fn as_any(&self) -> &::std::any::Any { self as &::std::any::Any } fn as_any_mut(&mut self) -> &mut ::std::any::Any { self as &mut ::std::any::Any } fn into_any(self: Box) -> ::std::boxed::Box<::std::any::Any> { self } fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { Self::descriptor_static() } fn new() -> Counter { Counter::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, }; unsafe { descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeDouble>( "value", |m: &Counter| { &m.value }, |m: &mut Counter| { &mut m.value }, )); ::protobuf::reflect::MessageDescriptor::new::( "Counter", fields, file_descriptor_proto() ) }) } } fn default_instance() -> &'static Counter { static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const Counter, }; unsafe { instance.get(Counter::new) } } } impl ::protobuf::Clear for Counter { fn clear(&mut self) { self.clear_value(); self.unknown_fields.clear(); } } impl ::std::fmt::Debug for Counter { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } impl ::protobuf::reflect::ProtobufValue for Counter { fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef<'_> { ::protobuf::reflect::ProtobufValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] pub struct Quantile { // message fields quantile: ::std::option::Option, value: ::std::option::Option, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, } impl Quantile { pub fn new() -> Quantile { ::std::default::Default::default() } // optional double quantile = 1; pub fn clear_quantile(&mut self) { self.quantile = ::std::option::Option::None; } pub fn has_quantile(&self) -> bool { self.quantile.is_some() } // Param is passed by value, moved pub fn set_quantile(&mut self, v: f64) { self.quantile = ::std::option::Option::Some(v); } pub fn get_quantile(&self) -> f64 { self.quantile.unwrap_or(0.) } // optional double value = 2; pub fn clear_value(&mut self) { self.value = ::std::option::Option::None; } pub fn has_value(&self) -> bool { self.value.is_some() } // Param is passed by value, moved pub fn set_value(&mut self, v: f64) { self.value = ::std::option::Option::Some(v); } pub fn get_value(&self) -> f64 { self.value.unwrap_or(0.) } } impl ::protobuf::Message for Quantile { fn is_initialized(&self) -> bool { true } fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { while !is.eof()? { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { if wire_type != ::protobuf::wire_format::WireTypeFixed64 { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_double()?; self.quantile = ::std::option::Option::Some(tmp); }, 2 => { if wire_type != ::protobuf::wire_format::WireTypeFixed64 { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_double()?; self.value = ::std::option::Option::Some(tmp); }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, }; } ::std::result::Result::Ok(()) } // Compute sizes of nested messages #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; if let Some(v) = self.quantile { my_size += 9; } if let Some(v) = self.value { my_size += 9; } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { if let Some(v) = self.quantile { os.write_double(1, v)?; } if let Some(v) = self.value { os.write_double(2, v)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } fn get_cached_size(&self) -> u32 { self.cached_size.get() } fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { &self.unknown_fields } fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { &mut self.unknown_fields } fn as_any(&self) -> &::std::any::Any { self as &::std::any::Any } fn as_any_mut(&mut self) -> &mut ::std::any::Any { self as &mut ::std::any::Any } fn into_any(self: Box) -> ::std::boxed::Box<::std::any::Any> { self } fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { Self::descriptor_static() } fn new() -> Quantile { Quantile::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, }; unsafe { descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeDouble>( "quantile", |m: &Quantile| { &m.quantile }, |m: &mut Quantile| { &mut m.quantile }, )); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeDouble>( "value", |m: &Quantile| { &m.value }, |m: &mut Quantile| { &mut m.value }, )); ::protobuf::reflect::MessageDescriptor::new::( "Quantile", fields, file_descriptor_proto() ) }) } } fn default_instance() -> &'static Quantile { static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const Quantile, }; unsafe { instance.get(Quantile::new) } } } impl ::protobuf::Clear for Quantile { fn clear(&mut self) { self.clear_quantile(); self.clear_value(); self.unknown_fields.clear(); } } impl ::std::fmt::Debug for Quantile { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } impl ::protobuf::reflect::ProtobufValue for Quantile { fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef<'_> { ::protobuf::reflect::ProtobufValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] pub struct Summary { // message fields sample_count: ::std::option::Option, sample_sum: ::std::option::Option, quantile: ::protobuf::RepeatedField, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, } impl Summary { pub fn new() -> Summary { ::std::default::Default::default() } // optional uint64 sample_count = 1; pub fn clear_sample_count(&mut self) { self.sample_count = ::std::option::Option::None; } pub fn has_sample_count(&self) -> bool { self.sample_count.is_some() } // Param is passed by value, moved pub fn set_sample_count(&mut self, v: u64) { self.sample_count = ::std::option::Option::Some(v); } pub fn get_sample_count(&self) -> u64 { self.sample_count.unwrap_or(0) } // optional double sample_sum = 2; pub fn clear_sample_sum(&mut self) { self.sample_sum = ::std::option::Option::None; } pub fn has_sample_sum(&self) -> bool { self.sample_sum.is_some() } // Param is passed by value, moved pub fn set_sample_sum(&mut self, v: f64) { self.sample_sum = ::std::option::Option::Some(v); } pub fn get_sample_sum(&self) -> f64 { self.sample_sum.unwrap_or(0.) } // repeated .io.prometheus.client.Quantile quantile = 3; pub fn clear_quantile(&mut self) { self.quantile.clear(); } // Param is passed by value, moved pub fn set_quantile(&mut self, v: ::protobuf::RepeatedField) { self.quantile = v; } // Mutable pointer to the field. pub fn mut_quantile(&mut self) -> &mut ::protobuf::RepeatedField { &mut self.quantile } // Take field pub fn take_quantile(&mut self) -> ::protobuf::RepeatedField { ::std::mem::replace(&mut self.quantile, ::protobuf::RepeatedField::new()) } pub fn get_quantile(&self) -> &[Quantile] { &self.quantile } } impl ::protobuf::Message for Summary { fn is_initialized(&self) -> bool { for v in &self.quantile { if !v.is_initialized() { return false; } }; true } fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { while !is.eof()? { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { if wire_type != ::protobuf::wire_format::WireTypeVarint { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_uint64()?; self.sample_count = ::std::option::Option::Some(tmp); }, 2 => { if wire_type != ::protobuf::wire_format::WireTypeFixed64 { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_double()?; self.sample_sum = ::std::option::Option::Some(tmp); }, 3 => { ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.quantile)?; }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, }; } ::std::result::Result::Ok(()) } // Compute sizes of nested messages #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; if let Some(v) = self.sample_count { my_size += ::protobuf::rt::value_size(1, v, ::protobuf::wire_format::WireTypeVarint); } if let Some(v) = self.sample_sum { my_size += 9; } for value in &self.quantile { let len = value.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; }; my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { if let Some(v) = self.sample_count { os.write_uint64(1, v)?; } if let Some(v) = self.sample_sum { os.write_double(2, v)?; } for v in &self.quantile { os.write_tag(3, ::protobuf::wire_format::WireTypeLengthDelimited)?; os.write_raw_varint32(v.get_cached_size())?; v.write_to_with_cached_sizes(os)?; }; os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } fn get_cached_size(&self) -> u32 { self.cached_size.get() } fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { &self.unknown_fields } fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { &mut self.unknown_fields } fn as_any(&self) -> &::std::any::Any { self as &::std::any::Any } fn as_any_mut(&mut self) -> &mut ::std::any::Any { self as &mut ::std::any::Any } fn into_any(self: Box) -> ::std::boxed::Box<::std::any::Any> { self } fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { Self::descriptor_static() } fn new() -> Summary { Summary::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, }; unsafe { descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeUint64>( "sample_count", |m: &Summary| { &m.sample_count }, |m: &mut Summary| { &mut m.sample_count }, )); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeDouble>( "sample_sum", |m: &Summary| { &m.sample_sum }, |m: &mut Summary| { &mut m.sample_sum }, )); fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "quantile", |m: &Summary| { &m.quantile }, |m: &mut Summary| { &mut m.quantile }, )); ::protobuf::reflect::MessageDescriptor::new::( "Summary", fields, file_descriptor_proto() ) }) } } fn default_instance() -> &'static Summary { static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const Summary, }; unsafe { instance.get(Summary::new) } } } impl ::protobuf::Clear for Summary { fn clear(&mut self) { self.clear_sample_count(); self.clear_sample_sum(); self.clear_quantile(); self.unknown_fields.clear(); } } impl ::std::fmt::Debug for Summary { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } impl ::protobuf::reflect::ProtobufValue for Summary { fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef<'_> { ::protobuf::reflect::ProtobufValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] pub struct Untyped { // message fields value: ::std::option::Option, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, } impl Untyped { pub fn new() -> Untyped { ::std::default::Default::default() } // optional double value = 1; pub fn clear_value(&mut self) { self.value = ::std::option::Option::None; } pub fn has_value(&self) -> bool { self.value.is_some() } // Param is passed by value, moved pub fn set_value(&mut self, v: f64) { self.value = ::std::option::Option::Some(v); } pub fn get_value(&self) -> f64 { self.value.unwrap_or(0.) } } impl ::protobuf::Message for Untyped { fn is_initialized(&self) -> bool { true } fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { while !is.eof()? { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { if wire_type != ::protobuf::wire_format::WireTypeFixed64 { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_double()?; self.value = ::std::option::Option::Some(tmp); }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, }; } ::std::result::Result::Ok(()) } // Compute sizes of nested messages #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; if let Some(v) = self.value { my_size += 9; } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { if let Some(v) = self.value { os.write_double(1, v)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } fn get_cached_size(&self) -> u32 { self.cached_size.get() } fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { &self.unknown_fields } fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { &mut self.unknown_fields } fn as_any(&self) -> &::std::any::Any { self as &::std::any::Any } fn as_any_mut(&mut self) -> &mut ::std::any::Any { self as &mut ::std::any::Any } fn into_any(self: Box) -> ::std::boxed::Box<::std::any::Any> { self } fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { Self::descriptor_static() } fn new() -> Untyped { Untyped::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, }; unsafe { descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeDouble>( "value", |m: &Untyped| { &m.value }, |m: &mut Untyped| { &mut m.value }, )); ::protobuf::reflect::MessageDescriptor::new::( "Untyped", fields, file_descriptor_proto() ) }) } } fn default_instance() -> &'static Untyped { static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const Untyped, }; unsafe { instance.get(Untyped::new) } } } impl ::protobuf::Clear for Untyped { fn clear(&mut self) { self.clear_value(); self.unknown_fields.clear(); } } impl ::std::fmt::Debug for Untyped { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } impl ::protobuf::reflect::ProtobufValue for Untyped { fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef<'_> { ::protobuf::reflect::ProtobufValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] pub struct Histogram { // message fields sample_count: ::std::option::Option, sample_sum: ::std::option::Option, bucket: ::protobuf::RepeatedField, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, } impl Histogram { pub fn new() -> Histogram { ::std::default::Default::default() } // optional uint64 sample_count = 1; pub fn clear_sample_count(&mut self) { self.sample_count = ::std::option::Option::None; } pub fn has_sample_count(&self) -> bool { self.sample_count.is_some() } // Param is passed by value, moved pub fn set_sample_count(&mut self, v: u64) { self.sample_count = ::std::option::Option::Some(v); } pub fn get_sample_count(&self) -> u64 { self.sample_count.unwrap_or(0) } // optional double sample_sum = 2; pub fn clear_sample_sum(&mut self) { self.sample_sum = ::std::option::Option::None; } pub fn has_sample_sum(&self) -> bool { self.sample_sum.is_some() } // Param is passed by value, moved pub fn set_sample_sum(&mut self, v: f64) { self.sample_sum = ::std::option::Option::Some(v); } pub fn get_sample_sum(&self) -> f64 { self.sample_sum.unwrap_or(0.) } // repeated .io.prometheus.client.Bucket bucket = 3; pub fn clear_bucket(&mut self) { self.bucket.clear(); } // Param is passed by value, moved pub fn set_bucket(&mut self, v: ::protobuf::RepeatedField) { self.bucket = v; } // Mutable pointer to the field. pub fn mut_bucket(&mut self) -> &mut ::protobuf::RepeatedField { &mut self.bucket } // Take field pub fn take_bucket(&mut self) -> ::protobuf::RepeatedField { ::std::mem::replace(&mut self.bucket, ::protobuf::RepeatedField::new()) } pub fn get_bucket(&self) -> &[Bucket] { &self.bucket } } impl ::protobuf::Message for Histogram { fn is_initialized(&self) -> bool { for v in &self.bucket { if !v.is_initialized() { return false; } }; true } fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { while !is.eof()? { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { if wire_type != ::protobuf::wire_format::WireTypeVarint { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_uint64()?; self.sample_count = ::std::option::Option::Some(tmp); }, 2 => { if wire_type != ::protobuf::wire_format::WireTypeFixed64 { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_double()?; self.sample_sum = ::std::option::Option::Some(tmp); }, 3 => { ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.bucket)?; }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, }; } ::std::result::Result::Ok(()) } // Compute sizes of nested messages #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; if let Some(v) = self.sample_count { my_size += ::protobuf::rt::value_size(1, v, ::protobuf::wire_format::WireTypeVarint); } if let Some(v) = self.sample_sum { my_size += 9; } for value in &self.bucket { let len = value.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; }; my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { if let Some(v) = self.sample_count { os.write_uint64(1, v)?; } if let Some(v) = self.sample_sum { os.write_double(2, v)?; } for v in &self.bucket { os.write_tag(3, ::protobuf::wire_format::WireTypeLengthDelimited)?; os.write_raw_varint32(v.get_cached_size())?; v.write_to_with_cached_sizes(os)?; }; os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } fn get_cached_size(&self) -> u32 { self.cached_size.get() } fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { &self.unknown_fields } fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { &mut self.unknown_fields } fn as_any(&self) -> &::std::any::Any { self as &::std::any::Any } fn as_any_mut(&mut self) -> &mut ::std::any::Any { self as &mut ::std::any::Any } fn into_any(self: Box) -> ::std::boxed::Box<::std::any::Any> { self } fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { Self::descriptor_static() } fn new() -> Histogram { Histogram::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, }; unsafe { descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeUint64>( "sample_count", |m: &Histogram| { &m.sample_count }, |m: &mut Histogram| { &mut m.sample_count }, )); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeDouble>( "sample_sum", |m: &Histogram| { &m.sample_sum }, |m: &mut Histogram| { &mut m.sample_sum }, )); fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "bucket", |m: &Histogram| { &m.bucket }, |m: &mut Histogram| { &mut m.bucket }, )); ::protobuf::reflect::MessageDescriptor::new::( "Histogram", fields, file_descriptor_proto() ) }) } } fn default_instance() -> &'static Histogram { static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const Histogram, }; unsafe { instance.get(Histogram::new) } } } impl ::protobuf::Clear for Histogram { fn clear(&mut self) { self.clear_sample_count(); self.clear_sample_sum(); self.clear_bucket(); self.unknown_fields.clear(); } } impl ::std::fmt::Debug for Histogram { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } impl ::protobuf::reflect::ProtobufValue for Histogram { fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef<'_> { ::protobuf::reflect::ProtobufValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] pub struct Bucket { // message fields cumulative_count: ::std::option::Option, upper_bound: ::std::option::Option, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, } impl Bucket { pub fn new() -> Bucket { ::std::default::Default::default() } // optional uint64 cumulative_count = 1; pub fn clear_cumulative_count(&mut self) { self.cumulative_count = ::std::option::Option::None; } pub fn has_cumulative_count(&self) -> bool { self.cumulative_count.is_some() } // Param is passed by value, moved pub fn set_cumulative_count(&mut self, v: u64) { self.cumulative_count = ::std::option::Option::Some(v); } pub fn get_cumulative_count(&self) -> u64 { self.cumulative_count.unwrap_or(0) } // optional double upper_bound = 2; pub fn clear_upper_bound(&mut self) { self.upper_bound = ::std::option::Option::None; } pub fn has_upper_bound(&self) -> bool { self.upper_bound.is_some() } // Param is passed by value, moved pub fn set_upper_bound(&mut self, v: f64) { self.upper_bound = ::std::option::Option::Some(v); } pub fn get_upper_bound(&self) -> f64 { self.upper_bound.unwrap_or(0.) } } impl ::protobuf::Message for Bucket { fn is_initialized(&self) -> bool { true } fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { while !is.eof()? { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { if wire_type != ::protobuf::wire_format::WireTypeVarint { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_uint64()?; self.cumulative_count = ::std::option::Option::Some(tmp); }, 2 => { if wire_type != ::protobuf::wire_format::WireTypeFixed64 { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_double()?; self.upper_bound = ::std::option::Option::Some(tmp); }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, }; } ::std::result::Result::Ok(()) } // Compute sizes of nested messages #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; if let Some(v) = self.cumulative_count { my_size += ::protobuf::rt::value_size(1, v, ::protobuf::wire_format::WireTypeVarint); } if let Some(v) = self.upper_bound { my_size += 9; } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { if let Some(v) = self.cumulative_count { os.write_uint64(1, v)?; } if let Some(v) = self.upper_bound { os.write_double(2, v)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } fn get_cached_size(&self) -> u32 { self.cached_size.get() } fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { &self.unknown_fields } fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { &mut self.unknown_fields } fn as_any(&self) -> &::std::any::Any { self as &::std::any::Any } fn as_any_mut(&mut self) -> &mut ::std::any::Any { self as &mut ::std::any::Any } fn into_any(self: Box) -> ::std::boxed::Box<::std::any::Any> { self } fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { Self::descriptor_static() } fn new() -> Bucket { Bucket::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, }; unsafe { descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeUint64>( "cumulative_count", |m: &Bucket| { &m.cumulative_count }, |m: &mut Bucket| { &mut m.cumulative_count }, )); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeDouble>( "upper_bound", |m: &Bucket| { &m.upper_bound }, |m: &mut Bucket| { &mut m.upper_bound }, )); ::protobuf::reflect::MessageDescriptor::new::( "Bucket", fields, file_descriptor_proto() ) }) } } fn default_instance() -> &'static Bucket { static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const Bucket, }; unsafe { instance.get(Bucket::new) } } } impl ::protobuf::Clear for Bucket { fn clear(&mut self) { self.clear_cumulative_count(); self.clear_upper_bound(); self.unknown_fields.clear(); } } impl ::std::fmt::Debug for Bucket { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } impl ::protobuf::reflect::ProtobufValue for Bucket { fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef<'_> { ::protobuf::reflect::ProtobufValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] pub struct Metric { // message fields label: ::protobuf::RepeatedField, gauge: ::protobuf::SingularPtrField, counter: ::protobuf::SingularPtrField, summary: ::protobuf::SingularPtrField, untyped: ::protobuf::SingularPtrField, histogram: ::protobuf::SingularPtrField, timestamp_ms: ::std::option::Option, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, } impl Metric { pub fn new() -> Metric { ::std::default::Default::default() } // repeated .io.prometheus.client.LabelPair label = 1; pub fn clear_label(&mut self) { self.label.clear(); } // Param is passed by value, moved pub fn set_label(&mut self, v: ::protobuf::RepeatedField) { self.label = v; } // Mutable pointer to the field. pub fn mut_label(&mut self) -> &mut ::protobuf::RepeatedField { &mut self.label } // Take field pub fn take_label(&mut self) -> ::protobuf::RepeatedField { ::std::mem::replace(&mut self.label, ::protobuf::RepeatedField::new()) } pub fn get_label(&self) -> &[LabelPair] { &self.label } // optional .io.prometheus.client.Gauge gauge = 2; pub fn clear_gauge(&mut self) { self.gauge.clear(); } pub fn has_gauge(&self) -> bool { self.gauge.is_some() } // Param is passed by value, moved pub fn set_gauge(&mut self, v: Gauge) { self.gauge = ::protobuf::SingularPtrField::some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. pub fn mut_gauge(&mut self) -> &mut Gauge { if self.gauge.is_none() { self.gauge.set_default(); } self.gauge.as_mut().unwrap() } // Take field pub fn take_gauge(&mut self) -> Gauge { self.gauge.take().unwrap_or_else(|| Gauge::new()) } pub fn get_gauge(&self) -> &Gauge { self.gauge.as_ref().unwrap_or_else(|| Gauge::default_instance()) } // optional .io.prometheus.client.Counter counter = 3; pub fn clear_counter(&mut self) { self.counter.clear(); } pub fn has_counter(&self) -> bool { self.counter.is_some() } // Param is passed by value, moved pub fn set_counter(&mut self, v: Counter) { self.counter = ::protobuf::SingularPtrField::some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. pub fn mut_counter(&mut self) -> &mut Counter { if self.counter.is_none() { self.counter.set_default(); } self.counter.as_mut().unwrap() } // Take field pub fn take_counter(&mut self) -> Counter { self.counter.take().unwrap_or_else(|| Counter::new()) } pub fn get_counter(&self) -> &Counter { self.counter.as_ref().unwrap_or_else(|| Counter::default_instance()) } // optional .io.prometheus.client.Summary summary = 4; pub fn clear_summary(&mut self) { self.summary.clear(); } pub fn has_summary(&self) -> bool { self.summary.is_some() } // Param is passed by value, moved pub fn set_summary(&mut self, v: Summary) { self.summary = ::protobuf::SingularPtrField::some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. pub fn mut_summary(&mut self) -> &mut Summary { if self.summary.is_none() { self.summary.set_default(); } self.summary.as_mut().unwrap() } // Take field pub fn take_summary(&mut self) -> Summary { self.summary.take().unwrap_or_else(|| Summary::new()) } pub fn get_summary(&self) -> &Summary { self.summary.as_ref().unwrap_or_else(|| Summary::default_instance()) } // optional .io.prometheus.client.Untyped untyped = 5; pub fn clear_untyped(&mut self) { self.untyped.clear(); } pub fn has_untyped(&self) -> bool { self.untyped.is_some() } // Param is passed by value, moved pub fn set_untyped(&mut self, v: Untyped) { self.untyped = ::protobuf::SingularPtrField::some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. pub fn mut_untyped(&mut self) -> &mut Untyped { if self.untyped.is_none() { self.untyped.set_default(); } self.untyped.as_mut().unwrap() } // Take field pub fn take_untyped(&mut self) -> Untyped { self.untyped.take().unwrap_or_else(|| Untyped::new()) } pub fn get_untyped(&self) -> &Untyped { self.untyped.as_ref().unwrap_or_else(|| Untyped::default_instance()) } // optional .io.prometheus.client.Histogram histogram = 7; pub fn clear_histogram(&mut self) { self.histogram.clear(); } pub fn has_histogram(&self) -> bool { self.histogram.is_some() } // Param is passed by value, moved pub fn set_histogram(&mut self, v: Histogram) { self.histogram = ::protobuf::SingularPtrField::some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. pub fn mut_histogram(&mut self) -> &mut Histogram { if self.histogram.is_none() { self.histogram.set_default(); } self.histogram.as_mut().unwrap() } // Take field pub fn take_histogram(&mut self) -> Histogram { self.histogram.take().unwrap_or_else(|| Histogram::new()) } pub fn get_histogram(&self) -> &Histogram { self.histogram.as_ref().unwrap_or_else(|| Histogram::default_instance()) } // optional int64 timestamp_ms = 6; pub fn clear_timestamp_ms(&mut self) { self.timestamp_ms = ::std::option::Option::None; } pub fn has_timestamp_ms(&self) -> bool { self.timestamp_ms.is_some() } // Param is passed by value, moved pub fn set_timestamp_ms(&mut self, v: i64) { self.timestamp_ms = ::std::option::Option::Some(v); } pub fn get_timestamp_ms(&self) -> i64 { self.timestamp_ms.unwrap_or(0) } } impl ::protobuf::Message for Metric { fn is_initialized(&self) -> bool { for v in &self.label { if !v.is_initialized() { return false; } }; for v in &self.gauge { if !v.is_initialized() { return false; } }; for v in &self.counter { if !v.is_initialized() { return false; } }; for v in &self.summary { if !v.is_initialized() { return false; } }; for v in &self.untyped { if !v.is_initialized() { return false; } }; for v in &self.histogram { if !v.is_initialized() { return false; } }; true } fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { while !is.eof()? { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.label)?; }, 2 => { ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.gauge)?; }, 3 => { ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.counter)?; }, 4 => { ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.summary)?; }, 5 => { ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.untyped)?; }, 7 => { ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.histogram)?; }, 6 => { if wire_type != ::protobuf::wire_format::WireTypeVarint { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } let tmp = is.read_int64()?; self.timestamp_ms = ::std::option::Option::Some(tmp); }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, }; } ::std::result::Result::Ok(()) } // Compute sizes of nested messages #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; for value in &self.label { let len = value.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; }; if let Some(ref v) = self.gauge.as_ref() { let len = v.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; } if let Some(ref v) = self.counter.as_ref() { let len = v.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; } if let Some(ref v) = self.summary.as_ref() { let len = v.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; } if let Some(ref v) = self.untyped.as_ref() { let len = v.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; } if let Some(ref v) = self.histogram.as_ref() { let len = v.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; } if let Some(v) = self.timestamp_ms { my_size += ::protobuf::rt::value_size(6, v, ::protobuf::wire_format::WireTypeVarint); } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { for v in &self.label { os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?; os.write_raw_varint32(v.get_cached_size())?; v.write_to_with_cached_sizes(os)?; }; if let Some(ref v) = self.gauge.as_ref() { os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?; os.write_raw_varint32(v.get_cached_size())?; v.write_to_with_cached_sizes(os)?; } if let Some(ref v) = self.counter.as_ref() { os.write_tag(3, ::protobuf::wire_format::WireTypeLengthDelimited)?; os.write_raw_varint32(v.get_cached_size())?; v.write_to_with_cached_sizes(os)?; } if let Some(ref v) = self.summary.as_ref() { os.write_tag(4, ::protobuf::wire_format::WireTypeLengthDelimited)?; os.write_raw_varint32(v.get_cached_size())?; v.write_to_with_cached_sizes(os)?; } if let Some(ref v) = self.untyped.as_ref() { os.write_tag(5, ::protobuf::wire_format::WireTypeLengthDelimited)?; os.write_raw_varint32(v.get_cached_size())?; v.write_to_with_cached_sizes(os)?; } if let Some(ref v) = self.histogram.as_ref() { os.write_tag(7, ::protobuf::wire_format::WireTypeLengthDelimited)?; os.write_raw_varint32(v.get_cached_size())?; v.write_to_with_cached_sizes(os)?; } if let Some(v) = self.timestamp_ms { os.write_int64(6, v)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } fn get_cached_size(&self) -> u32 { self.cached_size.get() } fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { &self.unknown_fields } fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { &mut self.unknown_fields } fn as_any(&self) -> &::std::any::Any { self as &::std::any::Any } fn as_any_mut(&mut self) -> &mut ::std::any::Any { self as &mut ::std::any::Any } fn into_any(self: Box) -> ::std::boxed::Box<::std::any::Any> { self } fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { Self::descriptor_static() } fn new() -> Metric { Metric::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, }; unsafe { descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "label", |m: &Metric| { &m.label }, |m: &mut Metric| { &mut m.label }, )); fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "gauge", |m: &Metric| { &m.gauge }, |m: &mut Metric| { &mut m.gauge }, )); fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "counter", |m: &Metric| { &m.counter }, |m: &mut Metric| { &mut m.counter }, )); fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "summary", |m: &Metric| { &m.summary }, |m: &mut Metric| { &mut m.summary }, )); fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "untyped", |m: &Metric| { &m.untyped }, |m: &mut Metric| { &mut m.untyped }, )); fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "histogram", |m: &Metric| { &m.histogram }, |m: &mut Metric| { &mut m.histogram }, )); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeInt64>( "timestamp_ms", |m: &Metric| { &m.timestamp_ms }, |m: &mut Metric| { &mut m.timestamp_ms }, )); ::protobuf::reflect::MessageDescriptor::new::( "Metric", fields, file_descriptor_proto() ) }) } } fn default_instance() -> &'static Metric { static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const Metric, }; unsafe { instance.get(Metric::new) } } } impl ::protobuf::Clear for Metric { fn clear(&mut self) { self.clear_label(); self.clear_gauge(); self.clear_counter(); self.clear_summary(); self.clear_untyped(); self.clear_histogram(); self.clear_timestamp_ms(); self.unknown_fields.clear(); } } impl ::std::fmt::Debug for Metric { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } impl ::protobuf::reflect::ProtobufValue for Metric { fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef<'_> { ::protobuf::reflect::ProtobufValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] pub struct MetricFamily { // message fields name: ::protobuf::SingularField<::std::string::String>, help: ::protobuf::SingularField<::std::string::String>, field_type: ::std::option::Option, metric: ::protobuf::RepeatedField, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, } impl MetricFamily { pub fn new() -> MetricFamily { ::std::default::Default::default() } // optional string name = 1; pub fn clear_name(&mut self) { self.name.clear(); } pub fn has_name(&self) -> bool { self.name.is_some() } // Param is passed by value, moved pub fn set_name(&mut self, v: ::std::string::String) { self.name = ::protobuf::SingularField::some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. pub fn mut_name(&mut self) -> &mut ::std::string::String { if self.name.is_none() { self.name.set_default(); } self.name.as_mut().unwrap() } // Take field pub fn take_name(&mut self) -> ::std::string::String { self.name.take().unwrap_or_else(|| ::std::string::String::new()) } pub fn get_name(&self) -> &str { match self.name.as_ref() { Some(v) => &v, None => "", } } // optional string help = 2; pub fn clear_help(&mut self) { self.help.clear(); } pub fn has_help(&self) -> bool { self.help.is_some() } // Param is passed by value, moved pub fn set_help(&mut self, v: ::std::string::String) { self.help = ::protobuf::SingularField::some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. pub fn mut_help(&mut self) -> &mut ::std::string::String { if self.help.is_none() { self.help.set_default(); } self.help.as_mut().unwrap() } // Take field pub fn take_help(&mut self) -> ::std::string::String { self.help.take().unwrap_or_else(|| ::std::string::String::new()) } pub fn get_help(&self) -> &str { match self.help.as_ref() { Some(v) => &v, None => "", } } // optional .io.prometheus.client.MetricType type = 3; pub fn clear_field_type(&mut self) { self.field_type = ::std::option::Option::None; } pub fn has_field_type(&self) -> bool { self.field_type.is_some() } // Param is passed by value, moved pub fn set_field_type(&mut self, v: MetricType) { self.field_type = ::std::option::Option::Some(v); } pub fn get_field_type(&self) -> MetricType { self.field_type.unwrap_or(MetricType::COUNTER) } // repeated .io.prometheus.client.Metric metric = 4; pub fn clear_metric(&mut self) { self.metric.clear(); } // Param is passed by value, moved pub fn set_metric(&mut self, v: ::protobuf::RepeatedField) { self.metric = v; } // Mutable pointer to the field. pub fn mut_metric(&mut self) -> &mut ::protobuf::RepeatedField { &mut self.metric } // Take field pub fn take_metric(&mut self) -> ::protobuf::RepeatedField { ::std::mem::replace(&mut self.metric, ::protobuf::RepeatedField::new()) } pub fn get_metric(&self) -> &[Metric] { &self.metric } } impl ::protobuf::Message for MetricFamily { fn is_initialized(&self) -> bool { for v in &self.metric { if !v.is_initialized() { return false; } }; true } fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { while !is.eof()? { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { ::protobuf::rt::read_singular_string_into(wire_type, is, &mut self.name)?; }, 2 => { ::protobuf::rt::read_singular_string_into(wire_type, is, &mut self.help)?; }, 3 => { ::protobuf::rt::read_proto2_enum_with_unknown_fields_into(wire_type, is, &mut self.field_type, 3, &mut self.unknown_fields)? }, 4 => { ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.metric)?; }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, }; } ::std::result::Result::Ok(()) } // Compute sizes of nested messages #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; if let Some(ref v) = self.name.as_ref() { my_size += ::protobuf::rt::string_size(1, &v); } if let Some(ref v) = self.help.as_ref() { my_size += ::protobuf::rt::string_size(2, &v); } if let Some(v) = self.field_type { my_size += ::protobuf::rt::enum_size(3, v); } for value in &self.metric { let len = value.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; }; my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { if let Some(ref v) = self.name.as_ref() { os.write_string(1, &v)?; } if let Some(ref v) = self.help.as_ref() { os.write_string(2, &v)?; } if let Some(v) = self.field_type { os.write_enum(3, v.value())?; } for v in &self.metric { os.write_tag(4, ::protobuf::wire_format::WireTypeLengthDelimited)?; os.write_raw_varint32(v.get_cached_size())?; v.write_to_with_cached_sizes(os)?; }; os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } fn get_cached_size(&self) -> u32 { self.cached_size.get() } fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { &self.unknown_fields } fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { &mut self.unknown_fields } fn as_any(&self) -> &::std::any::Any { self as &::std::any::Any } fn as_any_mut(&mut self) -> &mut ::std::any::Any { self as &mut ::std::any::Any } fn into_any(self: Box) -> ::std::boxed::Box<::std::any::Any> { self } fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { Self::descriptor_static() } fn new() -> MetricFamily { MetricFamily::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, }; unsafe { descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( "name", |m: &MetricFamily| { &m.name }, |m: &mut MetricFamily| { &mut m.name }, )); fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( "help", |m: &MetricFamily| { &m.help }, |m: &mut MetricFamily| { &mut m.help }, )); fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeEnum>( "type", |m: &MetricFamily| { &m.field_type }, |m: &mut MetricFamily| { &mut m.field_type }, )); fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "metric", |m: &MetricFamily| { &m.metric }, |m: &mut MetricFamily| { &mut m.metric }, )); ::protobuf::reflect::MessageDescriptor::new::( "MetricFamily", fields, file_descriptor_proto() ) }) } } fn default_instance() -> &'static MetricFamily { static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const MetricFamily, }; unsafe { instance.get(MetricFamily::new) } } } impl ::protobuf::Clear for MetricFamily { fn clear(&mut self) { self.clear_name(); self.clear_help(); self.clear_field_type(); self.clear_metric(); self.unknown_fields.clear(); } } impl ::std::fmt::Debug for MetricFamily { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } impl ::protobuf::reflect::ProtobufValue for MetricFamily { fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef<'_> { ::protobuf::reflect::ProtobufValueRef::Message(self) } } #[derive(Clone,PartialEq,Eq,Debug,Hash)] pub enum MetricType { COUNTER = 0, GAUGE = 1, SUMMARY = 2, UNTYPED = 3, HISTOGRAM = 4, } impl ::protobuf::ProtobufEnum for MetricType { fn value(&self) -> i32 { *self as i32 } fn from_i32(value: i32) -> ::std::option::Option { match value { 0 => ::std::option::Option::Some(MetricType::COUNTER), 1 => ::std::option::Option::Some(MetricType::GAUGE), 2 => ::std::option::Option::Some(MetricType::SUMMARY), 3 => ::std::option::Option::Some(MetricType::UNTYPED), 4 => ::std::option::Option::Some(MetricType::HISTOGRAM), _ => ::std::option::Option::None } } fn values() -> &'static [Self] { static values: &'static [MetricType] = &[ MetricType::COUNTER, MetricType::GAUGE, MetricType::SUMMARY, MetricType::UNTYPED, MetricType::HISTOGRAM, ]; values } fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor { static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::reflect::EnumDescriptor, }; unsafe { descriptor.get(|| { ::protobuf::reflect::EnumDescriptor::new("MetricType", file_descriptor_proto()) }) } } } impl ::std::marker::Copy for MetricType { } impl ::protobuf::reflect::ProtobufValue for MetricType { fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef<'_> { ::protobuf::reflect::ProtobufValueRef::Enum(self.descriptor()) } } static file_descriptor_proto_data: &'static [u8] = b"\ \n\x11proto_model.proto\x12\x14io.prometheus.client\"0\n\tLabelPair\x12\ \x10\n\x04name\x18\x01\x20\x01(\tB\x02\x18\0\x12\x11\n\x05value\x18\x02\ \x20\x01(\tB\x02\x18\0\"\x1a\n\x05Gauge\x12\x11\n\x05value\x18\x01\x20\ \x01(\x01B\x02\x18\0\"\x1c\n\x07Counter\x12\x11\n\x05value\x18\x01\x20\ \x01(\x01B\x02\x18\0\"3\n\x08Quantile\x12\x14\n\x08quantile\x18\x01\x20\ \x01(\x01B\x02\x18\0\x12\x11\n\x05value\x18\x02\x20\x01(\x01B\x02\x18\0\ \"q\n\x07Summary\x12\x18\n\x0csample_count\x18\x01\x20\x01(\x04B\x02\x18\ \0\x12\x16\n\nsample_sum\x18\x02\x20\x01(\x01B\x02\x18\0\x124\n\x08quant\ ile\x18\x03\x20\x03(\x0b2\x1e.io.prometheus.client.QuantileB\x02\x18\0\"\ \x1c\n\x07Untyped\x12\x11\n\x05value\x18\x01\x20\x01(\x01B\x02\x18\0\"o\ \n\tHistogram\x12\x18\n\x0csample_count\x18\x01\x20\x01(\x04B\x02\x18\0\ \x12\x16\n\nsample_sum\x18\x02\x20\x01(\x01B\x02\x18\0\x120\n\x06bucket\ \x18\x03\x20\x03(\x0b2\x1c.io.prometheus.client.BucketB\x02\x18\0\"?\n\ \x06Bucket\x12\x1c\n\x10cumulative_count\x18\x01\x20\x01(\x04B\x02\x18\0\ \x12\x17\n\x0bupper_bound\x18\x02\x20\x01(\x01B\x02\x18\0\"\xda\x02\n\ \x06Metric\x122\n\x05label\x18\x01\x20\x03(\x0b2\x1f.io.prometheus.clien\ t.LabelPairB\x02\x18\0\x12.\n\x05gauge\x18\x02\x20\x01(\x0b2\x1b.io.prom\ etheus.client.GaugeB\x02\x18\0\x122\n\x07counter\x18\x03\x20\x01(\x0b2\ \x1d.io.prometheus.client.CounterB\x02\x18\0\x122\n\x07summary\x18\x04\ \x20\x01(\x0b2\x1d.io.prometheus.client.SummaryB\x02\x18\0\x122\n\x07unt\ yped\x18\x05\x20\x01(\x0b2\x1d.io.prometheus.client.UntypedB\x02\x18\0\ \x126\n\thistogram\x18\x07\x20\x01(\x0b2\x1f.io.prometheus.client.Histog\ ramB\x02\x18\0\x12\x18\n\x0ctimestamp_ms\x18\x06\x20\x01(\x03B\x02\x18\0\ \"\x98\x01\n\x0cMetricFamily\x12\x10\n\x04name\x18\x01\x20\x01(\tB\x02\ \x18\0\x12\x10\n\x04help\x18\x02\x20\x01(\tB\x02\x18\0\x122\n\x04type\ \x18\x03\x20\x01(\x0e2\x20.io.prometheus.client.MetricTypeB\x02\x18\0\ \x120\n\x06metric\x18\x04\x20\x03(\x0b2\x1c.io.prometheus.client.MetricB\ \x02\x18\0*Q\n\nMetricType\x12\x0b\n\x07COUNTER\x10\0\x12\t\n\x05GAUGE\ \x10\x01\x12\x0b\n\x07SUMMARY\x10\x02\x12\x0b\n\x07UNTYPED\x10\x03\x12\r\ \n\tHISTOGRAM\x10\x04\x1a\x02\x10\0B\0b\x06proto2\ "; static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy { lock: ::protobuf::lazy::ONCE_INIT, ptr: 0 as *const ::protobuf::descriptor::FileDescriptorProto, }; fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto { ::protobuf::parse_from_bytes(file_descriptor_proto_data).unwrap() } pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { unsafe { file_descriptor_proto_lazy.get(|| { parse_descriptor_proto() }) } } prometheus-0.13.4/src/atomic64.rs000064400000000000000000000145731046102023000147010ustar 00000000000000// Copyright 2014 The Prometheus Authors // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::cmp::*; use std::f64; use std::ops::*; use std::sync::atomic::{AtomicI64 as StdAtomicI64, AtomicU64 as StdAtomicU64, Ordering}; /// An interface for numbers. Used to generically model float metrics and integer metrics, i.e. /// [`Counter`](crate::Counter) and [`IntCounter`](crate::Counter). pub trait Number: Sized + AddAssign + SubAssign + PartialOrd + PartialEq + Copy + Send + Sync { /// `std::convert::From for f64` is not implemented, so that we need to implement our own. fn from_i64(v: i64) -> Self; /// Convert to a f64. fn into_f64(self) -> f64; } impl Number for i64 { #[inline] fn from_i64(v: i64) -> Self { v } #[inline] fn into_f64(self) -> f64 { self as f64 } } impl Number for u64 { #[inline] fn from_i64(v: i64) -> Self { v as u64 } #[inline] fn into_f64(self) -> f64 { self as f64 } } impl Number for f64 { #[inline] fn from_i64(v: i64) -> Self { v as f64 } #[inline] fn into_f64(self) -> f64 { self } } /// An interface for atomics. Used to generically model float metrics and integer metrics, i.e. /// [`Counter`](crate::Counter) and [`IntCounter`](crate::IntCounter). pub trait Atomic: Send + Sync { /// The numeric type associated with this atomic. type T: Number; /// Create a new atomic value. fn new(val: Self::T) -> Self; /// Set the value to the provided value. fn set(&self, val: Self::T); /// Get the value. fn get(&self) -> Self::T; /// Increment the value by a given amount. fn inc_by(&self, delta: Self::T); /// Decrement the value by a given amount. fn dec_by(&self, delta: Self::T); } /// A atomic float. #[derive(Debug)] pub struct AtomicF64 { inner: StdAtomicU64, } #[inline] fn u64_to_f64(val: u64) -> f64 { f64::from_bits(val) } #[inline] fn f64_to_u64(val: f64) -> u64 { f64::to_bits(val) } impl Atomic for AtomicF64 { type T = f64; fn new(val: Self::T) -> AtomicF64 { AtomicF64 { inner: StdAtomicU64::new(f64_to_u64(val)), } } #[inline] fn set(&self, val: Self::T) { self.inner.store(f64_to_u64(val), Ordering::Relaxed); } #[inline] fn get(&self) -> Self::T { u64_to_f64(self.inner.load(Ordering::Relaxed)) } #[inline] fn inc_by(&self, delta: Self::T) { loop { let current = self.inner.load(Ordering::Acquire); let new = u64_to_f64(current) + delta; let result = self.inner.compare_exchange_weak( current, f64_to_u64(new), Ordering::Release, Ordering::Relaxed, ); if result.is_ok() { return; } } } #[inline] fn dec_by(&self, delta: Self::T) { self.inc_by(-delta); } } impl AtomicF64 { /// Store the value, returning the previous value. pub fn swap(&self, val: f64, ordering: Ordering) -> f64 { u64_to_f64(self.inner.swap(f64_to_u64(val), ordering)) } } /// A atomic signed integer. #[derive(Debug)] pub struct AtomicI64 { inner: StdAtomicI64, } impl Atomic for AtomicI64 { type T = i64; fn new(val: Self::T) -> AtomicI64 { AtomicI64 { inner: StdAtomicI64::new(val), } } #[inline] fn set(&self, val: Self::T) { self.inner.store(val, Ordering::Relaxed); } #[inline] fn get(&self) -> Self::T { self.inner.load(Ordering::Relaxed) } #[inline] fn inc_by(&self, delta: Self::T) { self.inner.fetch_add(delta, Ordering::Relaxed); } #[inline] fn dec_by(&self, delta: Self::T) { self.inner.fetch_sub(delta, Ordering::Relaxed); } } /// A atomic unsigned integer. #[derive(Debug)] pub struct AtomicU64 { inner: StdAtomicU64, } impl Atomic for AtomicU64 { type T = u64; fn new(val: Self::T) -> AtomicU64 { AtomicU64 { inner: StdAtomicU64::new(val), } } #[inline] fn set(&self, val: Self::T) { self.inner.store(val, Ordering::Relaxed); } #[inline] fn get(&self) -> Self::T { self.inner.load(Ordering::Relaxed) } #[inline] fn inc_by(&self, delta: Self::T) { self.inc_by_with_ordering(delta, Ordering::Relaxed); } #[inline] fn dec_by(&self, delta: Self::T) { self.inner.fetch_sub(delta, Ordering::Relaxed); } } impl AtomicU64 { /// Stores a value into the atomic integer if the current value is the same /// as the current value. /// /// This function is allowed to spuriously fail even when the comparison /// succeeds, which can result in more efficient code on some platforms. The /// return value is a result indicating whether the new value was written /// and containing the previous value. /// /// See [`StdAtomicU64`] for details. pub(crate) fn compare_exchange_weak( &self, current: u64, new: u64, success: Ordering, failure: Ordering, ) -> Result { self.inner .compare_exchange_weak(current, new, success, failure) } /// Increment the value by a given amount with the provided memory ordering. pub fn inc_by_with_ordering(&self, delta: u64, ordering: Ordering) { self.inner.fetch_add(delta, ordering); } /// Stores a value into the atomic integer, returning the previous value. pub fn swap(&self, val: u64, ordering: Ordering) -> u64 { self.inner.swap(val, ordering) } } #[cfg(test)] mod test { use std::f64::consts::PI; use std::f64::{self, EPSILON}; use super::*; #[test] fn test_atomic_f64() { let table: Vec = vec![0.0, 1.0, PI, f64::MIN, f64::MAX]; for f in table { assert!((f - AtomicF64::new(f).get()).abs() < EPSILON); } } #[test] fn test_atomic_i64() { let ai64 = AtomicI64::new(0); assert_eq!(ai64.get(), 0); ai64.inc_by(1); assert_eq!(ai64.get(), 1); ai64.inc_by(-5); assert_eq!(ai64.get(), -4); } #[test] fn test_atomic_u64() { let au64 = AtomicU64::new(0); assert_eq!(au64.get(), 0); au64.inc_by(123); assert_eq!(au64.get(), 123); } } prometheus-0.13.4/src/auto_flush.rs000064400000000000000000000133741046102023000154220ustar 00000000000000use crate::core::Atomic; use crate::counter::{CounterWithValueType, GenericLocalCounter}; use crate::histogram::{Instant, LocalHistogram}; use crate::metrics::MayFlush; use crate::timer; use parking_lot::Mutex; use std::thread::LocalKey; /// Delegator for auto flush-able local counter pub trait CounterDelegator { /// Get the root local metric for delegate fn get_root_metric(&self) -> &'static LocalKey; /// Get the final counter for delegate fn get_local<'a>(&self, root_metric: &'a T) -> &'a GenericLocalCounter; } /// Delegator for auto flush-able local counter pub trait HistogramDelegator { /// Get the root local metric for delegate fn get_root_metric(&self) -> &'static LocalKey; /// Get the final counter for delegate fn get_local<'a>(&self, root_metric: &'a T) -> &'a LocalHistogram; } /// Auto flush-able local counter #[derive(Debug)] pub struct AFLocalCounter> { /// Delegator to get thread local metric delegator: D, /// Phantomdata marker _p: std::marker::PhantomData<(Mutex, Mutex)>, } impl> AFLocalCounter { /// Construct a new AFLocalCounter from delegator. pub fn new(delegator: D) -> AFLocalCounter { timer::ensure_updater(); AFLocalCounter { delegator, _p: std::marker::PhantomData, } } } /// Auto flush-able local counter impl> AFLocalCounter { #[inline] /// Get the root local metric for delegate fn get_root_metric(&self) -> &'static LocalKey { self.delegator.get_root_metric() } #[inline] /// Get the final counter for delegate fn get_counter<'a>(&self, root_metric: &'a T) -> &'a GenericLocalCounter { self.delegator.get_local(root_metric) } /// Increase the given value to the local counter, /// and try to flush to global /// # Panics /// /// Panics in debug build if the value is < 0. #[inline] pub fn inc_by(&self, v: ::T) { self.get_root_metric().with(|m| { let counter = self.get_counter(m); counter.inc_by(v); m.may_flush(); }) } /// Increase the local counter by 1, /// and try to flush to global. #[inline] pub fn inc(&self) { self.get_root_metric().with(|m| { let counter = self.get_counter(m); counter.inc(); m.may_flush(); }) } /// Return the local counter value. #[inline] pub fn get(&self) -> ::T { self.get_root_metric().with(|m| { let counter = self.get_counter(m); counter.get() }) } /// Restart the counter, resetting its value back to 0. #[inline] pub fn reset(&self) { self.get_root_metric().with(|m| { let counter = self.get_counter(m); counter.reset(); }) } /// trigger flush of LocalKey #[inline] pub fn flush(&self) { self.get_root_metric().with(|m| m.flush()) } } /// Auto flush-able local counter #[derive(Debug)] pub struct AFLocalHistogram> { /// Delegator to get thread local metric delegator: D, /// Phantomdata marker _p: std::marker::PhantomData>, } impl> AFLocalHistogram { /// Construct a new AFLocalHistogram from delegator pub fn new(delegator: D) -> AFLocalHistogram { timer::ensure_updater(); AFLocalHistogram { delegator, _p: std::marker::PhantomData, } } } impl> AFLocalHistogram { /// 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); local.observe(v); m.may_flush(); }) } /// Observe execution time of a closure, in second. pub fn observe_closure_duration(&self, f: F) -> T where F: FnOnce() -> T, { let instant = Instant::now(); let res = f(); let elapsed = instant.elapsed_sec(); self.observe(elapsed); res } /// Observe execution time of a closure, in second. #[cfg(feature = "nightly")] pub fn observe_closure_duration_coarse(&self, f: F) -> T where F: FnOnce() -> T, { let instant = Instant::now_coarse(); let res = f(); let elapsed = instant.elapsed_sec(); self.observe(elapsed); res } /// Clear the local metric. pub fn clear(&self) { self.delegator .get_root_metric() .with(|m| self.delegator.get_local(m).clear()) } /// Flush the local metrics to the [`Histogram`](crate::Histogram) metric. pub fn flush(&self) { self.delegator .get_root_metric() .with(|m| self.delegator.get_local(m).flush()); } /// Return accumulated sum of local samples. pub fn get_sample_sum(&self) -> f64 { self.delegator .get_root_metric() .with(|m| self.delegator.get_local(m).get_sample_sum()) } /// Return count of local samples. pub fn get_sample_count(&self) -> u64 { self.delegator .get_root_metric() .with(|m| self.delegator.get_local(m).get_sample_count()) } } prometheus-0.13.4/src/counter.rs000064400000000000000000000452231046102023000147260ustar 00000000000000// Copyright 2014 The Prometheus Authors // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::cell::RefCell; use std::collections::HashMap; use std::marker::PhantomData; use std::sync::Arc; use crate::atomic64::{Atomic, AtomicF64, AtomicU64, Number}; use crate::desc::Desc; use crate::errors::Result; use crate::metrics::{Collector, LocalMetric, Metric, Opts}; use crate::proto; use crate::value::{Value, ValueType}; use crate::vec::{MetricVec, MetricVecBuilder}; /// The underlying implementation for [`Counter`] and [`IntCounter`]. #[derive(Debug)] pub struct GenericCounter { v: Arc>, } /// A [`Metric`] represents a single numerical value that only ever goes up. pub type Counter = GenericCounter; /// The integer version of [`Counter`]. Provides better performance if metric values /// are all positive integers (natural numbers). pub type IntCounter = GenericCounter; impl Clone for GenericCounter

{ fn clone(&self) -> Self { Self { v: Arc::clone(&self.v), } } } impl GenericCounter

{ /// Create a [`GenericCounter`] with the `name` and `help` arguments. pub fn new, S2: Into>(name: S1, help: S2) -> Result { let opts = Opts::new(name, help); Self::with_opts(opts) } /// Create a [`GenericCounter`] with the `opts` options. pub fn with_opts(opts: Opts) -> Result { Self::with_opts_and_label_values(&opts, &[]) } fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result { let v = Value::new(opts, ValueType::Counter, P::T::from_i64(0), label_values)?; Ok(Self { v: Arc::new(v) }) } /// Increase the given value to the counter. /// /// # Panics /// /// Panics in debug build if the value is < 0. #[inline] pub fn inc_by(&self, v: P::T) { debug_assert!(v >= P::T::from_i64(0)); self.v.inc_by(v); } /// Increase the counter by 1. #[inline] pub fn inc(&self) { self.v.inc(); } /// Return the counter value. #[inline] pub fn get(&self) -> P::T { self.v.get() } /// Restart the counter, resetting its value back to 0. #[inline] pub fn reset(&self) { self.v.set(P::T::from_i64(0)) } /// Return a [`GenericLocalCounter`] for single thread usage. pub fn local(&self) -> GenericLocalCounter

{ GenericLocalCounter::new(self.clone()) } } impl Collector for GenericCounter

{ fn desc(&self) -> Vec<&Desc> { vec![&self.v.desc] } fn collect(&self) -> Vec { vec![self.v.collect()] } } impl Metric for GenericCounter

{ fn metric(&self) -> proto::Metric { self.v.metric() } } #[derive(Debug)] pub struct CounterVecBuilder { _phantom: PhantomData

, } impl CounterVecBuilder

{ pub fn new() -> Self { Self { _phantom: PhantomData, } } } impl Clone for CounterVecBuilder

{ fn clone(&self) -> Self { Self::new() } } impl MetricVecBuilder for CounterVecBuilder

{ type M = GenericCounter

; type P = Opts; fn build(&self, opts: &Opts, vals: &[&str]) -> Result { Self::M::with_opts_and_label_values(opts, vals) } } /// The underlying implementation for [`CounterVec`] and [`IntCounterVec`]. pub type GenericCounterVec

= MetricVec>; /// A [`Collector`] that bundles a set of [`Counter`]s that all share /// the same [`Desc`], but have different values for their variable labels. This is /// used if you want to count the same thing partitioned by various dimensions /// (e.g. number of HTTP requests, partitioned by response code and method). pub type CounterVec = GenericCounterVec; /// The integer version of [`CounterVec`]. Provides better performance if metric /// are all positive integers (natural numbers). pub type IntCounterVec = GenericCounterVec; impl GenericCounterVec

{ /// Create a new [`GenericCounterVec`] based on the provided /// [`Opts`] and partitioned by the given label names. At least one label name must be /// provided. pub fn new(opts: Opts, label_names: &[&str]) -> Result { let variable_names = label_names.iter().map(|s| (*s).to_owned()).collect(); let opts = opts.variable_labels(variable_names); let metric_vec = MetricVec::create(proto::MetricType::COUNTER, CounterVecBuilder::new(), opts)?; Ok(metric_vec as Self) } /// Return a [`GenericLocalCounterVec`] for single thread usage. pub fn local(&self) -> GenericLocalCounterVec

{ GenericLocalCounterVec::new(self.clone()) } } /// The underlying implementation for [`LocalCounter`] /// and [`LocalIntCounter`]. #[derive(Debug)] pub struct GenericLocalCounter { counter: GenericCounter

, val: RefCell, } /// For auto_flush::AFLocalCounter to use to make type inference possible pub trait CounterWithValueType { ///the exact type which implements Atomic type ValueType: Atomic; } impl CounterWithValueType for GenericLocalCounter

{ type ValueType = P; } /// An unsync [`Counter`]. pub type LocalCounter = GenericLocalCounter; /// The integer version of [`LocalCounter`]. Provides better performance /// are all positive integers (natural numbers). pub type LocalIntCounter = GenericLocalCounter; impl GenericLocalCounter

{ fn new(counter: GenericCounter

) -> Self { Self { counter, val: RefCell::new(P::T::from_i64(0)), } } /// Increase the given value to the local counter. /// /// # Panics /// /// Panics in debug build if the value is < 0. #[inline] pub fn inc_by(&self, v: P::T) { debug_assert!(v >= P::T::from_i64(0)); *self.val.borrow_mut() += v; } /// Increase the local counter by 1. #[inline] pub fn inc(&self) { *self.val.borrow_mut() += P::T::from_i64(1); } /// Return the local counter value. #[inline] pub fn get(&self) -> P::T { *self.val.borrow() } /// Restart the counter, resetting its value back to 0. #[inline] pub fn reset(&self) { *self.val.borrow_mut() = P::T::from_i64(0); } /// Flush the local metrics to the [`Counter`]. #[inline] pub fn flush(&self) { if *self.val.borrow() == P::T::from_i64(0) { return; } self.counter.inc_by(*self.val.borrow()); *self.val.borrow_mut() = P::T::from_i64(0); } } impl LocalMetric for GenericLocalCounter

{ /// Flush the local metrics to the [`Counter`]. #[inline] fn flush(&self) { GenericLocalCounter::flush(self); } } impl Clone for GenericLocalCounter

{ fn clone(&self) -> Self { Self::new(self.counter.clone()) } } /// The underlying implementation for [`LocalCounterVec`] /// and [`LocalIntCounterVec`]. pub struct GenericLocalCounterVec { vec: GenericCounterVec

, local: HashMap>, } impl std::fmt::Debug for GenericLocalCounterVec

{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "GenericLocalCounterVec ({} locals)", self.local.keys().len() ) } } /// An unsync [`CounterVec`]. pub type LocalCounterVec = GenericLocalCounterVec; /// The integer version of [`LocalCounterVec`]. /// Provides better performance if metric values are all positive /// integers (natural numbers). pub type LocalIntCounterVec = GenericLocalCounterVec; impl GenericLocalCounterVec

{ fn new(vec: GenericCounterVec

) -> Self { let local = HashMap::with_capacity(vec.v.children.read().len()); Self { vec, local } } /// Get a [`GenericLocalCounter`] by label values. /// See more [MetricVec::with_label_values]. pub fn with_label_values<'a>(&'a mut self, vals: &[&str]) -> &'a mut GenericLocalCounter

{ let hash = self.vec.v.hash_label_values(vals).unwrap(); let vec = &self.vec; self.local .entry(hash) .or_insert_with(|| vec.with_label_values(vals).local()) } /// Remove a [`GenericLocalCounter`] by label values. /// See more [MetricVec::remove_label_values]. pub fn remove_label_values(&mut self, vals: &[&str]) -> Result<()> { let hash = self.vec.v.hash_label_values(vals)?; self.local.remove(&hash); self.vec.v.delete_label_values(vals) } /// Flush the local metrics to the [`CounterVec`] metric. pub fn flush(&self) { for h in self.local.values() { h.flush(); } } } impl LocalMetric for GenericLocalCounterVec

{ /// Flush the local metrics to the [`CounterVec`] metric. fn flush(&self) { GenericLocalCounterVec::flush(self); } } impl Clone for GenericLocalCounterVec

{ fn clone(&self) -> Self { Self::new(self.vec.clone()) } } #[cfg(test)] mod tests { use std::collections::HashMap; use std::f64::EPSILON; use super::*; use crate::metrics::{Collector, Opts}; #[test] fn test_counter() { let opts = Opts::new("test", "test help") .const_label("a", "1") .const_label("b", "2"); let counter = Counter::with_opts(opts).unwrap(); counter.inc(); assert_eq!(counter.get() as u64, 1); counter.inc_by(42.0); assert_eq!(counter.get() as u64, 43); let mut mfs = counter.collect(); assert_eq!(mfs.len(), 1); let mf = mfs.pop().unwrap(); let m = mf.get_metric().get(0).unwrap(); assert_eq!(m.get_label().len(), 2); assert_eq!(m.get_counter().get_value() as u64, 43); counter.reset(); assert_eq!(counter.get() as u64, 0); } #[test] fn test_int_counter() { let counter = IntCounter::new("foo", "bar").unwrap(); counter.inc(); assert_eq!(counter.get(), 1); counter.inc_by(11); assert_eq!(counter.get(), 12); let mut mfs = counter.collect(); assert_eq!(mfs.len(), 1); let mf = mfs.pop().unwrap(); let m = mf.get_metric().get(0).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); } #[test] fn test_local_counter() { let counter = Counter::new("counter", "counter helper").unwrap(); let local_counter1 = counter.local(); let local_counter2 = counter.local(); local_counter1.inc(); local_counter2.inc(); assert_eq!(local_counter1.get() as u64, 1); assert_eq!(local_counter2.get() as u64, 1); assert_eq!(counter.get() as u64, 0); local_counter1.flush(); assert_eq!(local_counter1.get() as u64, 0); assert_eq!(counter.get() as u64, 1); local_counter2.flush(); assert_eq!(counter.get() as u64, 2); local_counter1.reset(); local_counter2.reset(); counter.reset(); assert_eq!(counter.get() as u64, 0); local_counter1.flush(); assert_eq!(counter.get() as u64, 0); local_counter2.flush(); assert_eq!(counter.get() as u64, 0); } #[test] fn test_int_local_counter() { let counter = IntCounter::new("foo", "bar").unwrap(); let local_counter = counter.local(); local_counter.inc(); assert_eq!(local_counter.get(), 1); assert_eq!(counter.get(), 0); local_counter.inc_by(5); local_counter.flush(); assert_eq!(local_counter.get(), 0); assert_eq!(counter.get(), 6); local_counter.reset(); counter.reset(); assert_eq!(counter.get() as u64, 0); local_counter.flush(); assert_eq!(counter.get() as u64, 0); } #[test] fn test_counter_vec_with_labels() { let vec = CounterVec::new( Opts::new("test_couter_vec", "test counter 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(); assert!(vec.remove(&labels).is_ok()); assert!(vec.remove(&labels).is_err()); let mut labels2 = HashMap::new(); labels2.insert("l1", "v2"); labels2.insert("l2", "v1"); vec.with(&labels).inc(); assert!(vec.remove(&labels2).is_err()); vec.with(&labels).inc(); let mut labels3 = HashMap::new(); labels3.insert("l1", "v1"); assert!(vec.remove(&labels3).is_err()); } #[test] fn test_int_counter_vec() { let vec = IntCounterVec::new(Opts::new("foo", "bar"), &["l1", "l2"]).unwrap(); vec.with_label_values(&["v1", "v3"]).inc(); assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 1); vec.with_label_values(&["v1", "v2"]).inc_by(12); assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 1); assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 12); vec.with_label_values(&["v4", "v2"]).inc_by(2); assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 1); assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 12); assert_eq!(vec.with_label_values(&["v4", "v2"]).get(), 2); vec.with_label_values(&["v1", "v3"]).inc_by(5); assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 6); assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 12); assert_eq!(vec.with_label_values(&["v4", "v2"]).get(), 2); } #[test] fn test_counter_vec_with_label_values() { let vec = CounterVec::new( Opts::new("test_vec", "test counter vec help"), &["l1", "l2"], ) .unwrap(); assert!(vec.remove_label_values(&["v1", "v2"]).is_err()); vec.with_label_values(&["v1", "v2"]).inc(); assert!(vec.remove_label_values(&["v1", "v2"]).is_ok()); vec.with_label_values(&["v1", "v2"]).inc(); assert!(vec.remove_label_values(&["v1"]).is_err()); assert!(vec.remove_label_values(&["v1", "v3"]).is_err()); } #[test] fn test_counter_vec_local() { let vec = CounterVec::new( Opts::new("test_vec_local", "test counter vec help"), &["l1", "l2"], ) .unwrap(); let mut local_vec_1 = vec.local(); let mut local_vec_2 = local_vec_1.clone(); 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); 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); 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); 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); 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); // 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); 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); 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); local_vec_1.flush(); local_vec_2.flush(); assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= EPSILON); local_vec_1.flush(); local_vec_2.flush(); assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= EPSILON); } #[test] fn test_int_counter_vec_local() { let vec = IntCounterVec::new(Opts::new("foo", "bar"), &["l1", "l2"]).unwrap(); let mut local_vec_1 = vec.local(); assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_err()); local_vec_1.with_label_values(&["v1", "v2"]).inc_by(23); assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 23); assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 0); local_vec_1.flush(); assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 0); assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 23); local_vec_1.flush(); assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 0); assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 23); local_vec_1.with_label_values(&["v1", "v2"]).inc_by(11); assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 11); assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 23); local_vec_1.flush(); assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 0); assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 34); } #[cfg(debug_assertions)] #[test] #[should_panic(expected = "assertion failed")] fn test_counter_negative_inc() { let counter = Counter::new("foo", "bar").unwrap(); counter.inc_by(-42.0); } #[cfg(debug_assertions)] #[test] #[should_panic(expected = "assertion failed")] fn test_local_counter_negative_inc() { let counter = Counter::new("foo", "bar").unwrap(); let local = counter.local(); local.inc_by(-42.0); } } prometheus-0.13.4/src/desc.rs000064400000000000000000000227351046102023000141700ustar 00000000000000// Copyright 2014 The Prometheus Authors // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::collections::{BTreeSet, HashMap}; use std::hash::Hasher; use fnv::FnvHasher; use crate::errors::{Error, Result}; use crate::metrics::SEPARATOR_BYTE; use crate::proto::LabelPair; // [a-zA-Z_] fn matches_charset_without_colon(c: char) -> bool { c.is_ascii_alphabetic() || c == '_' } // [a-zA-Z_:] fn matches_charset_with_colon(c: char) -> bool { matches_charset_without_colon(c) || c == ':' } // Equivalent to regex ^[?][?0-9]*$ where ? denotes char set as validated by charset_validator fn is_valid_ident bool>(input: &str, mut charset_validator: F) -> bool { let mut chars = input.chars(); let zeroth = chars.next(); zeroth .and_then(|zeroth| { if charset_validator(zeroth) { Some(chars.all(|c| charset_validator(c) || c.is_ascii_digit())) } else { None } }) .unwrap_or(false) } // Details of required format are at // https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels pub(super) fn is_valid_metric_name(name: &str) -> bool { is_valid_ident(name, matches_charset_with_colon) } pub(super) fn is_valid_label_name(name: &str) -> bool { is_valid_ident(name, matches_charset_without_colon) } /// The descriptor used by every Prometheus [`Metric`](crate::core::Metric). It is essentially /// the immutable meta-data of a metric. The normal metric implementations /// included in this package manage their [`Desc`] under the hood. /// /// Descriptors registered with the same registry have to fulfill certain /// consistency and uniqueness criteria if they share the same fully-qualified /// name: They must have the same help string and the same label names (aka label /// dimensions) in each, constLabels and variableLabels, but they must differ in /// the values of the constLabels. /// /// Descriptors that share the same fully-qualified names and the same label /// values of their constLabels are considered equal. #[derive(Clone, Debug)] pub struct Desc { /// fq_name has been built from Namespace, Subsystem, and Name. pub fq_name: String, /// help provides some helpful information about this metric. pub help: String, /// const_label_pairs contains precalculated DTO label pairs based on /// the constant labels. pub const_label_pairs: Vec, /// variable_labels contains names of labels for which the metric /// maintains variable values. pub variable_labels: Vec, /// id is a hash of the values of the ConstLabels and fqName. This /// must be unique among all registered descriptors and can therefore be /// used as an identifier of the descriptor. pub id: u64, /// dim_hash is a hash of the label names (preset and variable) and the /// Help string. Each Desc with the same fqName must have the same /// dimHash. pub dim_hash: u64, } impl Desc { /// Initializes a new [`Desc`]. Errors are recorded in the Desc /// and will be reported on registration time. variableLabels and constLabels can /// be nil if no such labels should be set. fqName and help must not be empty. pub fn new( fq_name: String, help: String, variable_labels: Vec, const_labels: HashMap, ) -> Result { let mut desc = Desc { fq_name: fq_name.clone(), help, const_label_pairs: Vec::with_capacity(const_labels.len()), variable_labels, id: 0, dim_hash: 0, }; if desc.help.is_empty() { return Err(Error::Msg("empty help string".into())); } if !is_valid_metric_name(&desc.fq_name) { return Err(Error::Msg(format!( "'{}' is not a valid metric name", desc.fq_name ))); } let mut label_values = Vec::with_capacity(const_labels.len() + 1); label_values.push(fq_name); let mut label_names = BTreeSet::new(); for label_name in const_labels.keys() { if !is_valid_label_name(label_name) { return Err(Error::Msg(format!( "'{}' is not a valid label name", &label_name ))); } if !label_names.insert(label_name.clone()) { return Err(Error::Msg(format!( "duplicate const label name {}", label_name ))); } } // ... so that we can now add const label values in the order of their names. for label_name in &label_names { label_values.push(const_labels.get(label_name).cloned().unwrap()); } // Now add the variable label names, but prefix them with something that // cannot be in a regular label name. That prevents matching the label // dimension with a different mix between preset and variable labels. for label_name in &desc.variable_labels { if !is_valid_label_name(label_name) { return Err(Error::Msg(format!( "'{}' is not a valid label name", &label_name ))); } if !label_names.insert(format!("${}", label_name)) { return Err(Error::Msg(format!( "duplicate variable label name {}", label_name ))); } } let mut vh = FnvHasher::default(); for val in &label_values { vh.write(val.as_bytes()); vh.write_u8(SEPARATOR_BYTE); } desc.id = vh.finish(); // Now hash together (in this order) the help string and the sorted // label names. let mut lh = FnvHasher::default(); lh.write(desc.help.as_bytes()); lh.write_u8(SEPARATOR_BYTE); for label_name in &label_names { lh.write(label_name.as_bytes()); lh.write_u8(SEPARATOR_BYTE); } desc.dim_hash = lh.finish(); for (key, value) in const_labels { let mut label_pair = LabelPair::default(); label_pair.set_name(key); label_pair.set_value(value); desc.const_label_pairs.push(label_pair); } desc.const_label_pairs.sort(); Ok(desc) } } /// An interface for describing the immutable meta-data of a [`Metric`](crate::core::Metric). pub trait Describer { /// `describe` returns a [`Desc`]. fn describe(&self) -> Result; } #[cfg(test)] mod tests { use std::collections::HashMap; use crate::desc::{is_valid_label_name, is_valid_metric_name, Desc}; use crate::errors::Error; #[test] fn test_is_valid_metric_name() { let tbl = [ (":", true), ("_", true), ("a", true), (":9", true), ("_9", true), ("a9", true), ("a_b_9_d:x_", true), ("9", false), ("9:", false), ("9_", false), ("9a", false), ("a-", false), ]; for &(name, expected) in &tbl { assert_eq!(is_valid_metric_name(name), expected); } } #[test] fn test_is_valid_label_name() { let tbl = [ ("_", true), ("a", true), ("_9", true), ("a9", true), ("a_b_9_dx_", true), (":", false), (":9", false), ("9", false), ("9:", false), ("9_", false), ("9a", false), ("a-", false), ("a_b_9_d:x_", false), ]; for &(name, expected) in &tbl { assert_eq!(is_valid_label_name(name), expected); } } #[test] fn test_invalid_const_label_name() { for &name in &["-dash", "9gag", ":colon", "colon:", "has space"] { let res = Desc::new( "name".into(), "help".into(), vec![name.into()], HashMap::new(), ) .err() .expect(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), }; } } #[test] fn test_invalid_variable_label_name() { for &name in &["-dash", "9gag", ":colon", "colon:", "has space"] { 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()); match res { Error::Msg(msg) => assert_eq!(msg, format!("'{}' is not a valid label name", name)), other => panic!("{}", other), }; } } #[test] 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()); match res { Error::Msg(msg) => { assert_eq!(msg, format!("'{}' is not a valid metric name", name)) } other => panic!("{}", other), }; } } } prometheus-0.13.4/src/encoder/mod.rs000064400000000000000000000060341046102023000154420ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. #[cfg(feature = "protobuf")] mod pb; mod text; #[cfg(feature = "protobuf")] pub use self::pb::{ProtobufEncoder, PROTOBUF_FORMAT}; pub use self::text::{TextEncoder, TEXT_FORMAT}; use std::io::Write; use crate::errors::{Error, Result}; 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`. 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(&self, mfs: &[MetricFamily], writer: &mut W) -> Result<()>; /// `format_type` returns target format. fn format_type(&self) -> &str; } 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() { return Err(Error::Msg(format!("MetricFamily has no name: {:?}", mf))); } Ok(()) } #[cfg(test)] mod tests { use super::*; use crate::counter::CounterVec; use crate::encoder::Encoder; use crate::metrics::Collector; use crate::metrics::Opts; #[test] #[cfg(feature = "protobuf")] fn test_bad_proto_metrics() { let mut writer = Vec::::new(); let pb_encoder = ProtobufEncoder::new(); let cv = CounterVec::new( Opts::new("test_counter_vec", "help information"), &["labelname"], ) .unwrap(); // Empty metrics let mfs = cv.collect(); check_metric_family(&mfs[0]).unwrap_err(); pb_encoder.encode(&mfs, &mut writer).unwrap_err(); assert_eq!(writer.len(), 0); // Add a sub metric cv.with_label_values(&["foo"]).inc(); let mut mfs = cv.collect(); // Empty name (&mut mfs[0]).clear_name(); check_metric_family(&mfs[0]).unwrap_err(); pb_encoder.encode(&mfs, &mut writer).unwrap_err(); assert_eq!(writer.len(), 0); } #[test] fn test_bad_text_metrics() { let mut writer = Vec::::new(); let text_encoder = TextEncoder::new(); let cv = CounterVec::new( Opts::new("test_counter_vec", "help information"), &["labelname"], ) .unwrap(); // Empty metrics let mfs = cv.collect(); check_metric_family(&mfs[0]).unwrap_err(); text_encoder.encode(&mfs, &mut writer).unwrap_err(); assert_eq!(writer.len(), 0); // Add a sub metric cv.with_label_values(&["foo"]).inc(); let mut mfs = cv.collect(); // Empty name (&mut mfs[0]).clear_name(); check_metric_family(&mfs[0]).unwrap_err(); text_encoder.encode(&mfs, &mut writer).unwrap_err(); assert_eq!(writer.len(), 0); } } prometheus-0.13.4/src/encoder/pb.rs000064400000000000000000000047741046102023000152750ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::io::Write; use protobuf::Message; use crate::errors::Result; use crate::proto::MetricFamily; use super::{check_metric_family, Encoder}; /// The protocol buffer format of metric family. pub const PROTOBUF_FORMAT: &str = "application/vnd.google.protobuf; \ proto=io.prometheus.client.MetricFamily; \ encoding=delimited"; /// An implementation of an [`Encoder`] that converts a [`MetricFamily`] proto /// message into the binary wire format of protobuf. #[derive(Debug, Default)] pub struct ProtobufEncoder; impl ProtobufEncoder { /// Create a new protobuf encoder. pub fn new() -> ProtobufEncoder { ProtobufEncoder } } impl Encoder for ProtobufEncoder { fn encode(&self, metric_families: &[MetricFamily], writer: &mut W) -> Result<()> { for mf in metric_families { // Fail-fast checks. check_metric_family(mf)?; mf.write_length_delimited_to_writer(writer)?; } Ok(()) } fn format_type(&self) -> &str { PROTOBUF_FORMAT } } #[cfg(test)] mod tests { use crate::counter::CounterVec; use crate::encoder::Encoder; use crate::metrics::Opts; use crate::registry; // TODO: add more tests. #[rustfmt::skip] #[test] fn test_protobuf_encoder() { let cv = CounterVec::new(Opts::new("test_counter_vec", "help information"), &["labelname"]) .unwrap(); let reg = registry::Registry::new(); reg.register(Box::new(cv.clone())).unwrap(); cv.get_metric_with_label_values(&["2230"]).unwrap().inc(); let mf = reg.gather(); let mut writer = Vec::::new(); let encoder = super::ProtobufEncoder::new(); let res = encoder.encode(&mf, &mut writer); assert!(res.is_ok()); // Generated by a golang demo, // see more: https://gist.github.com/overvenus/bd39bde014b0cba87c9bde20dbea6ce0 let ans = vec![70, 10, 16, 116, 101, 115, 116, 95, 99, 111, 117, 110, 116, 101, 114, 95, 118, 101, 99, 18, 16, 104, 101, 108, 112, 32, 105, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 24, 0, 34, 30, 10, 17, 10, 9, 108, 97, 98, 101, 108, 110, 97, 109, 101, 18, 4, 50, 50, 51, 48, 26, 9, 9, 0, 0, 0, 0, 0, 0, 240, 63]; assert_eq!(ans, writer); } } prometheus-0.13.4/src/encoder/text.rs000064400000000000000000000364201046102023000156510ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::borrow::Cow; use std::io::{self, Write}; use crate::errors::Result; use crate::histogram::BUCKET_LABEL; use crate::proto::{self, MetricFamily, MetricType}; use super::{check_metric_family, Encoder}; /// The text format of metric family. pub const TEXT_FORMAT: &str = "text/plain; version=0.0.4"; const POSITIVE_INF: &str = "+Inf"; const QUANTILE: &str = "quantile"; /// An implementation of an [`Encoder`] that converts a [`MetricFamily`] proto message /// into text format. #[derive(Debug, Default)] pub struct TextEncoder; impl TextEncoder { /// Create a new text encoder. pub fn new() -> TextEncoder { TextEncoder } /// Appends metrics to a given `String` buffer. /// /// This is a convenience wrapper around `::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 `::encode`. pub fn encode_to_string(&self, metric_families: &[MetricFamily]) -> Result { let mut buf = String::new(); self.encode_utf8(metric_families, &mut buf)?; Ok(buf) } 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(); if !help.is_empty() { 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("# 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 { MetricType::COUNTER => { write_sample(writer, name, None, m, None, m.get_counter().get_value())?; } MetricType::GAUGE => { write_sample(writer, name, None, m, None, m.get_gauge().get_value())?; } MetricType::HISTOGRAM => { let h = m.get_histogram(); let mut inf_seen = false; for b in h.get_bucket() { let upper_bound = b.get_upper_bound(); write_sample( writer, name, Some("_bucket"), m, Some((BUCKET_LABEL, &upper_bound.to_string())), b.get_cumulative_count() as f64, )?; if upper_bound.is_sign_positive() && upper_bound.is_infinite() { inf_seen = true; } } if !inf_seen { write_sample( writer, name, Some("_bucket"), m, Some((BUCKET_LABEL, POSITIVE_INF)), h.get_sample_count() as f64, )?; } write_sample(writer, name, Some("_sum"), m, None, h.get_sample_sum())?; write_sample( writer, name, Some("_count"), m, None, h.get_sample_count() as f64, )?; } MetricType::SUMMARY => { let s = m.get_summary(); for q in s.get_quantile() { write_sample( writer, name, None, m, Some((QUANTILE, &q.get_quantile().to_string())), q.get_value(), )?; } write_sample(writer, name, Some("_sum"), m, None, s.get_sample_sum())?; write_sample( writer, name, Some("_count"), m, None, s.get_sample_count() as f64, )?; } MetricType::UNTYPED => { unimplemented!(); } } } } Ok(()) } } impl Encoder for TextEncoder { fn encode(&self, metric_families: &[MetricFamily], writer: &mut W) -> Result<()> { self.encode_impl(metric_families, &mut *writer) } fn format_type(&self) -> &str { TEXT_FORMAT } } /// `write_sample` writes a single sample in text format to `writer`, given the /// metric name, an optional metric name postfix, the metric proto message /// itself, optionally an additional label name and value (use empty strings if /// not required), and the value. The function returns the number of bytes /// written and any error encountered. fn write_sample( 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)?; if let Some(postfix) = name_postfix { writer.write_all(postfix)?; } label_pairs_to_text(mc.get_label(), additional_label, writer)?; writer.write_all(" ")?; writer.write_all(&value.to_string())?; let timestamp = mc.get_timestamp_ms(); if timestamp != 0 { writer.write_all(" ")?; writer.write_all(×tamp.to_string())?; } writer.write_all("\n")?; Ok(()) } /// `label_pairs_to_text` converts a slice of `LabelPair` proto messages plus /// the explicitly given additional label pair into text formatted as required /// by the text format and writes it to `writer`. An empty slice in combination /// with an empty string `additional_label_name` results in nothing being /// written. Otherwise, the label pairs are written, escaped as required by the /// text format, and enclosed in '{...}'. The function returns the number of /// bytes written and any error encountered. fn label_pairs_to_text( pairs: &[proto::LabelPair], additional_label: Option<(&str, &str)>, writer: &mut dyn WriteUtf8, ) -> Result<()> { if pairs.is_empty() && additional_label.is_none() { return Ok(()); } let mut separator = "{"; for lp in pairs { writer.write_all(separator)?; writer.write_all(lp.get_name())?; writer.write_all("=\"")?; writer.write_all(&escape_string(lp.get_value(), true))?; writer.write_all("\"")?; separator = ","; } if let Some((name, value)) = additional_label { writer.write_all(separator)?; writer.write_all(name)?; writer.write_all("=\"")?; writer.write_all(&escape_string(value, true))?; writer.write_all("\"")?; } writer.write_all("}")?; Ok(()) } fn find_first_occurence(v: &str, include_double_quote: bool) -> Option { if include_double_quote { memchr::memchr3(b'\\', b'\n', b'\"', v.as_bytes()) } else { memchr::memchr2(b'\\', b'\n', v.as_bytes()) } } /// `escape_string` replaces `\` by `\\`, new line character by `\n`, and `"` by `\"` if /// `include_double_quote` is true. /// /// Implementation adapted from /// https://lise-henry.github.io/articles/optimising_strings.html fn escape_string(v: &str, include_double_quote: bool) -> Cow<'_, str> { let first_occurence = find_first_occurence(v, include_double_quote); if let Some(first) = first_occurence { let mut escaped = String::with_capacity(v.len() * 2); escaped.push_str(&v[0..first]); let remainder = v[first..].chars(); for c in remainder { match c { '\\' | '\n' => { escaped.extend(c.escape_default()); } '"' if include_double_quote => { escaped.extend(c.escape_default()); } _ => { escaped.push(c); } } } escaped.shrink_to_fit(); escaped.into() } else { // The input string does not contain any characters that would need to // be escaped. Return it as it is. v.into() } } trait WriteUtf8 { fn write_all(&mut self, text: &str) -> io::Result<()>; } impl 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 { use super::*; use crate::counter::Counter; use crate::gauge::Gauge; use crate::histogram::{Histogram, HistogramOpts}; use crate::metrics::{Collector, Opts}; #[test] fn test_escape_string() { assert_eq!(r"\\", escape_string("\\", false)); assert_eq!(r"a\\", escape_string("a\\", false)); assert_eq!(r"\n", escape_string("\n", false)); assert_eq!(r"a\n", escape_string("a\n", false)); assert_eq!(r"\\n", escape_string("\\n", false)); assert_eq!(r##"\\n\""##, escape_string("\\n\"", true)); assert_eq!(r##"\\\n\""##, escape_string("\\\n\"", true)); assert_eq!(r##"\\\\n\""##, escape_string("\\\\n\"", true)); assert_eq!(r##"\"\\n\""##, escape_string("\"\\n\"", true)); } #[test] fn test_text_encoder() { 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 mut writer = Vec::::new(); let encoder = TextEncoder::new(); let txt = encoder.encode(&mf, &mut writer); assert!(txt.is_ok()); let counter_ans = r##"# HELP test_counter test help # TYPE test_counter counter test_counter{a="1",b="2"} 1 "##; assert_eq!(counter_ans.as_bytes(), writer.as_slice()); let gauge_opts = Opts::new("test_gauge", "test help") .const_label("a", "1") .const_label("b", "2"); let gauge = Gauge::with_opts(gauge_opts).unwrap(); gauge.inc(); gauge.set(42.0); let mf = gauge.collect(); writer.clear(); let txt = encoder.encode(&mf, &mut writer); assert!(txt.is_ok()); let gauge_ans = r##"# HELP test_gauge test help # TYPE test_gauge gauge test_gauge{a="1",b="2"} 42 "##; assert_eq!(gauge_ans.as_bytes(), writer.as_slice()); } #[test] fn test_text_encoder_histogram() { let opts = HistogramOpts::new("test_histogram", "test help").const_label("a", "1"); let histogram = Histogram::with_opts(opts).unwrap(); histogram.observe(0.25); let mf = histogram.collect(); let mut writer = Vec::::new(); let encoder = TextEncoder::new(); let res = encoder.encode(&mf, &mut writer); assert!(res.is_ok()); let ans = r##"# HELP test_histogram test help # TYPE test_histogram histogram test_histogram_bucket{a="1",le="0.005"} 0 test_histogram_bucket{a="1",le="0.01"} 0 test_histogram_bucket{a="1",le="0.025"} 0 test_histogram_bucket{a="1",le="0.05"} 0 test_histogram_bucket{a="1",le="0.1"} 0 test_histogram_bucket{a="1",le="0.25"} 1 test_histogram_bucket{a="1",le="0.5"} 1 test_histogram_bucket{a="1",le="1"} 1 test_histogram_bucket{a="1",le="2.5"} 1 test_histogram_bucket{a="1",le="5"} 1 test_histogram_bucket{a="1",le="10"} 1 test_histogram_bucket{a="1",le="+Inf"} 1 test_histogram_sum{a="1"} 0.25 test_histogram_count{a="1"} 1 "##; assert_eq!(ans.as_bytes(), writer.as_slice()); } #[test] fn test_text_encoder_summary() { use crate::proto::{Metric, Quantile, Summary}; use std::str; let mut metric_family = MetricFamily::default(); metric_family.set_name("test_summary".to_string()); metric_family.set_help("This is a test summary statistic".to_string()); metric_family.set_field_type(MetricType::SUMMARY); let mut summary = Summary::default(); summary.set_sample_count(5.0 as u64); summary.set_sample_sum(15.0); let mut quantile1 = Quantile::default(); quantile1.set_quantile(50.0); quantile1.set_value(3.0); let mut quantile2 = Quantile::default(); quantile2.set_quantile(100.0); quantile2.set_value(5.0); summary.set_quantile(from_vec!(vec!(quantile1, quantile2))); let mut metric = Metric::default(); metric.set_summary(summary); metric_family.set_metric(from_vec!(vec!(metric))); let mut writer = Vec::::new(); let encoder = TextEncoder::new(); let res = encoder.encode(&vec![metric_family], &mut writer); assert!(res.is_ok()); let ans = r##"# HELP test_summary This is a test summary statistic # TYPE test_summary summary test_summary{quantile="50"} 3 test_summary{quantile="100"} 5 test_summary_sum 15 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()); } } prometheus-0.13.4/src/errors.rs000064400000000000000000000021341046102023000145550ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use thiserror::Error; /// The error types for prometheus. #[derive(Debug, Error)] pub enum Error { /// A duplicate metric collector has already been registered. #[error("Duplicate metrics collector registration attempted")] AlreadyReg, /// The label cardinality was inconsistent. #[error("Inconsistent label cardinality, expect {expect} label values, but got {got}")] InconsistentCardinality { /// The expected number of labels. expect: usize, /// The actual number of labels. got: usize, }, /// An error message which is only a string. #[error("Error: {0}")] Msg(String), /// An error containing a [`std::io::Error`]. #[error("Io error: {0}")] Io(#[from] std::io::Error), /// An error containing a [`protobuf::error::ProtobufError`]. #[cfg(feature = "protobuf")] #[error("Protobuf error: {0}")] Protobuf(#[from] protobuf::error::ProtobufError), } /// A specialized Result type for prometheus. pub type Result = std::result::Result; prometheus-0.13.4/src/gauge.rs000064400000000000000000000155061046102023000143400ustar 00000000000000// Copyright 2014 The Prometheus Authors // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::marker::PhantomData; use std::sync::Arc; use crate::atomic64::{Atomic, AtomicF64, AtomicI64, Number}; use crate::desc::Desc; use crate::errors::Result; use crate::metrics::{Collector, Metric, Opts}; use crate::proto; use crate::value::{Value, ValueType}; use crate::vec::{MetricVec, MetricVecBuilder}; /// The underlying implementation for [`Gauge`] and [`IntGauge`]. #[derive(Debug)] pub struct GenericGauge { v: Arc>, } /// A [`Metric`] represents a single numerical value that can arbitrarily go up /// and down. pub type Gauge = GenericGauge; /// The integer version of [`Gauge`]. Provides better performance if metric values are /// all integers. pub type IntGauge = GenericGauge; impl Clone for GenericGauge

{ fn clone(&self) -> Self { Self { v: Arc::clone(&self.v), } } } impl GenericGauge

{ /// Create a [`GenericGauge`] with the `name` and `help` arguments. pub fn new, S2: Into>(name: S1, help: S2) -> Result { let opts = Opts::new(name, help); Self::with_opts(opts) } /// Create a [`GenericGauge`] with the `opts` options. pub fn with_opts(opts: Opts) -> Result { Self::with_opts_and_label_values(&opts, &[]) } fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result { let v = Value::new(opts, ValueType::Gauge, P::T::from_i64(0), label_values)?; Ok(Self { v: Arc::new(v) }) } /// Set the gauge to an arbitrary value. #[inline] pub fn set(&self, v: P::T) { self.v.set(v); } /// Increase the gauge by 1. #[inline] pub fn inc(&self) { self.v.inc(); } /// Decrease the gauge by 1. #[inline] pub fn dec(&self) { self.v.dec(); } /// Add the given value to the gauge. (The value can be /// negative, resulting in a decrement of the gauge.) #[inline] pub fn add(&self, v: P::T) { self.v.inc_by(v); } /// Subtract the given value from the gauge. (The value can be /// negative, resulting in an increment of the gauge.) #[inline] pub fn sub(&self, v: P::T) { self.v.dec_by(v); } /// Return the gauge value. #[inline] pub fn get(&self) -> P::T { self.v.get() } } impl Collector for GenericGauge

{ fn desc(&self) -> Vec<&Desc> { vec![&self.v.desc] } fn collect(&self) -> Vec { vec![self.v.collect()] } } impl Metric for GenericGauge

{ fn metric(&self) -> proto::Metric { self.v.metric() } } #[derive(Debug)] pub struct GaugeVecBuilder { _phantom: PhantomData

, } impl GaugeVecBuilder

{ pub fn new() -> Self { Self { _phantom: PhantomData, } } } impl Clone for GaugeVecBuilder

{ fn clone(&self) -> Self { Self::new() } } impl MetricVecBuilder for GaugeVecBuilder

{ type M = GenericGauge

; type P = Opts; fn build(&self, opts: &Opts, vals: &[&str]) -> Result { Self::M::with_opts_and_label_values(opts, vals) } } /// The underlying implementation for [`GaugeVec`] and [`IntGaugeVec`]. pub type GenericGaugeVec

= MetricVec>; /// A [`Collector`] that bundles a set of [`Gauge`]s that all share /// the same [`Desc`], but have different values for their variable labels. This is /// used if you want to count the same thing partitioned by various dimensions /// (e.g. number of operations queued, partitioned by user and operation type). pub type GaugeVec = GenericGaugeVec; /// The integer version of [`GaugeVec`]. Provides better performance if metric values /// are all integers. pub type IntGaugeVec = GenericGaugeVec; impl GenericGaugeVec

{ /// Create a new [`GenericGaugeVec`] based on the provided /// [`Opts`] and partitioned by the given label names. At least one label name must /// be provided. pub fn new(opts: Opts, label_names: &[&str]) -> Result { let variable_names = label_names.iter().map(|s| (*s).to_owned()).collect(); let opts = opts.variable_labels(variable_names); let metric_vec = MetricVec::create(proto::MetricType::GAUGE, GaugeVecBuilder::new(), opts)?; Ok(metric_vec as Self) } } #[cfg(test)] mod tests { use std::collections::HashMap; use super::*; use crate::metrics::{Collector, Opts}; #[test] fn test_gauge() { let opts = Opts::new("test", "test help") .const_label("a", "1") .const_label("b", "2"); let gauge = Gauge::with_opts(opts).unwrap(); gauge.inc(); assert_eq!(gauge.get() as u64, 1); gauge.add(42.0); assert_eq!(gauge.get() as u64, 43); gauge.sub(42.0); assert_eq!(gauge.get() as u64, 1); gauge.dec(); assert_eq!(gauge.get() as u64, 0); gauge.set(42.0); assert_eq!(gauge.get() as u64, 42); let mut mfs = gauge.collect(); assert_eq!(mfs.len(), 1); let mf = mfs.pop().unwrap(); let m = mf.get_metric().get(0).unwrap(); assert_eq!(m.get_label().len(), 2); assert_eq!(m.get_gauge().get_value() as u64, 42); } #[test] fn test_gauge_vec_with_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( Opts::new("test_gauge_vec", "test gauge vec help"), &["l1", "l2"], ) .unwrap(); assert!(vec.remove_label_values(&["v1", "v2"]).is_err()); vec.with_label_values(&["v1", "v2"]).inc(); assert!(vec.remove_label_values(&["v1", "v2"]).is_ok()); vec.with_label_values(&["v1", "v2"]).inc(); vec.with_label_values(&["v1", "v2"]).dec(); vec.with_label_values(&["v1", "v2"]).add(42.0); vec.with_label_values(&["v1", "v2"]).sub(42.0); vec.with_label_values(&["v1", "v2"]).set(42.0); assert!(vec.remove_label_values(&["v1"]).is_err()); assert!(vec.remove_label_values(&["v1", "v3"]).is_err()); } } prometheus-0.13.4/src/histogram.rs000064400000000000000000001423131046102023000152420ustar 00000000000000// Copyright 2014 The Prometheus Authors // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::cell::RefCell; use std::collections::HashMap; use std::convert::From; use std::sync::{ atomic::{AtomicU64 as StdAtomicU64, Ordering}, Arc, Mutex, }; use std::time::{Duration, Instant as StdInstant}; use crate::atomic64::{Atomic, AtomicF64, AtomicU64}; use crate::desc::{Desc, Describer}; use crate::errors::{Error, Result}; use crate::metrics::{Collector, LocalMetric, Metric, Opts}; use crate::proto; use crate::value::make_label_pairs; use crate::vec::{MetricVec, MetricVecBuilder}; /// The default [`Histogram`] buckets. The default buckets are /// tailored to broadly measure the response time (in seconds) of a /// network service. Most likely, however, you will be required to define /// buckets customized to your use case. pub const DEFAULT_BUCKETS: &[f64; 11] = &[ 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, ]; /// Used for the label that defines the upper bound of a /// bucket of a histogram ("le" -> "less or equal"). pub const BUCKET_LABEL: &str = "le"; #[inline] fn check_bucket_label(label: &str) -> Result<()> { if label == BUCKET_LABEL { return Err(Error::Msg( "`le` is not allowed as label name in histograms".to_owned(), )); } Ok(()) } fn check_and_adjust_buckets(mut buckets: Vec) -> Result> { if buckets.is_empty() { buckets = Vec::from(DEFAULT_BUCKETS as &'static [f64]); } for (i, upper_bound) in buckets.iter().enumerate() { if i < (buckets.len() - 1) && *upper_bound >= buckets[i + 1] { return Err(Error::Msg(format!( "histogram buckets must be in increasing \ order: {} >= {}", upper_bound, buckets[i + 1] ))); } } let tail = *buckets.last().unwrap(); if tail.is_sign_positive() && tail.is_infinite() { // The +Inf bucket is implicit. Remove it here. buckets.pop(); } Ok(buckets) } /// A struct that bundles the options for creating a [`Histogram`] metric. It is /// mandatory to set Name and Help to a non-empty string. All other fields are /// optional and can safely be left at their zero value. #[derive(Clone, Debug)] pub struct HistogramOpts { /// A container holding various options. pub common_opts: Opts, /// Defines the buckets into which observations are counted. Each /// element in the slice is the upper inclusive bound of a bucket. The /// values must be sorted in strictly increasing order. There is no need /// to add a highest bucket with +Inf bound, it will be added /// implicitly. The default value is DefBuckets. pub buckets: Vec, } impl HistogramOpts { /// Create a [`HistogramOpts`] with the `name` and `help` arguments. pub fn new, S2: Into>(name: S1, help: S2) -> HistogramOpts { HistogramOpts { common_opts: Opts::new(name, help), buckets: Vec::from(DEFAULT_BUCKETS as &'static [f64]), } } /// `namespace` sets the namespace. pub fn namespace>(mut self, namespace: S) -> Self { self.common_opts.namespace = namespace.into(); self } /// `subsystem` sets the sub system. pub fn subsystem>(mut self, subsystem: S) -> Self { self.common_opts.subsystem = subsystem.into(); self } /// `const_labels` sets the const labels. pub fn const_labels(mut self, const_labels: HashMap) -> Self { self.common_opts = self.common_opts.const_labels(const_labels); self } /// `const_label` adds a const label. pub fn const_label, S2: Into>(mut self, name: S1, value: S2) -> Self { self.common_opts = self.common_opts.const_label(name, value); self } /// `variable_labels` sets the variable labels. pub fn variable_labels(mut self, variable_labels: Vec) -> Self { self.common_opts = self.common_opts.variable_labels(variable_labels); self } /// `variable_label` adds a variable label. pub fn variable_label>(mut self, name: S) -> Self { self.common_opts = self.common_opts.variable_label(name); self } /// `fq_name` returns the fq_name. pub fn fq_name(&self) -> String { self.common_opts.fq_name() } /// `buckets` set the buckets. pub fn buckets(mut self, buckets: Vec) -> Self { self.buckets = buckets; self } } impl Describer for HistogramOpts { fn describe(&self) -> Result { self.common_opts.describe() } } impl From for HistogramOpts { fn from(opts: Opts) -> HistogramOpts { HistogramOpts { common_opts: opts, buckets: Vec::from(DEFAULT_BUCKETS as &'static [f64]), } } } /// Representation of a hot or cold shard. /// /// See [`HistogramCore`] for details. #[derive(Debug)] struct Shard { sum: AtomicF64, count: AtomicU64, buckets: Vec, } impl Shard { fn new(num_buckets: usize) -> Self { let mut buckets = Vec::new(); for _ in 0..num_buckets { buckets.push(AtomicU64::new(0)); } Shard { sum: AtomicF64::new(0.0), count: AtomicU64::new(0), buckets, } } } /// Index into an array of [`Shard`]s. /// /// Used in conjunction with [`ShardAndCount`] below. #[derive(Debug, Clone, Copy)] enum ShardIndex { /// First index. Corresponds to 0. First, /// Second index. Corresponds to 1. Second, } impl ShardIndex { /// Inverse the given [`ShardIndex`]. fn inverse(self) -> ShardIndex { match self { ShardIndex::First => ShardIndex::Second, ShardIndex::Second => ShardIndex::First, } } } impl From for ShardIndex { fn from(index: u64) -> Self { match index { 0 => ShardIndex::First, 1 => ShardIndex::Second, _ => panic!( "Invalid shard index {:?}. A histogram only has two shards.", index ), } } } impl From for usize { fn from(index: ShardIndex) -> Self { match index { ShardIndex::First => 0, ShardIndex::Second => 1, } } } /// An atomic u64 with the most significant used as a [`ShardIndex`] and the /// remaining 63 bits used to count [`Histogram`] observations. #[derive(Debug)] struct ShardAndCount { inner: StdAtomicU64, } impl ShardAndCount { /// Return a new [`ShardAndCount`] with both the most significant bit /// i.e. the `ShardIndex` and the remaining 63 bit i.e. the observation /// count set to 0. fn new() -> Self { ShardAndCount { inner: StdAtomicU64::new(0), } } /// Flip the most significant bit i.e. the [`ShardIndex`] leaving the /// remaining 63 bits unchanged. fn flip(&self, ordering: Ordering) -> (ShardIndex, u64) { let n = self.inner.fetch_add(1 << 63, ordering); ShardAndCount::split_shard_index_and_count(n) } /// Get the most significant bit i.e. the [`ShardIndex`] as well as the /// remaining 63 bits i.e. the observation count. fn get(&self) -> (ShardIndex, u64) { let n = self.inner.load(Ordering::Relaxed); ShardAndCount::split_shard_index_and_count(n) } /// Increment the observation count leaving the most significant bit i.e. /// the [`ShardIndex`] untouched. fn inc_by(&self, delta: u64, ordering: Ordering) -> (ShardIndex, u64) { let n = self.inner.fetch_add(delta, ordering); ShardAndCount::split_shard_index_and_count(n) } /// Increment the observation count by one leaving the most significant bit /// i.e. the [`ShardIndex`] untouched. fn inc(&self, ordering: Ordering) -> (ShardIndex, u64) { self.inc_by(1, ordering) } fn split_shard_index_and_count(n: u64) -> (ShardIndex, u64) { let shard = n >> 63; let count = n & ((1 << 63) - 1); (shard.into(), count) } } /// Core datastructure of a Prometheus histogram /// /// # Atomicity across collects /// /// 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. /// /// 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. /// /// If an observe and a collect operation interleave, the latter could be /// exposing a snapshot of the histogram that does not uphold all histogram /// invariants. For example for the invariant that the overall observation /// counter should equal the sum of all bucket counters: Say that an `observe` /// increases the overall counter but before updating a specific bucket counter /// a collect operation snapshots the histogram. /// /// The below implementation of `HistogramCore` prevents such race conditions by /// using two shards, one hot shard for `observe` operations to record their /// observation and one cold shard for collect operations to collect a /// consistent snapshot of the histogram. /// /// `observe` operations hit the hot shard and record their observation. Collect /// operations switch hot and cold, wait for all `observe` calls to finish on /// the previously hot now cold shard and then expose the consistent snapshot. #[derive(Debug)] pub struct HistogramCore { desc: Desc, label_pairs: Vec, /// Mutual exclusion to serialize collect operations. No two collect /// operations should operate on this datastructure at the same time. (See /// struct documentation for details.) `observe` operations can operate in /// parallel without holding this lock. collect_lock: Mutex<()>, /// An atomic u64 where the first bit determines the currently hot shard and /// the remaining 63 bits determine the overall count. shard_and_count: ShardAndCount, /// The two shards where `shard_and_count` determines which one is the hot /// and which one the cold at any given point in time. shards: [Shard; 2], upper_bounds: Vec, } impl HistogramCore { pub fn new(opts: &HistogramOpts, label_values: &[&str]) -> Result { 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())?; } let label_pairs = make_label_pairs(&desc, label_values)?; let buckets = check_and_adjust_buckets(opts.buckets.clone())?; Ok(HistogramCore { desc, label_pairs, collect_lock: Mutex::new(()), shard_and_count: ShardAndCount::new(), shards: [Shard::new(buckets.len()), Shard::new(buckets.len())], upper_bounds: buckets, }) } /// Record a given observation (f64) in the histogram. // // First increase the overall observation counter and thus learn which shard // is the current hot shard. Subsequently on the hot shard update the // corresponding bucket count, adjust the shard's sum and finally increase // the shard's count. pub fn observe(&self, v: f64) { // The collect code path uses `self.shard_and_count` and // `self.shards[x].count` to ensure not to collect data from a shard // while observe calls are still operating on it. // // To ensure the above, this `inc` needs to use `Acquire` ordering to // force anything below this line to stay below it. let (shard_index, _count) = self.shard_and_count.inc(Ordering::Acquire); let shard: &Shard = &self.shards[usize::from(shard_index)]; // Try find the bucket. let mut iter = self .upper_bounds .iter() .enumerate() .filter(|&(_, f)| v <= *f); if let Some((i, _)) = iter.next() { shard.buckets[i].inc_by(1); } shard.sum.inc_by(v); // Use `Release` ordering to ensure all operations above stay above. shard.count.inc_by_with_ordering(1, Ordering::Release); } /// Make a snapshot of the current histogram state exposed as a Protobuf /// struct. // // Acquire the collect lock, switch the hot and the cold shard, wait for all // remaining `observe` calls to finish on the previously hot now cold shard, // snapshot the data, update the now hot shard and reset the cold shard. pub fn proto(&self) -> proto::Histogram { let collect_guard = self.collect_lock.lock().expect("Lock poisoned"); // `flip` needs to use AcqRel ordering to ensure the lock operation // above stays above and the histogram operations (especially the shard // resets) below stay below. let (cold_shard_index, overall_count) = self.shard_and_count.flip(Ordering::AcqRel); let cold_shard = &self.shards[usize::from(cold_shard_index)]; let hot_shard = &self.shards[usize::from(cold_shard_index.inverse())]; // Wait for all currently active `observe` calls on the now cold shard // to finish. The above call to `flip` redirects all future `observe` // calls to the other previously cold, now hot, shard. Thus once the // cold shard counter equals the value of the global counter when the // shards were flipped, all in-progress `observe` calls are done. With // all of them done, the cold shard is now in a consistent state. // // `observe` uses `Release` ordering. `compare_exchange` needs to use // `Acquire` ordering to ensure that (1) one sees all the previous // `observe` stores to the counter and (2) to ensure the below shard // modifications happen after this point, thus the shard is not modified // by any `observe` operations. while cold_shard .count .compare_exchange_weak( overall_count, // While at it, reset cold shard count on success. 0, Ordering::Acquire, Ordering::Acquire, ) .is_err() {} // Get cold shard sum and reset to 0. // // Use `Acquire` for load and `Release` for store to ensure not to // interfere with previous or upcoming collect calls. let cold_shard_sum = cold_shard.sum.swap(0.0, Ordering::AcqRel); let mut h = proto::Histogram::default(); h.set_sample_sum(cold_shard_sum); h.set_sample_count(overall_count); let mut cumulative_count = 0; let mut buckets = Vec::with_capacity(self.upper_bounds.len()); for (i, upper_bound) in self.upper_bounds.iter().enumerate() { // Reset the cold shard and update the hot shard. // // Use `Acquire` for load and `Release` for store to ensure not to // interfere with previous or upcoming collect calls. let cold_bucket_count = cold_shard.buckets[i].swap(0, Ordering::AcqRel); hot_shard.buckets[i].inc_by(cold_bucket_count); cumulative_count += cold_bucket_count; let mut b = proto::Bucket::default(); b.set_cumulative_count(cumulative_count); b.set_upper_bound(*upper_bound); buckets.push(b); } h.set_bucket(from_vec!(buckets)); // Update the hot shard. hot_shard.count.inc_by(overall_count); hot_shard.sum.inc_by(cold_shard_sum); drop(collect_guard); h } fn sample_sum(&self) -> f64 { // Make sure to not overlap with any collect calls, as they might flip // the hot and cold shards. let _guard = self.collect_lock.lock().expect("Lock poisoned"); let (shard_index, _count) = self.shard_and_count.get(); self.shards[shard_index as usize].sum.get() } fn sample_count(&self) -> u64 { self.shard_and_count.get().1 } } // We have to wrap libc::timespec in order to implement std::fmt::Debug. #[cfg(all(feature = "nightly", target_os = "linux"))] pub struct Timespec(libc::timespec); #[cfg(all(feature = "nightly", target_os = "linux"))] impl std::fmt::Debug for Timespec { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "Timespec {{ tv_sec: {}, tv_nsec: {} }}", self.0.tv_sec, self.0.tv_nsec ) } } #[derive(Debug)] pub enum Instant { Monotonic(StdInstant), #[cfg(all(feature = "nightly", target_os = "linux"))] MonotonicCoarse(Timespec), } impl Instant { pub fn now() -> Instant { Instant::Monotonic(StdInstant::now()) } #[cfg(all(feature = "nightly", target_os = "linux"))] pub fn now_coarse() -> Instant { Instant::MonotonicCoarse(get_time_coarse()) } #[cfg(all(feature = "nightly", not(target_os = "linux")))] pub fn now_coarse() -> Instant { Instant::Monotonic(StdInstant::now()) } pub fn elapsed(&self) -> Duration { 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 // and therefore the timer registers are typically running at an offset. // Use millisecond resolution for ignoring the error. // See more: https://linux.die.net/man/2/clock_gettime #[cfg(all(feature = "nightly", target_os = "linux"))] Instant::MonotonicCoarse(t) => { let now = get_time_coarse(); let now_ms = now.0.tv_sec * MILLIS_PER_SEC + now.0.tv_nsec / NANOS_PER_MILLI; let t_ms = t.0.tv_sec * MILLIS_PER_SEC + t.0.tv_nsec / NANOS_PER_MILLI; let dur = now_ms - t_ms; if dur >= 0 { Duration::from_millis(dur as u64) } else { Duration::from_millis(0) } } } } #[inline] pub fn elapsed_sec(&self) -> f64 { duration_to_seconds(self.elapsed()) } } #[cfg(all(feature = "nightly", target_os = "linux"))] use self::coarse::*; #[cfg(all(feature = "nightly", target_os = "linux"))] mod coarse { use crate::histogram::Timespec; pub use libc::timespec; use libc::{clock_gettime, CLOCK_MONOTONIC_COARSE}; pub const NANOS_PER_MILLI: i64 = 1_000_000; pub const MILLIS_PER_SEC: i64 = 1_000; pub fn get_time_coarse() -> Timespec { let mut t = Timespec(timespec { tv_sec: 0, tv_nsec: 0, }); assert_eq!( unsafe { clock_gettime(CLOCK_MONOTONIC_COARSE, &mut t.0) }, 0 ); t } } /// Timer to measure and record the duration of an event. /// /// This timer can be stopped and observed at most once, either automatically (when it /// goes out of scope) or manually. /// Alternatively, it can be manually stopped and discarded in order to not record its value. #[must_use = "Timer should be kept in a variable otherwise it cannot observe duration"] #[derive(Debug)] pub struct HistogramTimer { /// A histogram for automatic recording of observations. histogram: Histogram, /// Whether the timer has already been observed once. observed: bool, /// Starting instant for the timer. start: Instant, } impl HistogramTimer { fn new(histogram: Histogram) -> Self { Self { histogram, observed: false, start: Instant::now(), } } #[cfg(feature = "nightly")] fn new_coarse(histogram: Histogram) -> Self { HistogramTimer { histogram, observed: false, start: Instant::now_coarse(), } } /// Observe and record timer duration (in seconds). /// /// It observes the floating-point number of seconds elapsed since the timer /// started, and it records that value to the attached histogram. pub fn observe_duration(self) { self.stop_and_record(); } /// Observe, record and return timer duration (in seconds). /// /// It observes and returns a floating-point number for seconds elapsed since /// the timer started, recording that value to the attached histogram. pub fn stop_and_record(self) -> f64 { let mut timer = self; timer.observe(true) } /// Observe and return timer duration (in seconds). /// /// It returns a floating-point number of seconds elapsed since the timer started, /// without recording to any histogram. pub fn stop_and_discard(self) -> f64 { let mut timer = self; timer.observe(false) } fn observe(&mut self, record: bool) -> f64 { let v = self.start.elapsed_sec(); self.observed = true; if record { self.histogram.observe(v); } v } } impl Drop for HistogramTimer { fn drop(&mut self) { if !self.observed { self.observe(true); } } } /// A [`Metric`] counts individual observations from an event or sample stream /// in configurable buckets. Similar to a [`Summary`](crate::proto::Summary), /// it also provides a sum of observations and an observation count. /// /// On the Prometheus server, quantiles can be calculated from a [`Histogram`] using /// the [`histogram_quantile`][1] function in the query language. /// /// Note that Histograms, in contrast to Summaries, can be aggregated with the /// Prometheus query language (see [the prometheus documentation][2] for /// detailed procedures). However, Histograms require the user to pre-define /// suitable buckets, (see [`linear_buckets`] and [`exponential_buckets`] for /// some helper provided here) and they are in general less accurate. The /// Observe method of a [`Histogram`] has a very low performance overhead in /// comparison with the Observe method of a Summary. /// /// [1]: https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile /// [2]: https://prometheus.io/docs/practices/histograms/ #[derive(Clone, Debug)] pub struct Histogram { core: Arc, } impl Histogram { /// `with_opts` creates a [`Histogram`] with the `opts` options. pub fn with_opts(opts: HistogramOpts) -> Result { Histogram::with_opts_and_label_values(&opts, &[]) } fn with_opts_and_label_values( opts: &HistogramOpts, label_values: &[&str], ) -> Result { let core = HistogramCore::new(opts, label_values)?; Ok(Histogram { core: Arc::new(core), }) } } impl Histogram { /// Add a single observation to the [`Histogram`]. pub fn observe(&self, v: f64) { self.core.observe(v) } /// Return a [`HistogramTimer`] to track a duration. pub fn start_timer(&self) -> HistogramTimer { HistogramTimer::new(self.clone()) } /// Return a [`HistogramTimer`] to track a duration. /// It is faster but less precise. #[cfg(feature = "nightly")] pub fn start_coarse_timer(&self) -> HistogramTimer { HistogramTimer::new_coarse(self.clone()) } /// Observe execution time of a closure, in second. pub fn observe_closure_duration(&self, f: F) -> T where F: FnOnce() -> T, { let instant = Instant::now(); let res = f(); let elapsed = instant.elapsed_sec(); self.observe(elapsed); res } /// Observe execution time of a closure, in second. #[cfg(feature = "nightly")] pub fn observe_closure_duration_coarse(&self, f: F) -> T where F: FnOnce() -> T, { let instant = Instant::now_coarse(); let res = f(); let elapsed = instant.elapsed_sec(); self.observe(elapsed); res } /// Return a [`LocalHistogram`] for single thread usage. pub fn local(&self) -> LocalHistogram { LocalHistogram::new(self.clone()) } /// Return accumulated sum of all samples. pub fn get_sample_sum(&self) -> f64 { self.core.sample_sum() } /// Return count of all samples. pub fn get_sample_count(&self) -> u64 { self.core.sample_count() } } 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 h = self.core.proto(); m.set_histogram(h); m } } impl Collector for Histogram { fn desc(&self) -> Vec<&Desc> { vec![&self.core.desc] } fn collect(&self) -> Vec { let mut m = proto::MetricFamily::default(); 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()])); vec![m] } } #[derive(Clone, Debug)] pub struct HistogramVecBuilder {} impl MetricVecBuilder for HistogramVecBuilder { type M = Histogram; type P = HistogramOpts; fn build(&self, opts: &HistogramOpts, vals: &[&str]) -> Result { Histogram::with_opts_and_label_values(opts, vals) } } /// A [`Collector`] that bundles a set of Histograms that all share the /// same [`Desc`], but have different values for their variable labels. This is used /// if you want to count the same thing partitioned by various dimensions /// (e.g. HTTP request latencies, partitioned by status code and method). pub type HistogramVec = MetricVec; impl HistogramVec { /// Create a new [`HistogramVec`] based on the provided /// [`HistogramOpts`] and partitioned by the given label names. At least /// one label name must be provided. pub fn new(opts: HistogramOpts, label_names: &[&str]) -> Result { let variable_names = label_names.iter().map(|s| (*s).to_owned()).collect(); let opts = opts.variable_labels(variable_names); let metric_vec = MetricVec::create(proto::MetricType::HISTOGRAM, HistogramVecBuilder {}, opts)?; Ok(metric_vec as HistogramVec) } /// Return a `LocalHistogramVec` for single thread usage. pub fn local(&self) -> LocalHistogramVec { let vec = self.clone(); LocalHistogramVec::new(vec) } } /// Create `count` buckets, each `width` wide, where the lowest /// bucket has an upper bound of `start`. The final +Inf bucket is not counted /// and not included in the returned slice. The returned slice is meant to be /// used for the Buckets field of [`HistogramOpts`]. /// /// The function returns an error if `count` is zero or `width` is zero or /// negative. pub fn linear_buckets(start: f64, width: f64, count: usize) -> Result> { if count < 1 { return Err(Error::Msg(format!( "LinearBuckets needs a positive count, count: {}", count ))); } if width <= 0.0 { return Err(Error::Msg(format!( "LinearBuckets needs a width greater then 0, width: {}", width ))); } let buckets: Vec<_> = (0..count) .map(|step| start + width * (step as f64)) .collect(); Ok(buckets) } /// Create `count` buckets, where the lowest bucket has an /// upper bound of `start` and each following bucket's upper bound is `factor` /// times the previous bucket's upper bound. The final +Inf bucket is not counted /// and not included in the returned slice. The returned slice is meant to be /// used for the Buckets field of [`HistogramOpts`]. /// /// The function returns an error if `count` is zero, if `start` is zero or /// negative, or if `factor` is less than or equal 1. pub fn exponential_buckets(start: f64, factor: f64, count: usize) -> Result> { if count < 1 { return Err(Error::Msg(format!( "exponential_buckets needs a positive count, count: {}", count ))); } if start <= 0.0 { return Err(Error::Msg(format!( "exponential_buckets needs a positive start value, \ start: {}", start ))); } if factor <= 1.0 { return Err(Error::Msg(format!( "exponential_buckets needs a factor greater than 1, \ factor: {}", factor ))); } let mut next = start; let mut buckets = Vec::with_capacity(count); for _ in 0..count { buckets.push(next); next *= factor; } 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, counts: Vec, count: u64, sum: f64, } /// An unsync [`Histogram`]. #[derive(Debug)] pub struct LocalHistogram { core: RefCell, } impl Clone for LocalHistogram { fn clone(&self) -> LocalHistogram { let core = self.core.clone(); let lh = LocalHistogram { core }; lh.clear(); lh } } /// An unsync [`HistogramTimer`]. #[must_use = "Timer should be kept in a variable otherwise it cannot observe duration"] #[derive(Debug)] pub struct LocalHistogramTimer { /// A local histogram for automatic recording of observations. local: LocalHistogram, /// Whether the timer has already been observed once. observed: bool, /// Starting instant for the timer. start: Instant, } impl LocalHistogramTimer { fn new(histogram: LocalHistogram) -> Self { Self { local: histogram, observed: false, start: Instant::now(), } } #[cfg(feature = "nightly")] fn new_coarse(histogram: LocalHistogram) -> Self { Self { local: histogram, observed: false, start: Instant::now_coarse(), } } /// Observe and record timer duration (in seconds). /// /// It observes the floating-point number of seconds elapsed since the timer /// started, and it records that value to the attached histogram. pub fn observe_duration(self) { self.stop_and_record(); } /// Observe, record and return timer duration (in seconds). /// /// It observes and returns a floating-point number for seconds elapsed since /// the timer started, recording that value to the attached histogram. pub fn stop_and_record(self) -> f64 { let mut timer = self; timer.observe(true) } /// Observe and return timer duration (in seconds). /// /// It returns a floating-point number of seconds elapsed since the timer started, /// without recording to any histogram. pub fn stop_and_discard(self) -> f64 { let mut timer = self; timer.observe(false) } fn observe(&mut self, record: bool) -> f64 { let v = self.start.elapsed_sec(); self.observed = true; if record { self.local.observe(v); } v } } impl Drop for LocalHistogramTimer { fn drop(&mut self) { if !self.observed { self.observe(true); } } } impl LocalHistogramCore { fn new(histogram: Histogram) -> LocalHistogramCore { let counts = vec![0; histogram.core.upper_bounds.len()]; LocalHistogramCore { histogram, counts, count: 0, sum: 0.0, } } pub fn observe(&mut self, v: f64) { // Try find the bucket. let mut iter = self .histogram .core .upper_bounds .iter() .enumerate() .filter(|&(_, f)| v <= *f); if let Some((i, _)) = iter.next() { self.counts[i] += 1; } self.count += 1; self.sum += v; } pub fn clear(&mut self) { for v in &mut self.counts { *v = 0 } self.count = 0; self.sum = 0.0; } pub fn flush(&mut self) { // No cached metric, return. if self.count == 0 { return; } { // The collect code path uses `self.shard_and_count` and // `self.shards[x].count` to ensure not to collect data from a shard // while observe calls are still operating on it. // // To ensure the above, this `inc` needs to use `Acquire` ordering // to force anything below this line to stay below it. let (shard_index, _count) = self .histogram .core .shard_and_count .inc_by(self.count, Ordering::Acquire); let shard = &self.histogram.core.shards[shard_index as usize]; for (i, v) in self.counts.iter().enumerate() { if *v > 0 { shard.buckets[i].inc_by(*v); } } shard.sum.inc_by(self.sum); // Use `Release` ordering to ensure all operations above stay above. shard .count .inc_by_with_ordering(self.count, Ordering::Release); } self.clear() } fn sample_sum(&self) -> f64 { self.sum } fn sample_count(&self) -> u64 { self.count } } impl LocalHistogram { fn new(histogram: Histogram) -> LocalHistogram { let core = LocalHistogramCore::new(histogram); LocalHistogram { core: RefCell::new(core), } } /// Add a single observation to the [`Histogram`]. pub fn observe(&self, v: f64) { self.core.borrow_mut().observe(v); } /// Return a `LocalHistogramTimer` to track a duration. pub fn start_timer(&self) -> LocalHistogramTimer { LocalHistogramTimer::new(self.clone()) } /// Return a `LocalHistogramTimer` to track a duration. /// It is faster but less precise. #[cfg(feature = "nightly")] pub fn start_coarse_timer(&self) -> LocalHistogramTimer { LocalHistogramTimer::new_coarse(self.clone()) } /// Observe execution time of a closure, in second. pub fn observe_closure_duration(&self, f: F) -> T where F: FnOnce() -> T, { let instant = Instant::now(); let res = f(); let elapsed = instant.elapsed_sec(); self.observe(elapsed); res } /// Observe execution time of a closure, in second. #[cfg(feature = "nightly")] pub fn observe_closure_duration_coarse(&self, f: F) -> T where F: FnOnce() -> T, { let instant = Instant::now_coarse(); let res = f(); let elapsed = instant.elapsed_sec(); self.observe(elapsed); res } /// Clear the local metric. pub fn clear(&self) { self.core.borrow_mut().clear(); } /// Flush the local metrics to the [`Histogram`] metric. pub fn flush(&self) { self.core.borrow_mut().flush(); } /// Return accumulated sum of local samples. pub fn get_sample_sum(&self) -> f64 { self.core.borrow().sample_sum() } /// Return count of local samples. pub fn get_sample_count(&self) -> u64 { self.core.borrow().sample_count() } } impl LocalMetric for LocalHistogram { /// Flush the local metrics to the [`Histogram`] metric. fn flush(&self) { LocalHistogram::flush(self); } } impl Drop for LocalHistogram { fn drop(&mut self) { self.flush() } } /// An unsync [`HistogramVec`]. #[derive(Debug)] pub struct LocalHistogramVec { vec: HistogramVec, local: HashMap, } impl LocalHistogramVec { fn new(vec: HistogramVec) -> LocalHistogramVec { let local = HashMap::with_capacity(vec.v.children.read().len()); LocalHistogramVec { vec, local } } /// Get a [`LocalHistogram`] by label values. /// See more [`MetricVec::with_label_values`]. pub fn with_label_values<'a>(&'a mut self, vals: &[&str]) -> &'a LocalHistogram { let hash = self.vec.v.hash_label_values(vals).unwrap(); let vec = &self.vec; self.local .entry(hash) .or_insert_with(|| vec.with_label_values(vals).local()) } /// Remove a [`LocalHistogram`] by label values. /// See more [`MetricVec::remove_label_values`]. pub fn remove_label_values(&mut self, vals: &[&str]) -> Result<()> { let hash = self.vec.v.hash_label_values(vals)?; self.local.remove(&hash); self.vec.v.delete_label_values(vals) } /// Flush the local metrics to the [`HistogramVec`] metric. pub fn flush(&self) { for h in self.local.values() { h.flush(); } } } impl LocalMetric for LocalHistogramVec { /// Flush the local metrics to the [`HistogramVec`] metric. fn flush(&self) { LocalHistogramVec::flush(self) } } impl Clone for LocalHistogramVec { fn clone(&self) -> LocalHistogramVec { LocalHistogramVec::new(self.vec.clone()) } } #[cfg(test)] mod tests { use std::f64::{EPSILON, INFINITY}; use std::thread; use std::time::Duration; use super::*; use crate::metrics::{Collector, Metric}; #[test] fn test_histogram() { let opts = HistogramOpts::new("test1", "test help") .const_label("a", "1") .const_label("b", "2"); let histogram = Histogram::with_opts(opts).unwrap(); histogram.observe(1.0); let timer = histogram.start_timer(); thread::sleep(Duration::from_millis(100)); timer.observe_duration(); let timer = histogram.start_timer(); let handler = thread::spawn(move || { let _timer = timer; thread::sleep(Duration::from_millis(400)); }); assert!(handler.join().is_ok()); let mut mfs = histogram.collect(); assert_eq!(mfs.len(), 1); let mf = mfs.pop().unwrap(); let m = mf.get_metric().get(0).unwrap(); assert_eq!(m.get_label().len(), 2); let proto_histogram = m.get_histogram(); assert_eq!(proto_histogram.get_sample_count(), 3); assert!(proto_histogram.get_sample_sum() >= 1.5); assert_eq!(proto_histogram.get_bucket().len(), DEFAULT_BUCKETS.len()); let buckets = vec![1.0, 2.0, 3.0]; let opts = HistogramOpts::new("test2", "test help").buckets(buckets.clone()); let histogram = Histogram::with_opts(opts).unwrap(); let mut mfs = histogram.collect(); assert_eq!(mfs.len(), 1); let mf = mfs.pop().unwrap(); let m = mf.get_metric().get(0).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_eq!(proto_histogram.get_bucket().len(), buckets.len()) } #[test] #[cfg(feature = "nightly")] fn test_histogram_coarse_timer() { let opts = HistogramOpts::new("test1", "test help"); let histogram = Histogram::with_opts(opts).unwrap(); let timer = histogram.start_coarse_timer(); thread::sleep(Duration::from_millis(100)); timer.observe_duration(); let timer = histogram.start_coarse_timer(); let handler = thread::spawn(move || { let _timer = timer; thread::sleep(Duration::from_millis(400)); }); assert!(handler.join().is_ok()); histogram.observe_closure_duration(|| { thread::sleep(Duration::from_millis(400)); }); let mut mfs = histogram.collect(); assert_eq!(mfs.len(), 1); let mf = mfs.pop().unwrap(); 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); } #[test] #[cfg(feature = "nightly")] fn test_instant_on_smp() { let zero = Duration::from_millis(0); for i in 0..100_000 { let now = Instant::now(); let now_coarse = Instant::now_coarse(); if i % 100 == 0 { thread::yield_now(); } assert!(now.elapsed() >= zero); assert!(now_coarse.elapsed() >= zero); } } #[test] fn test_buckets_invalidation() { let table = vec![ (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), ]; for (buckets, is_ok, length) in table { let got = check_and_adjust_buckets(buckets); assert_eq!(got.is_ok(), is_ok); if is_ok { assert_eq!(got.unwrap().len(), length); } } } #[test] fn test_buckets_functions() { let linear_table = vec![ ( -15.0, 5.0, 6, true, vec![-15.0, -10.0, -5.0, 0.0, 5.0, 10.0], ), (-15.0, 0.0, 6, false, vec![]), (-15.0, 5.0, 0, false, vec![]), ]; for (param1, param2, param3, is_ok, vec) in linear_table { let got = linear_buckets(param1, param2, param3); assert_eq!(got.is_ok(), is_ok); if got.is_ok() { assert_eq!(got.unwrap(), vec); } } let exponential_table = vec![ (100.0, 1.2, 3, true, vec![100.0, 120.0, 144.0]), (100.0, 0.5, 3, false, vec![]), (100.0, 1.2, 0, false, vec![]), ]; for (param1, param2, param3, is_ok, vec) in exponential_table { let got = exponential_buckets(param1, param2, param3); assert_eq!(got.is_ok(), is_ok); if got.is_ok() { assert_eq!(got.unwrap(), vec); } } } #[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( HistogramOpts::new("test_histogram_vec", "test histogram vec help"), &["l1", "l2"], ) .unwrap(); assert!(vec.remove_label_values(&["v1", "v2"]).is_err()); vec.with_label_values(&["v1", "v2"]).observe(1.0); assert!(vec.remove_label_values(&["v1", "v2"]).is_ok()); assert!(vec.remove_label_values(&["v1"]).is_err()); assert!(vec.remove_label_values(&["v1", "v3"]).is_err()); } #[test] fn test_histogram_vec_with_opts_buckets() { let labels = ["l1", "l2"]; let buckets = vec![1.0, 2.0, 3.0]; let vec = HistogramVec::new( HistogramOpts::new("test_histogram_vec", "test histogram vec help") .buckets(buckets.clone()), &labels, ) .unwrap(); let histogram = vec.with_label_values(&["v1", "v2"]); histogram.observe(1.0); let m = histogram.metric(); assert_eq!(m.get_label().len(), labels.len()); let proto_histogram = m.get_histogram(); assert_eq!(proto_histogram.get_sample_count(), 1); assert!((proto_histogram.get_sample_sum() - 1.0) < EPSILON); assert_eq!(proto_histogram.get_bucket().len(), buckets.len()) } #[test] fn test_histogram_local() { let buckets = vec![1.0, 2.0, 3.0]; let opts = HistogramOpts::new("test_histogram_local", "test histogram local help") .buckets(buckets.clone()); let histogram = Histogram::with_opts(opts).unwrap(); let local = histogram.local(); let check = |count, sum| { 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); }; local.observe(1.0); local.observe(4.0); check(0, 0.0); local.flush(); check(2, 5.0); local.observe(2.0); local.clear(); check(2, 5.0); local.observe(2.0); drop(local); check(3, 7.0); } #[test] fn test_histogram_vec_local() { let vec = HistogramVec::new( HistogramOpts::new("test_histogram_vec_local", "test histogram vec help"), &["l1", "l2"], ) .unwrap(); let mut local_vec = vec.local(); vec.remove_label_values(&["v1", "v2"]).unwrap_err(); local_vec.remove_label_values(&["v1", "v2"]).unwrap_err(); let check = |count, sum| { 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); }; { // Flush LocalHistogram let h = local_vec.with_label_values(&["v1", "v2"]); h.observe(1.0); h.flush(); check(1, 1.0); } { // Flush LocalHistogramVec local_vec.with_label_values(&["v1", "v2"]).observe(4.0); local_vec.flush(); check(2, 5.0); } { // Reset ["v1", "v2"] local_vec.remove_label_values(&["v1", "v2"]).unwrap(); // Flush on drop local_vec.with_label_values(&["v1", "v2"]).observe(2.0); drop(local_vec); check(1, 2.0); } } /// Ensure that when an observe and a collect operation interleave, the /// latter does not expose a snapshot of the histogram that does not uphold /// all histogram invariants. #[test] fn atomic_observe_across_collects() { let done = Arc::new(std::sync::atomic::AtomicBool::default()); let histogram = Histogram::with_opts(HistogramOpts::new("test_name", "test help").buckets(vec![1.0])) .unwrap(); let done_clone = done.clone(); let histogram_clone = histogram.clone(); let observing_thread = std::thread::spawn(move || loop { if done_clone.load(std::sync::atomic::Ordering::Relaxed) { break; } for _ in 0..1_000_000 { histogram_clone.observe(1.0); } }); let mut sample_count = 0; let mut cumulative_count = 0; let mut sample_sum = 0; for _ in 0..1_000_000 { let metric = &histogram.collect()[0].take_metric()[0]; let proto = metric.get_histogram(); 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(); if sample_count != cumulative_count { break; } // Observation value is always `1.0` thus count and sum should // always equal. The number of `observe` calls is limited to // 1_000_000, thus the sum is limited to 1_000_000. A float 64 is // able to represent the sum accurately up to 9_007_199_254_740_992. if sample_count != sample_sum { break; } } done.store(true, std::sync::atomic::Ordering::Relaxed); observing_thread.join().unwrap(); if sample_count != cumulative_count { panic!( "Histogram invariant violated: For a histogram with a single \ bucket observing values below the bucket's upper bound only \ the histogram's count should always be equal to the buckets's \ cumulative count, got {:?} and {:?} instead.", sample_count, cumulative_count, ); } if sample_count != sample_sum { panic!( "Histogram invariant violated: For a histogram which is only \ ever observing a value of `1.0` the sample count should equal \ the sum, instead got: {:?} and {:?}", sample_count, sample_sum, ) } } #[test] fn test_error_on_inconsistent_label_cardinality() { let hist = Histogram::with_opts( histogram_opts!( "example_histogram", "Used as an example", vec![0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 5.0] ) .variable_label("example_variable"), ); if let Err(Error::InconsistentCardinality { expect, got }) = hist { assert_eq!(1, expect); assert_eq!(0, got); } else { panic!("Expected InconsistentCardinality error.") } } } prometheus-0.13.4/src/lib.rs000064400000000000000000000140761046102023000140170ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. /*! The Rust client library for [Prometheus](https://prometheus.io/). Use of this library involves a few core concepts: * [`Metric`s](core/trait.Metric.html) like [`Counter`s](type.Counter.html) that represent information about your system. * A [`Registry`](struct.Registry.html) that [`Metric`s](core/trait.Metric.html) are registered with. * An endpoint that calls [`gather`](fn.gather.html) which returns [`MetricFamily`s](proto/struct.MetricFamily.html) through an [`Encoder`](trait.Encoder.html). # Basic Example ```rust use prometheus::{Opts, Registry, Counter, TextEncoder, Encoder}; // Create a Counter. let counter_opts = Opts::new("test_counter", "test counter help"); let counter = Counter::with_opts(counter_opts).unwrap(); // Create a Registry and register Counter. let r = Registry::new(); r.register(Box::new(counter.clone())).unwrap(); // Inc. counter.inc(); // Gather the metrics. let mut buffer = vec![]; let encoder = TextEncoder::new(); let metric_families = r.gather(); encoder.encode(&metric_families, &mut buffer).unwrap(); // Output to the standard output. println!("{}", String::from_utf8(buffer).unwrap()); ``` You can find more examples within [`/examples`](https://github.com/tikv/rust-prometheus/tree/master/examples). # Static Metrics This crate supports staticly built metrics. You can use it with [`lazy_static`](https://docs.rs/lazy_static/) to quickly build up and collect some metrics. ```rust 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(); } HIGH_FIVE_COUNTER.inc(); assert_eq!(HIGH_FIVE_COUNTER.get(), 1); ``` By default, this registers with a default registry. To make a report, you can call [`gather`](fn.gather.html). This will return a family of metrics you can then feed through an [`Encoder`](trait.Encoder.html) and report to Promethus. ``` # 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 = # register_int_counter!("highfives", "Number of high fives received").unwrap(); # } # HIGH_FIVE_COUNTER.inc(); let mut buffer = Vec::new(); let encoder = TextEncoder::new(); // Gather the metrics. let metric_families = prometheus::gather(); // Encode them to send. encoder.encode(&metric_families, &mut buffer).unwrap(); let output = String::from_utf8(buffer.clone()).unwrap(); const EXPECTED_OUTPUT: &'static str = "# HELP highfives Number of high fives received\n# TYPE highfives counter\nhighfives 1\n"; assert!(output.starts_with(EXPECTED_OUTPUT)); ``` See [prometheus_static_metric](https://docs.rs/prometheus-static-metric) for additional functionality. # Features This library supports four features: * `gen`: To generate protobuf client with the latest protobuf version instead of using the pre-generated client. * `nightly`: Enable nightly only features. * `process`: For collecting process info. * `push`: Enable push support. */ #![allow( clippy::needless_pass_by_value, clippy::new_without_default, clippy::new_ret_no_self )] #![deny(missing_docs)] #![deny(missing_debug_implementations)] /// Protocol buffers format of metrics. #[cfg(feature = "protobuf")] #[allow(warnings)] #[rustfmt::skip] #[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] mod macros; mod atomic64; mod auto_flush; mod counter; mod desc; mod encoder; mod errors; mod gauge; mod histogram; mod metrics; mod pulling_gauge; #[cfg(feature = "push")] mod push; mod registry; mod value; mod vec; // Public for generated code. #[doc(hidden)] pub mod timer; #[cfg(all(feature = "process", target_os = "linux"))] pub mod process_collector; pub mod local { /*! Unsync local metrics, provides better performance. */ pub use super::counter::{ CounterWithValueType, LocalCounter, LocalCounterVec, LocalIntCounter, LocalIntCounterVec, }; pub use super::histogram::{LocalHistogram, LocalHistogramTimer, LocalHistogramVec}; pub use super::metrics::{LocalMetric, MayFlush}; pub use super::auto_flush::{ AFLocalCounter, AFLocalHistogram, CounterDelegator, HistogramDelegator, }; } pub mod core { /*! Core traits and types. */ pub use super::atomic64::*; pub use super::counter::{ GenericCounter, GenericCounterVec, GenericLocalCounter, GenericLocalCounterVec, }; pub use super::desc::{Desc, Describer}; pub use super::gauge::{GenericGauge, GenericGaugeVec}; pub use super::metrics::{Collector, Metric, Opts}; pub use super::vec::{MetricVec, MetricVecBuilder}; } pub use self::counter::{Counter, CounterVec, IntCounter, IntCounterVec}; pub use self::encoder::Encoder; #[cfg(feature = "protobuf")] pub use self::encoder::ProtobufEncoder; pub use self::encoder::TextEncoder; #[cfg(feature = "protobuf")] 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, BasicAuthentication, }; pub use self::registry::Registry; pub use self::registry::{default_registry, gather, register, unregister}; prometheus-0.13.4/src/macros.rs000064400000000000000000001121101046102023000145210ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. /// Create labels with specified name-value pairs. /// /// # Examples /// /// ``` /// # use std::collections::HashMap; /// # use prometheus::labels; /// # fn main() { /// let labels = labels!{ /// "test" => "hello", /// "foo" => "bar", /// }; /// assert_eq!(labels.len(), 2); /// assert!(labels.get("test").is_some()); /// assert_eq!(*(labels.get("test").unwrap()), "hello"); /// /// let labels: HashMap<&str, &str> = labels!{}; /// assert!(labels.is_empty()); /// # } /// ``` #[macro_export] macro_rules! labels { ( $( $ KEY : expr => $ VALUE : expr ),* $(,)? ) => { { use std::collections::HashMap; let mut lbs = HashMap::new(); $( lbs.insert($KEY, $VALUE); )* lbs } }; } #[test] fn test_labels_without_trailing_comma() { let labels = labels! { "test" => "hello", "foo" => "bar" }; assert_eq!(labels.len(), 2); assert!(labels.get("test").is_some()); assert_eq!(*(labels.get("test").unwrap()), "hello"); } /// Create an [`Opts`][crate::Opts]. /// /// # Examples /// /// ``` /// # use prometheus::{labels, opts}; /// # fn main() { /// let name = "test_opts"; /// let help = "test opts help"; /// /// let opts = opts!(name, help); /// assert_eq!(opts.name, name); /// assert_eq!(opts.help, help); /// /// let opts = opts!(name, help, labels!{"test" => "hello", "foo" => "bar",}); /// assert_eq!(opts.const_labels.len(), 2); /// assert!(opts.const_labels.get("foo").is_some()); /// assert_eq!(opts.const_labels.get("foo").unwrap(), "bar"); /// /// let opts = opts!(name, /// help, /// labels!{"test" => "hello", "foo" => "bar",}, /// labels!{"ans" => "42",}); /// assert_eq!(opts.const_labels.len(), 3); /// assert!(opts.const_labels.get("ans").is_some()); /// assert_eq!(opts.const_labels.get("ans").unwrap(), "42"); /// # } /// ``` #[macro_export] macro_rules! opts { ( $ NAME : expr , $ HELP : expr $ ( , $ CONST_LABELS : expr ) * $ ( , ) ? ) => { { use std::collections::HashMap; let opts = $crate::Opts::new($NAME, $HELP); let lbs = HashMap::::new(); $( let mut lbs = lbs; lbs.extend($CONST_LABELS.iter().map(|(k, v)| ((*k).into(), (*v).into()))); )* opts.const_labels(lbs) } } } #[test] fn test_opts_trailing_comma() { let name = "test_opts"; let help = "test opts help"; let opts = opts!(name, help,); assert_eq!(opts.name, name); assert_eq!(opts.help, help); let opts = opts!(name, help, labels! {"test" => "hello", "foo" => "bar",},); assert_eq!(opts.const_labels.len(), 2); assert!(opts.const_labels.get("foo").is_some()); assert_eq!(opts.const_labels.get("foo").unwrap(), "bar"); let opts = opts!( name, help, labels! {"test" => "hello", "foo" => "bar",}, labels! {"ans" => "42",}, ); assert_eq!(opts.const_labels.len(), 3); assert!(opts.const_labels.get("ans").is_some()); assert_eq!(opts.const_labels.get("ans").unwrap(), "42"); } /// Create a [`HistogramOpts`][crate::HistogramOpts]. /// /// # Examples /// /// ``` /// # use prometheus::linear_buckets; /// # use prometheus::{histogram_opts, labels}; /// # fn main() { /// let name = "test_histogram_opts"; /// let help = "test opts help"; /// /// let opts = histogram_opts!(name, help); /// assert_eq!(opts.common_opts.name, name); /// assert_eq!(opts.common_opts.help, help); /// /// let opts = histogram_opts!(name, help, linear_buckets(1.0, 0.5, 4).unwrap()); /// assert_eq!(opts.common_opts.name, name); /// assert_eq!(opts.common_opts.help, help); /// assert_eq!(opts.buckets.len(), 4); /// /// let opts = histogram_opts!(name, /// help, /// vec![1.0, 2.0], /// labels!{"key".to_string() => "value".to_string(),}); /// assert_eq!(opts.common_opts.name, name); /// assert_eq!(opts.common_opts.help, help); /// assert_eq!(opts.buckets.len(), 2); /// assert!(opts.common_opts.const_labels.get("key").is_some()); /// assert_eq!(opts.common_opts.const_labels.get("key").unwrap(), "value"); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! histogram_opts { ($NAME:expr, $HELP:expr $(,)?) => {{ $crate::HistogramOpts::new($NAME, $HELP) }}; ($NAME:expr, $HELP:expr, $BUCKETS:expr $(,)?) => {{ let hopts = histogram_opts!($NAME, $HELP); hopts.buckets($BUCKETS) }}; ($NAME:expr, $HELP:expr, $BUCKETS:expr, $CONST_LABELS:expr $(,)?) => {{ let hopts = histogram_opts!($NAME, $HELP, $BUCKETS); hopts.const_labels($CONST_LABELS) }}; } #[test] fn test_histogram_opts_trailing_comma() { use crate::linear_buckets; let name = "test_histogram_opts"; let help = "test opts help"; let opts = histogram_opts!(name, help,); assert_eq!(opts.common_opts.name, name); assert_eq!(opts.common_opts.help, help); let opts = histogram_opts!(name, help, linear_buckets(1.0, 0.5, 4).unwrap(),); assert_eq!(opts.common_opts.name, name); assert_eq!(opts.common_opts.help, help); assert_eq!(opts.buckets.len(), 4); let opts = histogram_opts!( name, help, vec![1.0, 2.0], labels! {"key".to_string() => "value".to_string(),}, ); assert_eq!(opts.common_opts.name, name); assert_eq!(opts.common_opts.help, help); assert_eq!(opts.buckets.len(), 2); assert!(opts.common_opts.const_labels.get("key").is_some()); assert_eq!(opts.common_opts.const_labels.get("key").unwrap(), "value"); } /// Create a [`Counter`][crate::Counter] and registers to default registry. /// /// # Examples /// /// ``` /// # use prometheus::{opts, register_counter}; /// # fn main() { /// let opts = opts!("test_macro_counter_1", "help"); /// let res1 = register_counter!(opts); /// assert!(res1.is_ok()); /// /// let res2 = register_counter!("test_macro_counter_2", "help"); /// assert!(res2.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_counter { (@of_type $TYPE:ident, $OPTS:expr) => {{ let counter = $crate::$TYPE::with_opts($OPTS).unwrap(); $crate::register(Box::new(counter.clone())).map(|()| counter) }}; ($OPTS:expr $(,)?) => {{ register_counter!(@of_type Counter, $OPTS) }}; ($NAME:expr, $HELP:expr $(,)?) => {{ register_counter!(opts!($NAME, $HELP)) }}; } #[test] fn test_register_counter_trailing_comma() { let opts = opts!("test_macro_counter_1", "help",); let res1 = register_counter!(opts,); assert!(res1.is_ok()); let res2 = register_counter!("test_macro_counter_2", "help",); assert!(res2.is_ok()); } /// Create a [`Counter`][crate::Counter] and registers to a custom registry. /// /// # Examples /// /// ``` /// # use prometheus::{register_counter_with_registry, opts}; /// # use prometheus::Registry; /// # use std::collections::HashMap; /// # fn main() { /// let mut labels = HashMap::new(); /// labels.insert("mykey".to_string(), "myvalue".to_string()); /// let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); /// let opts = opts!("test_macro_counter_1", "help"); /// let res1 = register_counter_with_registry!(opts, custom_registry); /// assert!(res1.is_ok()); /// /// let res2 = register_counter_with_registry!("test_macro_counter_2", "help", custom_registry); /// assert!(res2.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_counter_with_registry { (@of_type $TYPE: ident, $OPTS:expr, $REGISTRY:expr) => {{ let counter = $crate::$TYPE::with_opts($OPTS).unwrap(); $REGISTRY.register(Box::new(counter.clone())).map(|()| counter) }}; ($OPTS:expr, $REGISTRY:expr $(,)?) => {{ register_counter_with_registry!(@of_type Counter, $OPTS, $REGISTRY) }}; ($NAME:expr, $HELP:expr, $REGISTRY:expr $(,)?) => {{ register_counter_with_registry!(opts!($NAME, $HELP), $REGISTRY) }}; } #[test] fn test_register_counter_with_registry_trailing_comma() { use crate::Registry; use std::collections::HashMap; let mut labels = HashMap::new(); labels.insert("mykey".to_string(), "myvalue".to_string()); let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); let opts = opts!("test_macro_counter_1", "help",); let res1 = register_counter_with_registry!(opts, custom_registry,); assert!(res1.is_ok()); let res2 = register_counter_with_registry!("test_macro_counter_2", "help", custom_registry,); assert!(res2.is_ok()); } /// Create an [`IntCounter`][crate::IntCounter] and registers to default registry. /// /// View docs of `register_counter` for examples. #[macro_export(local_inner_macros)] macro_rules! register_int_counter { ($OPTS:expr $(,)?) => {{ register_counter!(@of_type IntCounter, $OPTS) }}; ($NAME:expr, $HELP:expr $(,)?) => {{ register_int_counter!(opts!($NAME, $HELP)) }}; } /// Create an [`IntCounter`][crate::IntCounter] and registers to a custom registry. /// /// View docs of `register_counter_with_registry` for examples. #[macro_export(local_inner_macros)] macro_rules! register_int_counter_with_registry { ($OPTS:expr, $REGISTRY:expr $(,)?) => {{ register_counter_with_registry!(@of_type IntCounter, $OPTS, $REGISTRY) }}; ($NAME:expr, $HELP:expr, $REGISTRY:expr $(,)?) => {{ register_int_counter_with_registry!(opts!($NAME, $HELP), $REGISTRY) }}; } #[test] fn test_register_int_counter() { use crate::Registry; use std::collections::HashMap; let opts = opts!("test_opts_int_counter_1", "help"); let res = register_int_counter!(opts); assert!(res.is_ok()); let res = register_int_counter!("test_opts_int_counter_2", "help"); assert!(res.is_ok()); let opts = opts!("test_opts_int_counter_3", "help",); let res = register_int_counter!(opts,); assert!(res.is_ok()); let res = register_int_counter!("test_opts_int_counter_4", "help",); assert!(res.is_ok()); let mut labels = HashMap::new(); labels.insert("mykey".to_string(), "myvalue".to_string()); let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); let opts = opts!("test_opts_int_counter_1", "help"); let res = register_int_counter_with_registry!(opts, custom_registry); assert!(res.is_ok()); let res = register_int_counter_with_registry!("test_opts_int_counter_2", "help", custom_registry); assert!(res.is_ok()); let opts = opts!("test_opts_int_counter_3", "help"); let res = register_int_counter_with_registry!(opts, custom_registry,); assert!(res.is_ok()); let res = register_int_counter_with_registry!("test_opts_int_counter_4", "help", custom_registry,); assert!(res.is_ok()); } #[macro_export] #[doc(hidden)] macro_rules! __register_counter_vec { ($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr) => {{ let counter_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap(); $crate::register(Box::new(counter_vec.clone())).map(|()| counter_vec) }}; ($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr, $REGISTRY:expr) => {{ let counter_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap(); $REGISTRY .register(Box::new(counter_vec.clone())) .map(|()| counter_vec) }}; } /// Create a [`CounterVec`][crate::CounterVec] and registers to default registry. /// /// # Examples /// /// ``` /// # use prometheus::{opts, register_counter_vec}; /// # fn main() { /// let opts = opts!("test_macro_counter_vec_1", "help"); /// let counter_vec = register_counter_vec!(opts, &["a", "b"]); /// assert!(counter_vec.is_ok()); /// /// let counter_vec = register_counter_vec!("test_macro_counter_vec_2", "help", &["a", "b"]); /// assert!(counter_vec.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_counter_vec { ($OPTS:expr, $LABELS_NAMES:expr $(,)?) => {{ __register_counter_vec!(CounterVec, $OPTS, $LABELS_NAMES) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr $(,)?) => {{ register_counter_vec!(opts!($NAME, $HELP), $LABELS_NAMES) }}; } #[test] fn test_register_counter_vec_trailing_comma() { let opts = opts!("test_macro_counter_vec_1", "help",); let counter_vec = register_counter_vec!(opts, &["a", "b"],); assert!(counter_vec.is_ok()); let counter_vec = register_counter_vec!("test_macro_counter_vec_2", "help", &["a", "b"],); assert!(counter_vec.is_ok()); } /// Create a [`CounterVec`][crate::CounterVec] and registers to a custom registry. /// /// # Examples /// /// ``` /// # use prometheus::{register_counter_vec_with_registry, opts}; /// # use prometheus::Registry; /// # use std::collections::HashMap; /// # fn main() { /// let mut labels = HashMap::new(); /// labels.insert("mykey".to_string(), "myvalue".to_string()); /// let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); /// /// let opts = opts!("test_macro_counter_vec_1", "help"); /// let counter_vec = register_counter_vec_with_registry!(opts, &["a", "b"], custom_registry); /// assert!(counter_vec.is_ok()); /// /// let counter_vec = register_counter_vec_with_registry!("test_macro_counter_vec_2", "help", &["a", "b"], custom_registry); /// assert!(counter_vec.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_counter_vec_with_registry { ($OPTS:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{ __register_counter_vec!(CounterVec, $OPTS, $LABELS_NAMES, $REGISTRY) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{ register_counter_vec_with_registry!(opts!($NAME, $HELP), $LABELS_NAMES, $REGISTRY) }}; } #[test] fn test_register_counter_vec_with_registry_trailing_comma() { use crate::Registry; use std::collections::HashMap; let mut labels = HashMap::new(); labels.insert("mykey".to_string(), "myvalue".to_string()); let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); let opts = opts!("test_macro_counter_vec_1", "help",); let counter_vec = register_counter_vec_with_registry!(opts, &["a", "b"], custom_registry,); assert!(counter_vec.is_ok()); let counter_vec = register_counter_vec_with_registry!( "test_macro_counter_vec_2", "help", &["a", "b"], custom_registry, ); assert!(counter_vec.is_ok()); } /// Create an [`IntCounterVec`][crate::IntCounterVec] and registers to default registry. /// /// View docs of `register_counter_vec` for examples. #[macro_export(local_inner_macros)] macro_rules! register_int_counter_vec { ($OPTS:expr, $LABELS_NAMES:expr $(,)?) => {{ __register_counter_vec!(IntCounterVec, $OPTS, $LABELS_NAMES) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr $(,)?) => {{ register_int_counter_vec!(opts!($NAME, $HELP), $LABELS_NAMES) }}; } /// Create an [`IntCounterVec`][crate::IntCounterVec] and registers to a custom registry. /// /// View docs of `register_counter_vec_with_registry` for examples. #[macro_export(local_inner_macros)] macro_rules! register_int_counter_vec_with_registry { ($OPTS:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{ __register_counter_vec!(IntCounterVec, $OPTS, $LABELS_NAMES, $REGISTRY) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{ register_int_counter_vec_with_registry!(opts!($NAME, $HELP), $LABELS_NAMES, $REGISTRY) }}; } #[test] fn test_register_int_counter_vec() { use crate::Registry; use std::collections::HashMap; let opts = opts!("test_opts_int_counter_vec_1", "help"); let res = register_int_counter_vec!(opts, &["a", "b"]); assert!(res.is_ok()); let res = register_int_counter_vec!("test_opts_int_counter_vec_2", "help", &["a", "b"]); assert!(res.is_ok()); let opts = opts!("test_opts_int_counter_vec_3", "help",); let res = register_int_counter_vec!(opts, &["a", "b"],); assert!(res.is_ok()); let res = register_int_counter_vec!("test_opts_int_counter_vec_4", "help", &["a", "b"],); assert!(res.is_ok()); let mut labels = HashMap::new(); labels.insert("mykey".to_string(), "myvalue".to_string()); let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); let opts = opts!("test_opts_int_counter_vec_1", "help"); let res = register_int_counter_vec_with_registry!(opts, &["a", "b"], custom_registry); assert!(res.is_ok()); let res = register_int_counter_vec_with_registry!( "test_opts_int_counter_vec_2", "help", &["a", "b"], custom_registry ); assert!(res.is_ok()); let opts = opts!("test_opts_int_counter_vec_3", "help"); let res = register_int_counter_vec_with_registry!(opts, &["a", "b"], custom_registry,); assert!(res.is_ok()); let res = register_int_counter_vec_with_registry!( "test_opts_int_counter_vec_4", "help", &["a", "b"], custom_registry, ); assert!(res.is_ok()); } #[macro_export] #[doc(hidden)] macro_rules! __register_gauge { ($TYPE:ident, $OPTS:expr) => {{ let gauge = $crate::$TYPE::with_opts($OPTS).unwrap(); $crate::register(Box::new(gauge.clone())).map(|()| gauge) }}; ($TYPE:ident, $OPTS:expr, $REGISTRY:expr) => {{ let gauge = $crate::$TYPE::with_opts($OPTS).unwrap(); $REGISTRY.register(Box::new(gauge.clone())).map(|()| gauge) }}; } /// Create a [`Gauge`][crate::Gauge] and registers to default registry. /// /// # Examples /// /// ``` /// # use prometheus::{opts, register_gauge}; /// # fn main() { /// let opts = opts!("test_macro_gauge", "help"); /// let res1 = register_gauge!(opts); /// assert!(res1.is_ok()); /// /// let res2 = register_gauge!("test_macro_gauge_2", "help"); /// assert!(res2.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_gauge { ($OPTS:expr $(,)?) => {{ __register_gauge!(Gauge, $OPTS) }}; ($NAME:expr, $HELP:expr $(,)?) => {{ register_gauge!(opts!($NAME, $HELP)) }}; } #[test] fn test_register_gauge_trailing_comma() { let opts = opts!("test_macro_gauge", "help",); let res1 = register_gauge!(opts,); assert!(res1.is_ok()); let res2 = register_gauge!("test_macro_gauge_2", "help",); assert!(res2.is_ok()); } /// Create a [`Gauge`][crate::Gauge] and registers to a custom registry. /// /// # Examples /// /// ``` /// # use prometheus::{register_gauge_with_registry, opts}; /// # use prometheus::Registry; /// # use std::collections::HashMap; /// # fn main() { /// let mut labels = HashMap::new(); /// labels.insert("mykey".to_string(), "myvalue".to_string()); /// let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); /// /// let opts = opts!("test_macro_gauge", "help"); /// let res1 = register_gauge_with_registry!(opts, custom_registry); /// assert!(res1.is_ok()); /// /// let res2 = register_gauge_with_registry!("test_macro_gauge_2", "help", custom_registry); /// assert!(res2.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_gauge_with_registry { ($OPTS:expr, $REGISTRY:expr $(,)?) => {{ __register_gauge!(Gauge, $OPTS, $REGISTRY) }}; ($NAME:expr, $HELP:expr, $REGISTRY:expr $(,)?) => {{ register_gauge_with_registry!(opts!($NAME, $HELP), $REGISTRY) }}; } #[test] fn test_register_gauge_with_registry_trailing_comma() { use crate::Registry; use std::collections::HashMap; let mut labels = HashMap::new(); labels.insert("mykey".to_string(), "myvalue".to_string()); let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); let opts = opts!("test_macro_gauge", "help",); let res1 = register_gauge_with_registry!(opts, custom_registry,); assert!(res1.is_ok()); let res2 = register_gauge_with_registry!("test_macro_gauge_2", "help", custom_registry,); assert!(res2.is_ok()); } /// Create an [`IntGauge`][crate::IntGauge] and registers to default registry. /// /// View docs of `register_gauge` for examples. #[macro_export(local_inner_macros)] macro_rules! register_int_gauge { ($OPTS:expr $(,)?) => {{ __register_gauge!(IntGauge, $OPTS) }}; ($NAME:expr, $HELP:expr $(,)?) => {{ register_int_gauge!(opts!($NAME, $HELP)) }}; } /// Create an [`IntGauge`][crate::IntGauge] and registers to a custom registry. /// /// View docs of `register_gauge_with_registry` for examples. #[macro_export(local_inner_macros)] macro_rules! register_int_gauge_with_registry { ($OPTS:expr, $REGISTRY:expr $(,)?) => {{ __register_gauge!(IntGauge, $OPTS, $REGISTRY) }}; ($NAME:expr, $HELP:expr, $REGISTRY:expr $(,)?) => {{ register_int_gauge_with_registry!(opts!($NAME, $HELP), $REGISTRY) }}; } #[macro_export] #[doc(hidden)] macro_rules! __register_gauge_vec { ($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr $(,)?) => {{ let gauge_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap(); $crate::register(Box::new(gauge_vec.clone())).map(|()| gauge_vec) }}; ($TYPE:ident, $OPTS:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{ let gauge_vec = $crate::$TYPE::new($OPTS, $LABELS_NAMES).unwrap(); $REGISTRY .register(Box::new(gauge_vec.clone())) .map(|()| gauge_vec) }}; } #[test] fn test_register_int_gauge() { use crate::Registry; use std::collections::HashMap; let opts = opts!("test_opts_int_gauge_1", "help"); let res = register_int_gauge!(opts); assert!(res.is_ok()); let res = register_int_gauge!("test_opts_int_gauge_2", "help"); assert!(res.is_ok()); let opts = opts!("test_opts_int_gauge_3", "help",); let res = register_int_gauge!(opts,); assert!(res.is_ok()); let res = register_int_gauge!("test_opts_int_gauge_4", "help",); assert!(res.is_ok()); let mut labels = HashMap::new(); labels.insert("mykey".to_string(), "myvalue".to_string()); let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); let opts = opts!("test_opts_int_gauge_1", "help"); let res = register_int_gauge_with_registry!(opts, custom_registry); assert!(res.is_ok()); let res = register_int_gauge_with_registry!("test_opts_int_gauge_2", "help", custom_registry); assert!(res.is_ok()); let opts = opts!("test_opts_int_gauge_3", "help"); let res = register_int_gauge_with_registry!(opts, custom_registry,); assert!(res.is_ok()); let res = register_int_gauge_with_registry!("test_opts_int_gauge_4", "help", custom_registry,); assert!(res.is_ok()); } /// Create a [`GaugeVec`][crate::GaugeVec] and registers to default registry. /// /// # Examples /// /// ``` /// # use prometheus::{opts, register_gauge_vec}; /// # fn main() { /// let opts = opts!("test_macro_gauge_vec_1", "help"); /// let gauge_vec = register_gauge_vec!(opts, &["a", "b"]); /// assert!(gauge_vec.is_ok()); /// /// let gauge_vec = register_gauge_vec!("test_macro_gauge_vec_2", "help", &["a", "b"]); /// assert!(gauge_vec.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_gauge_vec { ($OPTS:expr, $LABELS_NAMES:expr $(,)?) => {{ __register_gauge_vec!(GaugeVec, $OPTS, $LABELS_NAMES) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr $(,)?) => {{ register_gauge_vec!(opts!($NAME, $HELP), $LABELS_NAMES) }}; } #[test] fn test_register_gauge_vec_trailing_comma() { let opts = opts!("test_macro_gauge_vec_1", "help",); let gauge_vec = register_gauge_vec!(opts, &["a", "b"],); assert!(gauge_vec.is_ok()); let gauge_vec = register_gauge_vec!("test_macro_gauge_vec_2", "help", &["a", "b"],); assert!(gauge_vec.is_ok()); } /// Create a [`GaugeVec`][crate::GaugeVec] and registers to a custom registry. /// /// # Examples /// /// ``` /// # use prometheus::{register_gauge_vec_with_registry, opts}; /// # use prometheus::Registry; /// # use std::collections::HashMap; /// # fn main() { /// let mut labels = HashMap::new(); /// labels.insert("mykey".to_string(), "myvalue".to_string()); /// let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); /// /// let opts = opts!("test_macro_gauge_vec_1", "help"); /// let gauge_vec = register_gauge_vec_with_registry!(opts, &["a", "b"], custom_registry); /// assert!(gauge_vec.is_ok()); /// /// let gauge_vec = register_gauge_vec_with_registry!("test_macro_gauge_vec_2", "help", &["a", "b"], custom_registry); /// assert!(gauge_vec.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_gauge_vec_with_registry { ($OPTS:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{ __register_gauge_vec!(GaugeVec, $OPTS, $LABELS_NAMES, $REGISTRY) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{ register_gauge_vec_with_registry!(opts!($NAME, $HELP), $LABELS_NAMES, $REGISTRY) }}; } #[test] fn test_register_gauge_vec_with_registry_trailing_comma() { use crate::Registry; use std::collections::HashMap; let mut labels = HashMap::new(); labels.insert("mykey".to_string(), "myvalue".to_string()); let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); let opts = opts!("test_macro_gauge_vec_1", "help",); let gauge_vec = register_gauge_vec_with_registry!(opts, &["a", "b"], custom_registry,); assert!(gauge_vec.is_ok()); let gauge_vec = register_gauge_vec_with_registry!( "test_macro_gauge_vec_2", "help", &["a", "b"], custom_registry, ); assert!(gauge_vec.is_ok()); } /// Create an [`IntGaugeVec`][crate::IntGaugeVec] and registers to default registry. /// /// View docs of `register_gauge_vec` for examples. #[macro_export(local_inner_macros)] macro_rules! register_int_gauge_vec { ($OPTS:expr, $LABELS_NAMES:expr $(,)?) => {{ __register_gauge_vec!(IntGaugeVec, $OPTS, $LABELS_NAMES) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr $(,)?) => {{ register_int_gauge_vec!(opts!($NAME, $HELP), $LABELS_NAMES) }}; } /// Create an [`IntGaugeVec`][crate::IntGaugeVec] and registers to a custom registry. /// /// View docs of `register_gauge_vec_with_registry` for examples. #[macro_export(local_inner_macros)] macro_rules! register_int_gauge_vec_with_registry { ($OPTS:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{ __register_gauge_vec!(IntGaugeVec, $OPTS, $LABELS_NAMES, $REGISTRY) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{ register_int_gauge_vec_with_registry!(opts!($NAME, $HELP), $LABELS_NAMES, $REGISTRY) }}; } #[test] fn test_register_int_gauge_vec() { use crate::Registry; use std::collections::HashMap; let opts = opts!("test_opts_int_gauge_vec_1", "help"); let res = register_int_gauge_vec!(opts, &["a", "b"]); assert!(res.is_ok()); let res = register_int_gauge_vec!("test_opts_int_gauge_vec_2", "help", &["a", "b"]); assert!(res.is_ok()); let opts = opts!("test_opts_int_gauge_vec_3", "help",); let res = register_int_gauge_vec!(opts, &["a", "b"],); assert!(res.is_ok()); let res = register_int_gauge_vec!("test_opts_int_gauge_vec_4", "help", &["a", "b"],); assert!(res.is_ok()); let mut labels = HashMap::new(); labels.insert("mykey".to_string(), "myvalue".to_string()); let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); let opts = opts!("test_opts_int_gauge_vec_1", "help"); let res = register_int_gauge_vec_with_registry!(opts, &["a", "b"], custom_registry); assert!(res.is_ok()); let res = register_int_gauge_vec_with_registry!( "test_opts_int_gauge_vec_2", "help", &["a", "b"], custom_registry ); assert!(res.is_ok()); let opts = opts!("test_opts_int_gauge_vec_3", "help"); let res = register_int_gauge_vec_with_registry!(opts, &["a", "b"], custom_registry,); assert!(res.is_ok()); let res = register_int_gauge_vec_with_registry!( "test_opts_int_gauge_vec_4", "help", &["a", "b"], custom_registry, ); assert!(res.is_ok()); } /// Create a [`Histogram`][crate::Histogram] and registers to default registry. /// /// # Examples /// /// ``` /// # use prometheus::{histogram_opts, register_histogram}; /// # fn main() { /// let opts = histogram_opts!("test_macro_histogram", "help"); /// let res1 = register_histogram!(opts); /// assert!(res1.is_ok()); /// /// let res2 = register_histogram!("test_macro_histogram_2", "help"); /// assert!(res2.is_ok()); /// /// let res3 = register_histogram!("test_macro_histogram_4", /// "help", /// vec![1.0, 2.0]); /// assert!(res3.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_histogram { ($NAME:expr, $HELP:expr $(,)?) => { register_histogram!(histogram_opts!($NAME, $HELP)) }; ($NAME:expr, $HELP:expr, $BUCKETS:expr $(,)?) => { register_histogram!(histogram_opts!($NAME, $HELP, $BUCKETS)) }; ($HOPTS:expr $(,)?) => {{ let histogram = $crate::Histogram::with_opts($HOPTS).unwrap(); $crate::register(Box::new(histogram.clone())).map(|()| histogram) }}; } #[test] fn test_register_histogram_trailing_comma() { let opts = histogram_opts!("test_macro_histogram", "help",); let res1 = register_histogram!(opts,); assert!(res1.is_ok()); let res2 = register_histogram!("test_macro_histogram_2", "help",); assert!(res2.is_ok()); let res3 = register_histogram!("test_macro_histogram_4", "help", vec![1.0, 2.0],); assert!(res3.is_ok()); } /// Create a [`Histogram`][crate::Histogram] and registers to a custom registry. /// /// # Examples /// /// ``` /// # use prometheus::{register_histogram_with_registry, histogram_opts}; /// # use prometheus::Registry; /// # use std::collections::HashMap; /// # fn main() { /// let mut labels = HashMap::new(); /// labels.insert("mykey".to_string(), "myvalue".to_string()); /// let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); /// /// let opts = histogram_opts!("test_macro_histogram", "help"); /// let res1 = register_histogram_with_registry!(opts, custom_registry); /// assert!(res1.is_ok()); /// /// let res2 = register_histogram_with_registry!("test_macro_histogram_2", "help", custom_registry); /// assert!(res2.is_ok()); /// /// let res3 = register_histogram_with_registry!("test_macro_histogram_4", /// "help", /// vec![1.0, 2.0], custom_registry); /// assert!(res3.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_histogram_with_registry { ($NAME:expr, $HELP:expr, $REGISTRY:expr $(,)?) => { register_histogram_with_registry!(histogram_opts!($NAME, $HELP), $REGISTRY) }; ($NAME:expr, $HELP:expr, $BUCKETS:expr, $REGISTRY:expr $(,)?) => { register_histogram_with_registry!(histogram_opts!($NAME, $HELP, $BUCKETS), $REGISTRY) }; ($HOPTS:expr, $REGISTRY:expr $(,)?) => {{ let histogram = $crate::Histogram::with_opts($HOPTS).unwrap(); $REGISTRY .register(Box::new(histogram.clone())) .map(|()| histogram) }}; } #[test] fn test_register_histogram_with_registry_trailing_comma() { use crate::Registry; use std::collections::HashMap; let mut labels = HashMap::new(); labels.insert("mykey".to_string(), "myvalue".to_string()); let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); let opts = histogram_opts!("test_macro_histogram", "help",); let res1 = register_histogram_with_registry!(opts, custom_registry,); assert!(res1.is_ok()); let res2 = register_histogram_with_registry!("test_macro_histogram_2", "help", custom_registry,); assert!(res2.is_ok()); let res3 = register_histogram_with_registry!( "test_macro_histogram_4", "help", vec![1.0, 2.0], custom_registry, ); assert!(res3.is_ok()); } /// Create a [`HistogramVec`][crate::HistogramVec] and registers to default registry. /// /// # Examples /// /// ``` /// # use prometheus::{histogram_opts, register_histogram_vec}; /// # fn main() { /// let opts = histogram_opts!("test_macro_histogram_vec_1", "help"); /// let histogram_vec = register_histogram_vec!(opts, &["a", "b"]); /// assert!(histogram_vec.is_ok()); /// /// let histogram_vec = /// register_histogram_vec!("test_macro_histogram_vec_2", "help", &["a", "b"]); /// assert!(histogram_vec.is_ok()); /// /// let histogram_vec = register_histogram_vec!("test_macro_histogram_vec_3", /// "help", /// &["test_label"], /// vec![0.0, 1.0, 2.0]); /// assert!(histogram_vec.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_histogram_vec { ($HOPTS:expr, $LABELS_NAMES:expr $(,)?) => {{ let histogram_vec = $crate::HistogramVec::new($HOPTS, $LABELS_NAMES).unwrap(); $crate::register(Box::new(histogram_vec.clone())).map(|()| histogram_vec) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr $(,)?) => {{ register_histogram_vec!(histogram_opts!($NAME, $HELP), $LABELS_NAMES) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr, $BUCKETS:expr $(,)?) => {{ register_histogram_vec!(histogram_opts!($NAME, $HELP, $BUCKETS), $LABELS_NAMES) }}; } #[test] fn test_register_histogram_vec_trailing_comma() { let opts = histogram_opts!("test_macro_histogram_vec_1", "help",); let histogram_vec = register_histogram_vec!(opts, &["a", "b"],); assert!(histogram_vec.is_ok()); let histogram_vec = register_histogram_vec!("test_macro_histogram_vec_2", "help", &["a", "b"],); assert!(histogram_vec.is_ok()); let histogram_vec = register_histogram_vec!( "test_macro_histogram_vec_3", "help", &["test_label"], vec![0.0, 1.0, 2.0], ); assert!(histogram_vec.is_ok()); } /// Create a [`HistogramVec`][crate::HistogramVec] and registers to default registry. /// /// # Examples /// /// ``` /// # use prometheus::{register_histogram_vec_with_registry, histogram_opts}; /// # use prometheus::Registry; /// # use std::collections::HashMap; /// # fn main() { /// let mut labels = HashMap::new(); /// labels.insert("mykey".to_string(), "myvalue".to_string()); /// let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); /// /// let opts = histogram_opts!("test_macro_histogram_vec_1", "help"); /// let histogram_vec = register_histogram_vec_with_registry!(opts, &["a", "b"], custom_registry); /// assert!(histogram_vec.is_ok()); /// /// let histogram_vec = /// register_histogram_vec_with_registry!("test_macro_histogram_vec_2", "help", &["a", "b"], custom_registry); /// assert!(histogram_vec.is_ok()); /// /// let histogram_vec = register_histogram_vec_with_registry!("test_macro_histogram_vec_3", /// "help", /// &["test_label"], /// vec![0.0, 1.0, 2.0], custom_registry); /// assert!(histogram_vec.is_ok()); /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! register_histogram_vec_with_registry { ($HOPTS:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{ let histogram_vec = $crate::HistogramVec::new($HOPTS, $LABELS_NAMES).unwrap(); $REGISTRY .register(Box::new(histogram_vec.clone())) .map(|()| histogram_vec) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr, $REGISTRY:expr $(,)?) => {{ register_histogram_vec_with_registry!( histogram_opts!($NAME, $HELP), $LABELS_NAMES, $REGISTRY ) }}; ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr, $BUCKETS:expr, $REGISTRY:expr $(,)?) => {{ register_histogram_vec_with_registry!( histogram_opts!($NAME, $HELP, $BUCKETS), $LABELS_NAMES, $REGISTRY ) }}; } #[test] fn test_register_histogram_vec_with_registry_trailing_comma() { use crate::Registry; use std::collections::HashMap; let mut labels = HashMap::new(); labels.insert("mykey".to_string(), "myvalue".to_string()); let custom_registry = Registry::new_custom(Some("myprefix".to_string()), Some(labels)).unwrap(); let opts = histogram_opts!("test_macro_histogram_vec_1", "help",); let histogram_vec = register_histogram_vec_with_registry!(opts, &["a", "b"], custom_registry,); assert!(histogram_vec.is_ok()); let histogram_vec = register_histogram_vec_with_registry!( "test_macro_histogram_vec_2", "help", &["a", "b"], custom_registry, ); assert!(histogram_vec.is_ok()); let histogram_vec = register_histogram_vec_with_registry!( "test_macro_histogram_vec_3", "help", &["test_label"], vec![0.0, 1.0, 2.0], custom_registry, ); assert!(histogram_vec.is_ok()); } prometheus-0.13.4/src/metrics.rs000064400000000000000000000216631046102023000147170ustar 00000000000000// Copyright 2014 The Prometheus Authors // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::cmp::{Eq, Ord, Ordering, PartialOrd}; use std::collections::HashMap; use crate::desc::{Desc, Describer}; use crate::errors::Result; use crate::proto::{self, LabelPair}; use crate::timer; use std::cell::Cell; pub const SEPARATOR_BYTE: u8 = 0xFF; /// An interface for collecting metrics. pub trait Collector: Sync + Send { /// Return descriptors for metrics. fn desc(&self) -> Vec<&Desc>; /// Collect metrics. fn collect(&self) -> Vec; } /// An interface models a single sample value with its meta data being exported to Prometheus. pub trait Metric: Sync + Send + Clone { /// Return the protocol Metric. fn metric(&self) -> proto::Metric; } /// An interface models a Metric only usable in single thread environment. pub trait LocalMetric { /// Flush the local metrics to the global one. fn flush(&self); } /// An interface models a LocalMetric with try to flush functions. /// Not intend to be implemented by user manually, used in macro generated code. pub trait MayFlush: LocalMetric { /// If the LocalMetric is already flushed in last `flush_interval_sec` seconds, then do nothing, /// else flush and update last flush time. fn try_flush(&self, last_flush: &Cell, flush_interval_millis: u64) { let now = timer::recent_millis(); let last_tick = last_flush.get(); if now < last_tick + flush_interval_millis { return; } self.flush(); last_flush.set(now); } /// Open to implementation to fill try_flush parameters fn may_flush(&self); } /// A struct that bundles the options for creating most [`Metric`] types. #[derive(Debug, Clone)] pub struct Opts { /// namespace, subsystem, and name are components of the fully-qualified /// name of the [`Metric`] (created by joining these components with /// "_"). Only Name is mandatory, the others merely help structuring the /// name. Note that the fully-qualified name of the metric must be a /// valid Prometheus metric name. pub namespace: String, /// namespace, subsystem, and name are components of the fully-qualified /// name of the [`Metric`] (created by joining these components with /// "_"). Only Name is mandatory, the others merely help structuring the /// name. Note that the fully-qualified name of the metric must be a /// valid Prometheus metric name. pub subsystem: String, /// namespace, subsystem, and name are components of the fully-qualified /// name of the [`Metric`] (created by joining these components with /// "_"). Only Name is mandatory, the others merely help structuring the /// name. Note that the fully-qualified name of the metric must be a /// valid Prometheus metric name. pub name: String, /// help provides information about this metric. Mandatory! /// /// Metrics with the same fully-qualified name must have the same Help /// string. pub help: String, /// const_labels are used to attach fixed labels to this metric. Metrics /// with the same fully-qualified name must have the same label names in /// their ConstLabels. /// /// Note that in most cases, labels have a value that varies during the /// lifetime of a process. Those labels are usually managed with a metric /// vector collector (like CounterVec, GaugeVec). ConstLabels /// serve only special purposes. One is for the special case where the /// value of a label does not change during the lifetime of a process, /// e.g. if the revision of the running binary is put into a /// label. Another, more advanced purpose is if more than one [`Collector`] /// needs to collect Metrics with the same fully-qualified name. In that /// case, those Metrics must differ in the values of their /// ConstLabels. See the [`Collector`] examples. /// /// If the value of a label never changes (not even between binaries), /// that label most likely should not be a label at all (but part of the /// metric name). pub const_labels: HashMap, /// variable_labels contains names of labels for which the metric maintains /// variable values. Metrics with the same fully-qualified name must have /// the same label names in their variable_labels. /// /// Note that variable_labels is used in `MetricVec`. To create a single /// metric must leave it empty. pub variable_labels: Vec, } impl Opts { /// `new` creates the Opts with the `name` and `help` arguments. pub fn new, S2: Into>(name: S1, help: S2) -> Opts { Opts { namespace: "".to_owned(), subsystem: "".to_owned(), name: name.into(), help: help.into(), const_labels: HashMap::new(), variable_labels: Vec::new(), } } /// `namespace` sets the namespace. pub fn namespace>(mut self, namespace: S) -> Self { self.namespace = namespace.into(); self } /// `subsystem` sets the sub system. pub fn subsystem>(mut self, subsystem: S) -> Self { self.subsystem = subsystem.into(); self } /// `const_labels` sets the const labels. pub fn const_labels(mut self, const_labels: HashMap) -> Self { self.const_labels = const_labels; self } /// `const_label` adds a const label. pub fn const_label, S2: Into>(mut self, name: S1, value: S2) -> Self { self.const_labels.insert(name.into(), value.into()); self } /// `variable_labels` sets the variable labels. pub fn variable_labels(mut self, variable_labels: Vec) -> Self { self.variable_labels = variable_labels; self } /// `variable_label` adds a variable label. pub fn variable_label>(mut self, name: S) -> Self { self.variable_labels.push(name.into()); self } /// `fq_name` returns the fq_name. pub fn fq_name(&self) -> String { build_fq_name(&self.namespace, &self.subsystem, &self.name) } } impl Describer for Opts { fn describe(&self) -> Result { Desc::new( self.fq_name(), self.help.clone(), self.variable_labels.clone(), self.const_labels.clone(), ) } } impl Ord for LabelPair { fn cmp(&self, other: &LabelPair) -> Ordering { self.get_name().cmp(other.get_name()) } } impl Eq for LabelPair {} impl PartialOrd for LabelPair { fn partial_cmp(&self, other: &LabelPair) -> Option { Some(self.cmp(other)) } } /// `build_fq_name` joins the given three name components by "_". Empty name /// components are ignored. If the name parameter itself is empty, an empty /// string is returned, no matter what. [`Metric`] implementations included in this /// library use this function internally to generate the fully-qualified metric /// name from the name component in their Opts. Users of the library will only /// need this function if they implement their own [`Metric`] or instantiate a Desc /// directly. fn build_fq_name(namespace: &str, subsystem: &str, name: &str) -> String { if name.is_empty() { return "".to_owned(); } if !namespace.is_empty() && !subsystem.is_empty() { return format!("{}_{}_{}", namespace, subsystem, name); } else if !namespace.is_empty() { return format!("{}_{}", namespace, name); } else if !subsystem.is_empty() { return format!("{}_{}", subsystem, name); } name.to_owned() } #[cfg(test)] mod tests { use std::cmp::{Ord, Ordering}; use super::*; use crate::proto::LabelPair; fn new_label_pair(name: &str, value: &str) -> LabelPair { let mut l = LabelPair::default(); l.set_name(name.to_owned()); l.set_value(value.to_owned()); l } #[test] fn test_label_cmp() { let tbl = vec![ ("k1", "k2", Ordering::Less), ("k1", "k1", Ordering::Equal), ("k1", "k0", Ordering::Greater), ]; for (l1, l2, order) in tbl { let lhs = new_label_pair(l1, l1); let rhs = new_label_pair(l2, l2); assert_eq!(lhs.cmp(&rhs), order); } } #[test] fn test_build_fq_name() { let tbl = vec![ ("a", "b", "c", "a_b_c"), ("", "b", "c", "b_c"), ("a", "", "c", "a_c"), ("", "", "c", "c"), ("a", "b", "", ""), ("a", "", "", ""), ("", "b", "", ""), (" ", "", "", ""), ]; for (namespace, subsystem, name, res) in tbl { assert_eq!(&build_fq_name(namespace, subsystem, name), res); } } #[test] fn test_different_generic_types() { Opts::new(format!("{}_{}", "string", "label"), "&str_label"); } } prometheus-0.13.4/src/plain_model.rs000064400000000000000000000206461046102023000155340ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. //! Non-generated version of required structures provided by the protobuf. //! This version is used when the `protobuf` feature is turned off. #![allow(missing_docs)] #[derive(PartialEq, Clone, Default, Debug)] pub struct LabelPair { name: String, value: String, } impl LabelPair { #[deprecated(note = "Use default()", since = "0.5.1")] pub fn new() -> LabelPair { Default::default() } #[deprecated( note = "This method is protobuf specific and will be removed in a future version", since = "0.5.1" )] pub fn clear_name(&mut self) { self.name.clear(); } pub fn set_name(&mut self, v: String) { self.name = v; } pub fn get_name(&self) -> &str { &self.name } pub fn set_value(&mut self, v: String) { self.value = v; } pub fn get_value(&self) -> &str { &self.value } } #[derive(PartialEq, Clone, Default, Debug)] pub struct Gauge { value: f64, } impl Gauge { #[deprecated(note = "Use default()", since = "0.5.1")] pub fn new() -> Gauge { Default::default() } pub fn set_value(&mut self, v: f64) { self.value = v; } pub fn get_value(&self) -> f64 { self.value } } #[derive(PartialEq, Clone, Default, Debug)] pub struct Counter { value: f64, } impl Counter { #[deprecated(note = "Use default()", since = "0.5.1")] pub fn new() -> Counter { Default::default() } // Param is passed by value, moved pub fn set_value(&mut self, v: f64) { self.value = v; } pub fn get_value(&self) -> f64 { self.value } } #[derive(PartialEq, Clone, Default, Debug)] pub struct Quantile { quantile: f64, value: f64, } impl Quantile { #[deprecated(note = "Use default()", since = "0.5.1")] pub fn new() -> Quantile { Default::default() } pub fn set_quantile(&mut self, v: f64) { self.quantile = v; } pub fn get_quantile(&self) -> f64 { self.quantile } pub fn set_value(&mut self, v: f64) { self.value = v; } pub fn get_value(&self) -> f64 { self.value } } #[derive(PartialEq, Clone, Default, Debug)] pub struct Summary { sample_count: u64, sample_sum: f64, quantile: Vec, } impl Summary { #[deprecated(note = "Use default()", since = "0.5.1")] pub fn new() -> Summary { Default::default() } pub fn set_sample_count(&mut self, v: u64) { self.sample_count = v; } pub fn get_sample_count(&self) -> u64 { self.sample_count } pub fn set_sample_sum(&mut self, v: f64) { self.sample_sum = v; } pub fn get_sample_sum(&self) -> f64 { self.sample_sum } pub fn set_quantile(&mut self, v: Vec) { self.quantile = v; } pub fn get_quantile(&self) -> &[Quantile] { &self.quantile } } #[derive(PartialEq, Clone, Default, Debug)] pub struct Untyped { value: f64, } impl Untyped { #[deprecated(note = "Use default()", since = "0.5.1")] pub fn new() -> Untyped { Default::default() } #[deprecated( note = "Untyped struct is protobuf specific and will be removed in a future version", since = "0.5.1" )] pub fn set_value(&mut self, v: f64) { self.value = v; } #[deprecated( note = "Untyped struct is protobuf specific and will be removed in a future version", since = "0.5.1" )] pub fn get_value(&self) -> f64 { self.value } } #[derive(PartialEq, Clone, Default, Debug)] pub struct Histogram { sample_count: u64, sample_sum: f64, bucket: Vec, } impl Histogram { #[deprecated(note = "Use default()", since = "0.5.1")] pub fn new() -> Histogram { Default::default() } pub fn set_sample_count(&mut self, v: u64) { self.sample_count = v; } pub fn get_sample_count(&self) -> u64 { self.sample_count } pub fn set_sample_sum(&mut self, v: f64) { self.sample_sum = v; } pub fn get_sample_sum(&self) -> f64 { self.sample_sum } pub fn set_bucket(&mut self, v: Vec) { self.bucket = v; } pub fn get_bucket(&self) -> &[Bucket] { &self.bucket } } #[derive(PartialEq, Clone, Default, Debug)] pub struct Bucket { cumulative_count: u64, upper_bound: f64, } impl Bucket { #[deprecated(note = "Use default()", since = "0.5.1")] pub fn new() -> Bucket { Default::default() } pub fn set_cumulative_count(&mut self, v: u64) { self.cumulative_count = v; } pub fn get_cumulative_count(&self) -> u64 { self.cumulative_count } pub fn set_upper_bound(&mut self, v: f64) { self.upper_bound = v; } pub fn get_upper_bound(&self) -> f64 { self.upper_bound } } #[derive(PartialEq, Clone, Default, Debug)] pub struct Metric { // message fields label: Vec, gauge: Gauge, counter: Counter, summary: Summary, untyped: Untyped, histogram: Histogram, timestamp_ms: i64, } impl Metric { #[deprecated(note = "Use default()", since = "0.5.1")] pub fn new() -> Metric { Default::default() } pub fn set_label(&mut self, v: Vec) { self.label = v; } pub fn mut_label(&mut self) -> &mut [LabelPair] { &mut self.label } pub fn take_label(&mut self) -> Vec { ::std::mem::replace(&mut self.label, Vec::new()) } pub fn get_label(&self) -> &[LabelPair] { &self.label } pub fn set_gauge(&mut self, v: Gauge) { self.gauge = v; } pub fn get_gauge(&self) -> &Gauge { &self.gauge } pub fn set_counter(&mut self, v: Counter) { self.counter = v; } pub fn get_counter(&self) -> &Counter { &self.counter } pub fn set_summary(&mut self, v: Summary) { self.summary = v; } pub fn get_summary(&self) -> &Summary { &self.summary } #[deprecated( note = "This method is protobuf specific and will be removed in a future version", since = "0.5.1" )] pub fn set_untyped(&mut self, v: Untyped) { self.untyped = v; } #[deprecated( note = "This method is protobuf specific and will be removed in a future version", since = "0.5.1" )] pub fn get_untyped(&self) -> &Untyped { &self.untyped } pub fn set_histogram(&mut self, v: Histogram) { self.histogram = v; } pub fn get_histogram(&self) -> &Histogram { &self.histogram } pub fn set_timestamp_ms(&mut self, v: i64) { self.timestamp_ms = v; } pub fn get_timestamp_ms(&self) -> i64 { self.timestamp_ms } } #[derive(Clone, PartialEq, Eq, Debug, Hash, Copy)] pub enum MetricType { COUNTER, GAUGE, SUMMARY, UNTYPED, HISTOGRAM, } impl Default for MetricType { fn default() -> Self { MetricType::COUNTER } } #[derive(PartialEq, Clone, Default, Debug)] pub struct MetricFamily { name: String, help: String, field_type: MetricType, metric: Vec, } impl MetricFamily { #[deprecated(note = "Use default()", since = "0.5.1")] pub fn new() -> MetricFamily { Default::default() } pub fn clear_name(&mut self) { self.name.clear(); } pub fn set_name(&mut self, v: String) { self.name = v; } pub fn get_name(&self) -> &str { &self.name } pub fn set_help(&mut self, v: String) { self.help = v; } pub fn get_help(&self) -> &str { &self.help } pub fn set_field_type(&mut self, v: MetricType) { self.field_type = v; } pub fn get_field_type(&self) -> MetricType { self.field_type } #[deprecated( note = "This method is protobuf specific and will be removed in a future version", since = "0.5.1" )] pub fn clear_metric(&mut self) { self.metric.clear(); } pub fn set_metric(&mut self, v: Vec) { self.metric = v; } pub fn mut_metric(&mut self) -> &mut Vec { &mut self.metric } pub fn take_metric(&mut self) -> Vec { ::std::mem::replace(&mut self.metric, Vec::new()) } pub fn get_metric(&self) -> &[Metric] { &self.metric } } prometheus-0.13.4/src/process_collector.rs000064400000000000000000000153511046102023000167720ustar 00000000000000// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. //! Monitor a process. //! //! This module only supports **Linux** platform. use lazy_static::lazy_static; use crate::counter::IntCounter; use crate::desc::Desc; 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; /// 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, thread count, as well as the process /// start time for the given process id. #[derive(Debug)] pub struct ProcessCollector { pid: pid_t, descs: Vec, cpu_total: IntCounter, open_fds: IntGauge, max_fds: IntGauge, vsize: IntGauge, rss: IntGauge, start_time: IntGauge, threads: IntGauge, } impl ProcessCollector { /// Create a `ProcessCollector` with the given process id and namespace. pub fn new>(pid: pid_t, namespace: S) -> ProcessCollector { let namespace = namespace.into(); let mut descs = Vec::new(); let cpu_total = IntCounter::with_opts( Opts::new( "process_cpu_seconds_total", "Total user and system CPU time spent in \ seconds.", ) .namespace(namespace.clone()), ) .unwrap(); descs.extend(cpu_total.desc().into_iter().cloned()); 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 = IntGauge::with_opts( Opts::new( "process_max_fds", "Maximum number of open file descriptors.", ) .namespace(namespace.clone()), ) .unwrap(); descs.extend(max_fds.desc().into_iter().cloned()); let vsize = IntGauge::with_opts( Opts::new( "process_virtual_memory_bytes", "Virtual memory size in bytes.", ) .namespace(namespace.clone()), ) .unwrap(); descs.extend(vsize.desc().into_iter().cloned()); let rss = IntGauge::with_opts( Opts::new( "process_resident_memory_bytes", "Resident memory size in bytes.", ) .namespace(namespace.clone()), ) .unwrap(); descs.extend(rss.desc().into_iter().cloned()); let start_time = IntGauge::with_opts( Opts::new( "process_start_time_seconds", "Start time of the process since unix epoch \ in seconds.", ) .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, open_fds, max_fds, vsize, rss, start_time, threads, } } /// Return a `ProcessCollector` of the calling process. pub fn for_self() -> ProcessCollector { let pid = unsafe { libc::getpid() }; ProcessCollector::new(pid, "") } } impl Collector for ProcessCollector { fn desc(&self) -> Vec<&Desc> { self.descs.iter().collect() } fn collect(&self) -> Vec { let p = match procfs::process::Process::new(self.pid) { Ok(p) => p, Err(..) => { // we can't construct a Process object, so there's no stats to gather return Vec::new(); } }; // file descriptors if let Ok(fd_count) = p.fd_count() { 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 i64) } } 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); // 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); } // collect MetricFamilys. let mut mfs = Vec::with_capacity(METRICS_NUMBER); 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: i64 = { unsafe { libc::sysconf(libc::_SC_CLK_TCK) }.into() }; // getconf PAGESIZE static ref PAGESIZE: i64 = { unsafe { libc::sysconf(libc::_SC_PAGESIZE) }.into() }; } #[cfg(test)] mod tests { use super::*; use crate::metrics::Collector; use crate::registry; #[test] fn test_process_collector() { let pc = ProcessCollector::for_self(); { // Seven metrics per process collector. let descs = pc.desc(); assert_eq!(descs.len(), super::METRICS_NUMBER); let mfs = pc.collect(); assert_eq!(mfs.len(), super::METRICS_NUMBER); } let r = registry::Registry::new(); let res = r.register(Box::new(pc)); assert!(res.is_ok()); } } prometheus-0.13.4/src/pulling_gauge.rs000064400000000000000000000054021046102023000160640ustar 00000000000000use 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 /// /// /// # 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 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", &"") .finish() } } impl PullingGauge { /// Create a new [`PullingGauge`]. pub fn new, S2: Into>( name: S1, help: S2, value: Box f64 + Send + Sync>, ) -> crate::Result { 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()); let mut metric = Metric::default(); metric.set_gauge(gauge); metric } } impl Collector for PullingGauge { fn desc(&self) -> Vec<&crate::core::Desc> { vec![&self.desc] } fn collect(&self) -> Vec { 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(from_vec!(vec![self.metric()])); vec![m] } } #[cfg(test)] mod tests { use super::*; use crate::metrics::Collector; #[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()); } } prometheus-0.13.4/src/push.rs000064400000000000000000000225341046102023000142260ustar 00000000000000// Copyright 2014 The Prometheus Authors // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::collections::HashMap; use std::hash::BuildHasher; use std::str::{self, FromStr}; use std::time::Duration; 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; use crate::proto; use crate::registry::Registry; const REQWEST_TIMEOUT_SEC: Duration = Duration::from_secs(10); lazy_static! { static ref HTTP_CLIENT: Client = Client::builder() .timeout(REQWEST_TIMEOUT_SEC) .build() .unwrap(); } /// `BasicAuthentication` holder for supporting `push` to Pushgateway endpoints /// using Basic access authentication. /// Can be passed to any `push_metrics` method. #[derive(Debug)] pub struct BasicAuthentication { /// The Basic Authentication username (possibly empty string). pub username: String, /// The Basic Authentication password (possibly empty string). pub password: String, } /// `push_metrics` pushes all gathered metrics to the Pushgateway specified by /// url, using the provided job name and the (optional) further grouping labels /// (the grouping map may be nil). See the Pushgateway documentation for /// detailed implications of the job and other grouping labels. Neither the job /// name nor any grouping label value may contain a "/". The metrics pushed must /// not contain a job label of their own nor any of the grouping labels. /// /// You can use just host:port or ip:port as url, in which case 'http://' is /// added automatically. You can also include the schema in the URL. However, do /// not include the '/metrics/jobs/...' part. /// /// Note that all previously pushed metrics with the same job and other grouping /// labels will be replaced with the metrics pushed by this call. (It uses HTTP /// method 'PUT' to push to the Pushgateway.) pub fn push_metrics( job: &str, grouping: HashMap, url: &str, mfs: Vec, basic_auth: Option, ) -> Result<()> { push(job, grouping, url, mfs, "PUT", basic_auth) } /// `push_add_metrics` works like `push_metrics`, but only previously pushed /// metrics with the same name (and the same job and other grouping labels) will /// be replaced. (It uses HTTP method 'POST' to push to the Pushgateway.) pub fn push_add_metrics( job: &str, grouping: HashMap, url: &str, mfs: Vec, basic_auth: Option, ) -> Result<()> { push(job, grouping, url, mfs, "POST", basic_auth) } const LABEL_NAME_JOB: &str = "job"; fn push( job: &str, grouping: HashMap, url: &str, mfs: Vec, method: &str, basic_auth: Option, ) -> Result<()> { // Suppress clippy warning needless_pass_by_value. let grouping = grouping; let mut push_url = if url.contains("://") { url.to_owned() } else { format!("http://{}", url) }; if push_url.ends_with('/') { push_url.pop(); } let mut url_components = Vec::new(); if job.contains('/') { return Err(Error::Msg(format!("job contains '/': {}", job))); } // TODO: escape job url_components.push(job.to_owned()); for (ln, lv) in &grouping { // TODO: check label name if lv.contains('/') { return Err(Error::Msg(format!( "value of grouping label {} contains '/': {}", ln, lv ))); } url_components.push(ln.to_owned()); url_components.push(lv.to_owned()); } push_url = format!("{}/metrics/job/{}", push_url, url_components.join("/")); let encoder = ProtobufEncoder::new(); let mut buf = Vec::new(); for mf in mfs { // Check for pre-existing grouping labels: for m in mf.get_metric() { for lp in m.get_label() { if lp.get_name() == LABEL_NAME_JOB { return Err(Error::Msg(format!( "pushed metric {} already contains a \ job label", mf.get_name() ))); } if grouping.contains_key(lp.get_name()) { return Err(Error::Msg(format!( "pushed metric {} already contains \ grouping label {}", mf.get_name(), lp.get_name() ))); } } } // Ignore error, `no metrics` and `no name`. let _ = encoder.encode(&[mf], &mut buf); } let mut builder = HTTP_CLIENT .request( Method::from_str(method).unwrap(), Url::from_str(&push_url).unwrap(), ) .header(CONTENT_TYPE, encoder.format_type()) .body(buf); if let Some(BasicAuthentication { username, password }) = basic_auth { builder = builder.basic_auth(username, Some(password)); } let response = builder.send().map_err(|e| Error::Msg(format!("{}", e)))?; match response.status() { StatusCode::ACCEPTED => Ok(()), StatusCode::OK => Ok(()), _ => Err(Error::Msg(format!( "unexpected status code {} while pushing to {}", response.status(), push_url ))), } } fn push_from_collector( job: &str, grouping: HashMap, url: &str, collectors: Vec>, method: &str, basic_auth: Option, ) -> Result<()> { let registry = Registry::new(); for bc in collectors { registry.register(bc)?; } let mfs = registry.gather(); push(job, grouping, url, mfs, method, basic_auth) } /// `push_collector` push metrics collected from the provided collectors. It is /// a convenient way to push only a few metrics. pub fn push_collector( job: &str, grouping: HashMap, url: &str, collectors: Vec>, basic_auth: Option, ) -> Result<()> { push_from_collector(job, grouping, url, collectors, "PUT", basic_auth) } /// `push_add_collector` works like `push_add_metrics`, it collects from the /// provided collectors. It is a convenient way to push only a few metrics. pub fn push_add_collector( job: &str, grouping: HashMap, url: &str, collectors: Vec>, basic_auth: Option, ) -> Result<()> { push_from_collector(job, grouping, url, collectors, "POST", basic_auth) } const DEFAULT_GROUP_LABEL_PAIR: (&str, &str) = ("instance", "unknown"); /// `hostname_grouping_key` returns a label map with the only entry /// {instance=""}. This can be conveniently used as the grouping /// parameter if metrics should be pushed with the hostname as label. The /// returned map is created upon each call so that the caller is free to add more /// labels to the map. /// /// Note: This function returns `instance = "unknown"` in Windows. #[cfg(not(target_os = "windows"))] pub fn hostname_grouping_key() -> HashMap { // Host names are limited to 255 bytes. // ref: http://pubs.opengroup.org/onlinepubs/7908799/xns/gethostname.html let max_len = 256; let mut name = vec![0u8; max_len]; match unsafe { libc::gethostname( name.as_mut_ptr() as *mut libc::c_char, max_len as libc::size_t, ) } { 0 => { let last_char = name.iter().position(|byte| *byte == 0).unwrap_or(max_len); labels! { DEFAULT_GROUP_LABEL_PAIR.0.to_owned() => str::from_utf8(&name[..last_char]) .unwrap_or(DEFAULT_GROUP_LABEL_PAIR.1).to_owned(), } } _ => { labels! {DEFAULT_GROUP_LABEL_PAIR.0.to_owned() => DEFAULT_GROUP_LABEL_PAIR.1.to_owned(),} } } } #[cfg(target_os = "windows")] pub fn hostname_grouping_key() -> HashMap { labels! {DEFAULT_GROUP_LABEL_PAIR.0.to_owned() => DEFAULT_GROUP_LABEL_PAIR.1.to_owned(),} } #[cfg(test)] mod tests { use super::*; use crate::proto; #[test] fn test_hostname_grouping_key() { let map = hostname_grouping_key(); assert!(!map.is_empty()); } #[test] fn test_push_bad_label_name() { let table = vec![ // Error message: "pushed metric {} already contains a job label" (LABEL_NAME_JOB, "job label"), // Error message: "pushed metric {} already contains grouping label {}" (DEFAULT_GROUP_LABEL_PAIR.0, "grouping label"), ]; for case in table { 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])); let mut mf = proto::MetricFamily::new(); mf.set_metric(from_vec!(vec![m])); let res = push_metrics("test", hostname_grouping_key(), "mockurl", vec![mf], None); assert!(format!("{}", res.unwrap_err()).contains(case.1)); } } } prometheus-0.13.4/src/registry.rs000064400000000000000000000471711046102023000151230ustar 00000000000000// Copyright 2014 The Prometheus Authors // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::collections::btree_map::Entry as BEntry; use std::collections::hash_map::Entry as HEntry; use std::collections::{BTreeMap, HashMap, HashSet}; use std::sync::Arc; use parking_lot::RwLock; 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>, pub dim_hashes_by_name: HashMap, pub desc_ids: HashSet, /// Optional common labels for all registered collectors. pub labels: Option>, /// Optional common namespace for all registered collectors. pub prefix: Option, } impl std::fmt::Debug for RegistryCore { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "RegistryCore ({} collectors)", self.collectors_by_id.keys().len() ) } } impl RegistryCore { fn register(&mut self, c: Box) -> Result<()> { let mut desc_id_set = HashSet::new(); let mut collector_id: u64 = 0; for desc in c.desc() { // Is the desc_id unique? // (In other words: Is the fqName + constLabel combination unique?) if self.desc_ids.contains(&desc.id) { return Err(Error::AlreadyReg); } if let Some(hash) = self.dim_hashes_by_name.get(&desc.fq_name) { if *hash != desc.dim_hash { return Err(Error::Msg(format!( "a previously registered descriptor with the \ same fully-qualified name as {:?} has \ different label names or a different help \ string", desc ))); } } self.dim_hashes_by_name .insert(desc.fq_name.clone(), desc.dim_hash); // If it is not a duplicate desc in this collector, add it to // the collector_id. if desc_id_set.insert(desc.id) { // The set did not have this value present, true is returned. collector_id = collector_id.wrapping_add(desc.id); } else { // The set did have this value present, false is returned. // // TODO: Should we allow duplicate descs within the same collector? return Err(Error::Msg(format!( "a duplicate descriptor within the same \ collector the same fully-qualified name: {:?}", desc.fq_name ))); } } match self.collectors_by_id.entry(collector_id) { HEntry::Vacant(vc) => { self.desc_ids.extend(desc_id_set); vc.insert(c); Ok(()) } HEntry::Occupied(_) => Err(Error::AlreadyReg), } } fn unregister(&mut self, c: Box) -> Result<()> { let mut id_set = Vec::new(); let mut collector_id: u64 = 0; for desc in c.desc() { if !id_set.iter().any(|id| *id == desc.id) { id_set.push(desc.id); collector_id = collector_id.wrapping_add(desc.id); } } if self.collectors_by_id.remove(&collector_id).is_none() { return Err(Error::Msg(format!( "collector {:?} is not registered", c.desc() ))); } for id in id_set { self.desc_ids.remove(&id); } // dim_hashes_by_name is left untouched as those must be consistent // throughout the lifetime of a program. Ok(()) } fn gather(&self) -> Vec { let mut mf_by_name = BTreeMap::new(); for c in self.collectors_by_id.values() { let mfs = c.collect(); for mut mf in mfs { // Prune empty MetricFamilies. if mf.get_metric().is_empty() { continue; } let name = mf.get_name().to_owned(); match mf_by_name.entry(name) { BEntry::Vacant(entry) => { entry.insert(mf); } BEntry::Occupied(mut entry) => { let existent_mf = entry.get_mut(); let existent_metrics = existent_mf.mut_metric(); // TODO: check type. // TODO: check consistency. for metric in mf.take_metric().into_iter() { existent_metrics.push(metric); } } } } } // TODO: metric_family injection hook. // Now that MetricFamilies are all set, sort their Metrics // lexicographically by their label values. for mf in mf_by_name.values_mut() { mf.mut_metric().sort_by(|m1, m2| { let lps1 = m1.get_label(); let lps2 = m2.get_label(); if lps1.len() != lps2.len() { // This should not happen. The metrics are // inconsistent. However, we have to deal with the fact, as // people might use custom collectors or metric family injection // to create inconsistent metrics. So let's simply compare the // number of labels in this case. That will still yield // reproducible sorting. return lps1.len().cmp(&lps2.len()); } for (lp1, lp2) in lps1.iter().zip(lps2.iter()) { if lp1.get_value() != lp2.get_value() { return lp1.get_value().cmp(lp2.get_value()); } } // We should never arrive here. Multiple metrics with the same // label set in the same scrape will lead to undefined ingestion // behavior. However, as above, we have to provide stable sorting // 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()) }); } // Write out MetricFamilies sorted by their name. mf_by_name .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()); m.set_name(prefixed); } // Add registry common labels, if any. if let Some(ref hmap) = self.labels { let pairs: Vec = hmap .iter() .map(|(k, v)| { let mut label = proto::LabelPair::default(); label.set_name(k.to_string()); label.set_value(v.to_string()); label }) .collect(); for metric in m.mut_metric().iter_mut() { let mut labels: Vec<_> = metric.take_label().into(); labels.append(&mut pairs.clone()); metric.set_label(labels.into()); } } m }) .collect() } } /// A struct for registering Prometheus collectors, collecting their metrics, and gathering /// them into `MetricFamilies` for exposition. #[derive(Clone, Default, Debug)] pub struct Registry { r: Arc>, } impl Registry { /// `new` creates a Registry. pub fn new() -> Registry { Default::default() } /// Create a new registry, with optional custom prefix and labels. pub fn new_custom( prefix: Option, labels: Option>, ) -> Result { if let Some(ref namespace) = prefix { if namespace.is_empty() { return Err(Error::Msg("empty prefix namespace".to_string())); } } let reg = Registry::default(); { let mut core = reg.r.write(); core.prefix = prefix; core.labels = labels; } Ok(reg) } /// `register` registers a new [`Collector`] to be included in metrics /// collection. It returns an error if the descriptors provided by the /// [`Collector`] are invalid or if they — in combination with descriptors of /// already registered Collectors — do not fulfill the consistency and /// uniqueness criteria described in the documentation of [`Desc`](crate::core::Desc). /// /// If the provided [`Collector`] is equal to a [`Collector`] already registered /// (which includes the case of re-registering the same [`Collector`]), the /// AlreadyReg error returns. pub fn register(&self, c: Box) -> Result<()> { self.r.write().register(c) } /// `unregister` unregisters the [`Collector`] that equals the [`Collector`] passed /// in as an argument. (Two Collectors are considered equal if their /// Describe method yields the same set of descriptors.) The function /// returns error when the [`Collector`] is not registered. pub fn unregister(&self, c: Box) -> Result<()> { self.r.write().unregister(c) } /// `gather` calls the Collect method of the registered Collectors and then /// gathers the collected metrics into a lexicographically sorted slice /// of MetricFamily protobufs. pub fn gather(&self) -> Vec { self.r.read().gather() } } cfg_if! { if #[cfg(all(feature = "process", target_os="linux"))] { fn register_default_process_collector(reg: &Registry) -> Result<()> { use crate::process_collector::ProcessCollector; let pc = ProcessCollector::for_self(); reg.register(Box::new(pc)) } } else { fn register_default_process_collector(_: &Registry) -> Result<()> { Ok(()) } } } // Default registry for rust-prometheus. lazy_static! { static ref DEFAULT_REGISTRY: Registry = { let reg = Registry::default(); // Register a default process collector. register_default_process_collector(®).unwrap(); reg }; } /// Default registry (global static). pub fn default_registry() -> &'static Registry { lazy_static::initialize(&DEFAULT_REGISTRY); &DEFAULT_REGISTRY } /// Registers a new [`Collector`] to be included in metrics collection. It /// returns an error if the descriptors provided by the [`Collector`] are invalid or /// if they - in combination with descriptors of already registered Collectors - /// do not fulfill the consistency and uniqueness criteria described in the /// [`Desc`](crate::core::Desc) documentation. pub fn register(c: Box) -> Result<()> { DEFAULT_REGISTRY.register(c) } /// Unregisters the [`Collector`] that equals the [`Collector`] passed in as /// an argument. (Two Collectors are considered equal if their Describe method /// yields the same set of descriptors.) The function returns an error if a /// [`Collector`] was not registered. pub fn unregister(c: Box) -> Result<()> { DEFAULT_REGISTRY.unregister(c) } /// Return all `MetricFamily` of `DEFAULT_REGISTRY`. pub fn gather() -> Vec { DEFAULT_REGISTRY.gather() } #[cfg(test)] mod tests { use std::collections::HashMap; use std::thread; use super::*; use crate::counter::{Counter, CounterVec}; use crate::desc::Desc; use crate::metrics::{Collector, Opts}; use crate::proto; #[test] fn test_registry() { let r = Registry::new(); let counter = Counter::new("test", "test help").unwrap(); r.register(Box::new(counter.clone())).unwrap(); counter.inc(); let r1 = r.clone(); let handler = thread::spawn(move || { let metric_families = r1.gather(); assert_eq!(metric_families.len(), 1); }); assert!(handler.join().is_ok()); assert!(r.register(Box::new(counter.clone())).is_err()); assert!(r.unregister(Box::new(counter.clone())).is_ok()); assert!(r.unregister(Box::new(counter.clone())).is_err()); assert!(r.register(Box::new(counter.clone())).is_ok()); let counter_vec = CounterVec::new(Opts::new("test_vec", "test vec help"), &["a", "b"]).unwrap(); r.register(Box::new(counter_vec.clone())).unwrap(); counter_vec.with_label_values(&["1", "2"]).inc(); } #[test] fn test_default_registry() { let counter = Counter::new("test", "test help").unwrap(); assert!(register(Box::new(counter.clone())).is_ok()); assert_ne!(gather().len(), 0); assert_ne!(default_registry().gather().len(), 0); assert_eq!(gather().len(), default_registry().gather().len()); assert!(unregister(Box::new(counter.clone())).is_ok()); assert!(unregister(Box::new(counter.clone())).is_err()); assert!(default_registry() .unregister(Box::new(counter.clone())) .is_err()); assert!(register(Box::new(counter.clone())).is_ok()); } #[test] fn test_gather_order() { let r = Registry::new(); let counter_a = Counter::new("test_a_counter", "test help").unwrap(); let counter_b = Counter::new("test_b_counter", "test help").unwrap(); let counter_2 = Counter::new("test_2_counter", "test help").unwrap(); r.register(Box::new(counter_b.clone())).unwrap(); r.register(Box::new(counter_2.clone())).unwrap(); r.register(Box::new(counter_a.clone())).unwrap(); 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"); let r = Registry::new(); let opts = Opts::new("test", "test help") .const_label("a", "1") .const_label("b", "2"); let counter_vec = CounterVec::new(opts, &["cc", "c1", "a2", "c0"]).unwrap(); r.register(Box::new(counter_vec.clone())).unwrap(); let mut map1 = HashMap::new(); map1.insert("cc", "12"); map1.insert("c1", "a1"); map1.insert("a2", "0"); map1.insert("c0", "hello"); counter_vec.with(&map1).inc(); let mut map2 = HashMap::new(); map2.insert("cc", "12"); map2.insert("c1", "0"); map2.insert("a2", "0"); map2.insert("c0", "hello"); counter_vec.with(&map2).inc(); counter_vec.with(&map2).inc(); let mut map3 = HashMap::new(); map3.insert("cc", "12"); map3.insert("c1", "0"); map3.insert("a2", "da"); map3.insert("c0", "hello"); counter_vec.with(&map3).inc(); counter_vec.with(&map3).inc(); counter_vec.with(&map3).inc(); let mut map4 = HashMap::new(); map4.insert("cc", "12"); map4.insert("c1", "0"); map4.insert("a2", "da"); map4.insert("c0", "你好"); counter_vec.with(&map4).inc(); counter_vec.with(&map4).inc(); counter_vec.with(&map4).inc(); counter_vec.with(&map4).inc(); // # HELP test test help // # TYPE test counter // test{a="1",a2="0",b="2",c0="hello",c1="0",cc="12"} 2 // test{a="1",a2="0",b="2",c0="hello",c1="a1",cc="12"} 1 // test{a="1",a2="da",b="2",c0="hello",c1="0",cc="12"} 3 // test{a="1",a2="da",b="2",c0="你好",c1="0",cc="12"} 4 let mfs = r.gather(); assert_eq!(mfs.len(), 1); let ms = mfs[0].get_metric(); assert_eq!(ms.len(), 4); assert_eq!(ms[0].get_counter().get_value() as u64, 2); assert_eq!(ms[1].get_counter().get_value() as u64, 1); assert_eq!(ms[2].get_counter().get_value() as u64, 3); assert_eq!(ms[3].get_counter().get_value() as u64, 4); } #[test] fn test_with_prefix_gather() { assert!(Registry::new_custom(Some("".to_string()), None).is_err()); let r = Registry::new_custom(Some("common_prefix".to_string()), None).unwrap(); let counter_a = Counter::new("test_a_counter", "test help").unwrap(); r.register(Box::new(counter_a.clone())).unwrap(); let mfs = r.gather(); assert_eq!(mfs.len(), 1); assert_eq!(mfs[0].get_name(), "common_prefix_test_a_counter"); } #[test] fn test_with_labels_gather() { let mut labels = HashMap::new(); labels.insert("tkey".to_string(), "tvalue".to_string()); let r = Registry::new_custom(None, Some(labels)).unwrap(); let counter_a = Counter::new("test_a_counter", "test help").unwrap(); r.register(Box::new(counter_a.clone())).unwrap(); let counter_vec = CounterVec::new(Opts::new("test_vec", "test vec help"), &["a", "b"]).unwrap(); r.register(Box::new(counter_vec.clone())).unwrap(); counter_vec.with_label_values(&["one", "two"]).inc(); counter_vec.with_label_values(&["three", "four"]).inc(); 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"); let mut needle = proto::LabelPair::default(); needle.set_name("tkey".to_string()); needle.set_value("tvalue".to_string()); let metrics = mfs[0].get_metric(); for m in metrics { assert!(m.get_label().contains(&needle)); } let metrics = mfs[1].get_metric(); for m in metrics { assert!(m.get_label().contains(&needle)); } } struct MultipleCollector { descs: Vec, counters: Vec, } impl Collector for MultipleCollector { fn desc(&self) -> Vec<&Desc> { self.descs.iter().collect() } fn collect(&self) -> Vec { self.counters .iter() .inspect(|c| c.inc()) .map(|c| c.collect()) .fold(Vec::new(), |mut acc, mfs| { acc.extend(mfs); acc }) } } #[test] fn test_register_multiplecollector() { let counters = vec![ Counter::new("c1", "c1 is a counter").unwrap(), Counter::new("c2", "c2 is a counter").unwrap(), ]; let descs = counters.iter().map(|c| c.desc().into_iter().cloned()).fold( Vec::new(), |mut acc, ds| { acc.extend(ds); acc }, ); let mc = MultipleCollector { descs, counters }; let r = Registry::new(); r.register(Box::new(mc)).unwrap(); } #[test] fn test_prune_empty_metric_family() { let counter_vec = CounterVec::new(Opts::new("test_vec", "test vec help"), &["a", "b"]).unwrap(); let r = Registry::new(); r.register(Box::new(counter_vec.clone())).unwrap(); assert!(r.gather().is_empty()); counter_vec.with_label_values(&["1", "2"]).inc(); assert!(!r.gather().is_empty()); } } prometheus-0.13.4/src/timer.rs000064400000000000000000000045601046102023000143660ustar 00000000000000use 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! { static ref ANCHOR: Instant = Instant::now(); } /// Convert a duration to millisecond. #[inline] pub fn duration_to_millis(dur: Duration) -> u64 { dur.as_secs() * 1000 + dur.subsec_millis() as u64 } /// Returns milliseconds since ANCHOR. /// /// ANCHOR is some fixed point in history. pub fn now_millis() -> u64 { let res = Instant::now(); let t = duration_to_millis(res.saturating_duration_since(*ANCHOR)); let mut recent = RECENT.load(Ordering::Relaxed); loop { if recent > t { return recent; } match RECENT.compare_exchange_weak(recent, t, Ordering::Relaxed, Ordering::Relaxed) { Ok(_) => return t, Err(r) => recent = r, } } } /// Returns recent returned value by `now_millis`. pub fn recent_millis() -> u64 { RECENT.load(Ordering::Relaxed) } lazy_static! { static ref UPDATER_IS_RUNNING: AtomicBool = AtomicBool::new(false); } const CHECK_UPDATE_INTERVAL: Duration = Duration::from_millis(200); /// Ensures background updater is running, which will call `now_millis` periodically. pub fn ensure_updater() { if UPDATER_IS_RUNNING .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) .is_ok() { std::thread::Builder::new() .name("time updater".to_owned()) .spawn(|| loop { thread::sleep(CHECK_UPDATE_INTERVAL); now_millis(); }) .unwrap(); } } #[cfg(test)] mod tests { use std::thread; use std::time::Duration; #[test] fn test_duration_to_millis() { let cases = vec![(1, 1, 1000), (0, 1_000_000, 1), (3, 103_000_000, 3103)]; for (secs, nanos, exp) in cases { let dur = Duration::new(secs, nanos); assert_eq!(super::duration_to_millis(dur), exp); } } #[test] fn test_time_update() { assert_eq!(super::recent_millis(), 0); let now = super::now_millis(); assert_eq!(super::recent_millis(), now); super::ensure_updater(); thread::sleep(super::CHECK_UPDATE_INTERVAL * 2); assert!(super::recent_millis() > now); } } prometheus-0.13.4/src/value.rs000064400000000000000000000076341046102023000143670ustar 00000000000000// Copyright 2014 The Prometheus Authors // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use crate::atomic64::{Atomic, Number}; use crate::desc::{Desc, Describer}; use crate::errors::{Error, Result}; use crate::proto::{Counter, Gauge, LabelPair, Metric, MetricFamily, MetricType}; /// `ValueType` is an enumeration of metric types that represent a simple value /// for [`Counter`] and [`Gauge`]. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum ValueType { Counter, Gauge, } impl ValueType { /// `metric_type` returns the corresponding proto metric type. pub fn metric_type(self) -> MetricType { match self { ValueType::Counter => MetricType::COUNTER, ValueType::Gauge => MetricType::GAUGE, } } } /// A generic metric for [`Counter`] and [`Gauge`]. /// Its effective type is determined by `ValueType`. This is a low-level /// building block used by the library to back the implementations of /// [`Counter`] and [`Gauge`]. #[derive(Debug)] pub struct Value { pub desc: Desc, pub val: P, pub val_type: ValueType, pub label_pairs: Vec, } impl Value

{ pub fn new( describer: &D, val_type: ValueType, val: P::T, label_values: &[&str], ) -> Result { let desc = describer.describe()?; let label_pairs = make_label_pairs(&desc, label_values)?; Ok(Self { desc, val: P::new(val), val_type, label_pairs, }) } #[inline] pub fn get(&self) -> P::T { self.val.get() } #[inline] pub fn set(&self, val: P::T) { self.val.set(val); } #[inline] pub fn inc_by(&self, val: P::T) { self.val.inc_by(val); } #[inline] pub fn inc(&self) { self.inc_by(P::T::from_i64(1)); } #[inline] pub fn dec(&self) { self.dec_by(P::T::from_i64(1)); } #[inline] pub fn dec_by(&self, val: P::T) { self.val.dec_by(val) } pub fn metric(&self) -> Metric { let mut m = Metric::default(); m.set_label(from_vec!(self.label_pairs.clone())); let val = self.get(); match self.val_type { ValueType::Counter => { let mut counter = Counter::default(); counter.set_value(val.into_f64()); m.set_counter(counter); } ValueType::Gauge => { let mut gauge = Gauge::default(); gauge.set_value(val.into_f64()); m.set_gauge(gauge); } } m } pub fn collect(&self) -> 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(self.val_type.metric_type()); m.set_metric(from_vec!(vec![self.metric()])); m } } pub fn make_label_pairs(desc: &Desc, label_values: &[&str]) -> Result> { if desc.variable_labels.len() != label_values.len() { return Err(Error::InconsistentCardinality { expect: desc.variable_labels.len(), got: label_values.len(), }); } let total_len = desc.variable_labels.len() + desc.const_label_pairs.len(); if total_len == 0 { return Ok(vec![]); } if desc.variable_labels.is_empty() { return Ok(desc.const_label_pairs.clone()); } let mut label_pairs = Vec::with_capacity(total_len); 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_pairs.push(label_pair); } for label_pair in &desc.const_label_pairs { label_pairs.push(label_pair.clone()); } label_pairs.sort(); Ok(label_pairs) } prometheus-0.13.4/src/vec.rs000064400000000000000000000353641046102023000140310ustar 00000000000000// Copyright 2014 The Prometheus Authors // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use std::collections::HashMap; use std::hash::Hasher; use std::sync::Arc; use fnv::FnvHasher; use parking_lot::RwLock; use crate::desc::{Desc, Describer}; use crate::errors::{Error, Result}; use crate::metrics::{Collector, Metric}; use crate::proto::{MetricFamily, MetricType}; /// An interface for building a metric vector. pub trait MetricVecBuilder: Send + Sync + Clone { /// The associated Metric collected. type M: Metric; /// The associated describer. type P: Describer + Sync + Send + Clone; /// `build` builds a [`Metric`] with option and corresponding label names. fn build(&self, _: &Self::P, _: &[&str]) -> Result; } #[derive(Debug)] pub(crate) struct MetricVecCore { pub children: RwLock>, pub desc: Desc, pub metric_type: MetricType, pub new_metric: T, pub opts: T::P, } impl MetricVecCore { pub fn collect(&self) -> 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(self.metric_type); let children = self.children.read(); let mut metrics = Vec::with_capacity(children.len()); for child in children.values() { metrics.push(child.metric()); } m.set_metric(from_vec!(metrics)); m } pub fn get_metric_with_label_values(&self, vals: &[&str]) -> Result { let h = self.hash_label_values(vals)?; if let Some(metric) = self.children.read().get(&h).cloned() { return Ok(metric); } self.get_or_create_metric(h, vals) } pub fn get_metric_with(&self, labels: &HashMap<&str, &str>) -> Result { let h = self.hash_labels(labels)?; if let Some(metric) = self.children.read().get(&h).cloned() { return Ok(metric); } let vals = self.get_label_values(labels)?; self.get_or_create_metric(h, &vals) } pub fn delete_label_values(&self, vals: &[&str]) -> Result<()> { let h = self.hash_label_values(vals)?; let mut children = self.children.write(); if children.remove(&h).is_none() { return Err(Error::Msg(format!("missing label values {:?}", vals))); } Ok(()) } pub fn delete(&self, labels: &HashMap<&str, &str>) -> Result<()> { let h = self.hash_labels(labels)?; let mut children = self.children.write(); if children.remove(&h).is_none() { return Err(Error::Msg(format!("missing labels {:?}", labels))); } Ok(()) } /// `reset` deletes all metrics in this vector. pub fn reset(&self) { self.children.write().clear(); } pub(crate) fn hash_label_values(&self, vals: &[&str]) -> Result { if vals.len() != self.desc.variable_labels.len() { return Err(Error::InconsistentCardinality { expect: self.desc.variable_labels.len(), got: vals.len(), }); } let mut h = FnvHasher::default(); for val in vals { h.write(val.as_bytes()); } Ok(h.finish()) } fn hash_labels(&self, labels: &HashMap<&str, &str>) -> Result { if labels.len() != self.desc.variable_labels.len() { return Err(Error::InconsistentCardinality { expect: self.desc.variable_labels.len(), got: labels.len(), }); } 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()), None => { return Err(Error::Msg(format!( "label name {} missing in label map", name ))); } } } Ok(h.finish()) } fn get_label_values<'a>(&self, labels: &'a HashMap<&str, &str>) -> Result> { let mut values = Vec::new(); for name in &self.desc.variable_labels { match labels.get(&name.as_ref()) { Some(val) => values.push(*val), None => { return Err(Error::Msg(format!( "label name {} missing in label map", name ))); } } } Ok(values) } fn get_or_create_metric(&self, hash: u64, label_values: &[&str]) -> Result { let mut children = self.children.write(); // Check exist first. if let Some(metric) = children.get(&hash).cloned() { return Ok(metric); } let metric = self.new_metric.build(&self.opts, label_values)?; children.insert(hash, metric.clone()); Ok(metric) } } /// A [`Collector`] to bundle metrics of the same name that /// differ in their label values. It is usually not used directly but as a /// building block for implementations of vectors of a given metric /// type. [`GaugeVec`](crate::GaugeVec) and [`CounterVec`](crate::CounterVec) /// are examples already provided in this package. #[derive(Clone)] pub struct MetricVec { pub(crate) v: Arc>, } impl std::fmt::Debug for MetricVec { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "MetricVec") } } impl MetricVec { /// `create` creates a MetricVec with description `desc`, a metric type `metric_type` and /// a MetricVecBuilder `new_metric`. pub fn create(metric_type: MetricType, new_metric: T, opts: T::P) -> Result> { let desc = opts.describe()?; let v = MetricVecCore { children: RwLock::new(HashMap::new()), desc, metric_type, new_metric, opts, }; Ok(MetricVec { v: Arc::new(v) }) } /// `get_metric_with_label_values` returns the [`Metric`] for the given slice /// of label values (same order as the VariableLabels in Desc). If that combination of /// label values is accessed for the first time, a new [`Metric`] is created. /// /// It is possible to call this method without using the returned [`Metric`] /// to only create the new [`Metric`] but leave it at its start value (e.g. a /// [`Histogram`](crate::Histogram) without any observations). /// /// Keeping the [`Metric`] for later use is possible (and should be considered /// if performance is critical), but keep in mind that Reset, DeleteLabelValues and Delete can /// be used to delete the [`Metric`] from the MetricVec. In that case, the /// [`Metric`] will still exist, but it will not be exported anymore, even if a /// [`Metric`] with the same label values is created later. See also the /// CounterVec example. /// /// An error is returned if the number of label values is not the same as the /// number of VariableLabels in Desc. /// /// Note that for more than one label value, this method is prone to mistakes /// caused by an incorrect order of arguments. Consider get_metric_with(labels) as /// 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 { self.v.get_metric_with_label_values(vals) } /// `get_metric_with` returns the [`Metric`] for the given Labels map (the /// label names must match those of the VariableLabels in Desc). If that label map is /// accessed for the first time, a new [`Metric`] is created. Implications of /// creating a [`Metric`] without using it and keeping the /// [`Metric`] for later use are the same as for GetMetricWithLabelValues. /// /// An error is returned if the number and names of the Labels are inconsistent /// with those of the VariableLabels in Desc. /// /// 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 { self.v.get_metric_with(labels) } /// `with_label_values` works as `get_metric_with_label_values`, but panics if an error /// occurs. /// /// # Examples /// /// ``` /// use prometheus::{CounterVec, Opts}; /// let vec = CounterVec::new( /// Opts::new("requests_total", "Number of requests."), /// &["code", "http_method"] /// ).unwrap(); /// vec.with_label_values(&["404", "POST"]).inc() /// ``` pub fn with_label_values(&self, vals: &[&str]) -> T::M { 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 { self.get_metric_with(labels).unwrap() } /// `remove_label_values` removes the metric where the variable labels are the same /// as those passed in as labels (same order as the VariableLabels in Desc). It /// returns true if a metric was deleted. /// /// It returns an error if the number of label values is not the same as the /// number of VariableLabels in Desc. /// /// Note that for more than one label value, this method is prone to mistakes /// caused by an incorrect order of arguments. Consider delete(labels) as 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 remove_label_values(&self, vals: &[&str]) -> Result<()> { self.v.delete_label_values(vals) } /// `remove` removes the metric where the variable labels are the same as those /// passed in as labels. It returns true if a metric was deleted. /// /// It returns an error if the number and names of the Labels are inconsistent /// with those of the VariableLabels in the Desc of the MetricVec. /// /// 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<()> { self.v.delete(labels) } /// `reset` deletes all metrics in this vector. pub fn reset(&self) { self.v.reset() } } impl Collector for MetricVec { fn desc(&self) -> Vec<&Desc> { vec![&self.v.desc] } fn collect(&self) -> Vec { vec![self.v.collect()] } } #[cfg(test)] mod tests { use std::collections::HashMap; use crate::counter::CounterVec; use crate::gauge::GaugeVec; use crate::metrics::{Metric, Opts}; #[test] fn test_counter_vec_with_labels() { let vec = CounterVec::new( Opts::new("test_couter_vec", "test counter 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(); assert!(vec.remove(&labels).is_ok()); assert!(vec.remove(&labels).is_err()); let mut labels2 = HashMap::new(); labels2.insert("l1", "v2"); labels2.insert("l2", "v1"); vec.with(&labels).inc(); assert!(vec.remove(&labels2).is_err()); vec.with(&labels).inc(); let mut labels3 = HashMap::new(); labels3.insert("l1", "v1"); assert!(vec.remove(&labels3).is_err()); } #[test] fn test_counter_vec_with_label_values() { let vec = CounterVec::new( Opts::new("test_vec", "test counter vec help"), &["l1", "l2"], ) .unwrap(); assert!(vec.remove_label_values(&["v1", "v2"]).is_err()); vec.with_label_values(&["v1", "v2"]).inc(); assert!(vec.remove_label_values(&["v1", "v2"]).is_ok()); vec.with_label_values(&["v1", "v2"]).inc(); assert!(vec.remove_label_values(&["v1"]).is_err()); assert!(vec.remove_label_values(&["v1", "v3"]).is_err()); } #[test] fn test_gauge_vec_with_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( Opts::new("test_gauge_vec", "test gauge vec help"), &["l1", "l2"], ) .unwrap(); assert!(vec.remove_label_values(&["v1", "v2"]).is_err()); vec.with_label_values(&["v1", "v2"]).inc(); assert!(vec.remove_label_values(&["v1", "v2"]).is_ok()); vec.with_label_values(&["v1", "v2"]).inc(); vec.with_label_values(&["v1", "v2"]).dec(); vec.with_label_values(&["v1", "v2"]).add(42.0); vec.with_label_values(&["v1", "v2"]).sub(42.0); vec.with_label_values(&["v1", "v2"]).set(42.0); assert!(vec.remove_label_values(&["v1"]).is_err()); assert!(vec.remove_label_values(&["v1", "v3"]).is_err()); } #[test] fn test_vec_get_metric_with() { let vec = CounterVec::new( Opts::new("test_vec", "test counter vec help"), &["b", "c", "a"], ) .unwrap(); // create a new metric that labels are {b" => "c", "c" => "a" "a" => "b"}. let mut labels = HashMap::new(); labels.insert("a", "b"); labels.insert("b", "c"); labels.insert("c", "a"); let c = vec.get_metric_with(&labels).unwrap(); let m = c.metric(); 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()]); } } }