├── .github ├── FUNDING.yml └── workflows │ ├── jekyll-gh-pages.yml │ └── main.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── LICENSE ├── NOTICE ├── README.md ├── composer.json ├── docs ├── danog │ └── BetterPrometheus │ │ ├── BetterCollector.md │ │ ├── BetterCollectorRegistry.md │ │ ├── BetterCounter.md │ │ ├── BetterGauge.md │ │ ├── BetterHistogram.md │ │ └── BetterSummary.md └── index.md ├── examples └── 1-all.php ├── lib ├── BetterCollector.php ├── BetterCollectorRegistry.php ├── BetterCounter.php ├── BetterGauge.php ├── BetterHistogram.php └── BetterSummary.php └── psalm.xml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: danog 2 | -------------------------------------------------------------------------------- /.github/workflows/jekyll-gh-pages.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages 2 | name: Deploy Jekyll with GitHub Pages dependencies preinstalled 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["master"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Build job 26 | build: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | - name: Setup Pages 32 | uses: actions/configure-pages@v5 33 | - name: Build with Jekyll 34 | uses: actions/jekyll-build-pages@v1 35 | with: 36 | source: ./ 37 | destination: ./_site 38 | - name: Upload artifact 39 | uses: actions/upload-pages-artifact@v3 40 | 41 | # Deployment job 42 | deploy: 43 | environment: 44 | name: github-pages 45 | url: ${{ steps.deployment.outputs.page_url }} 46 | runs-on: ubuntu-latest 47 | needs: build 48 | steps: 49 | - name: Deploy to GitHub Pages 50 | id: deployment 51 | uses: actions/deploy-pages@v4 52 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | pull_request: 4 | push: 5 | jobs: 6 | run: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest] 11 | php-versions: ["8.1", "8.2", "8.3"] 12 | name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.os }} 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Setup PHP 18 | uses: shivammathur/setup-php@v2 19 | with: 20 | php-version: ${{ matrix.php-versions }} 21 | extensions: mbstring, intl, sockets 22 | coverage: xdebug 23 | 24 | - name: Check environment 25 | run: | 26 | php --version 27 | composer --version 28 | 29 | - name: Get composer cache directory 30 | id: composercache 31 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 32 | 33 | - name: Cache dependencies 34 | uses: actions/cache@v2 35 | with: 36 | path: ${{ steps.composercache.outputs.dir }} 37 | key: ${{ matrix.os }}-composer-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }} 38 | restore-keys: ${{ matrix.os }}-composer-${{ matrix.php-versions }}- 39 | 40 | - name: Run composer 41 | run: | 42 | composer update 43 | 44 | - name: Run codestyle check 45 | env: 46 | PHP_CS_FIXER_IGNORE_ENV: 1 47 | run: | 48 | vendor/bin/php-cs-fixer --diff --dry-run -v fix 49 | 50 | - name: Run Psalm analysis 51 | run: | 52 | vendor/bin/psalm.phar --shepherd 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | composer.lock 4 | phpunit.xml 5 | vendor 6 | .php_cs.cache 7 | coverage 8 | .phpunit* 9 | *.cache 10 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | true, 8 | 'phpdoc_to_param_type' => true, 9 | 'phpdoc_to_return_type' => true, 10 | 'phpdoc_to_property_type' => true, 11 | ]); 12 | } 13 | }; 14 | 15 | $config->getFinder() 16 | ->in(__DIR__ . '/lib'); 17 | 18 | $cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__; 19 | 20 | $config->setCacheFile($cacheDir . '/.php_cs.cache'); 21 | 22 | return $config; 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | better-prometheus - A better Prometheus library for PHP applications. 2 | 3 | Copyright 2024 Daniil Gentili 4 | 5 | Homepage: https://github.com/danog/better-prometheus 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # better-prometheus 2 | 3 | [![Psalm coverage](https://shepherd.dev/github/danog/better-prometheus/coverage.svg)](https://shepherd.dev/github/danog/better-prometheus) 4 | [![Psalm level 1](https://shepherd.dev/github/danog/better-prometheus/level.svg)](https://shepherd.dev/github/danog/better-prometheus) 5 | ![License](https://img.shields.io/github/license/danog/better-prometheus?v) 6 | 7 | A better Prometheus library for PHP applications. 8 | 9 | Offers a modern, clean PHP 8.1 API, with support for **default label values**, based on and compatible with the original `promphp/prometheus_client_php` library. 10 | 11 | ## Installation 12 | 13 | ```bash 14 | composer require danog/better-prometheus 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```php 20 | values, not just keys. 35 | $counter = $registry->getOrRegisterCounter( 36 | 'test', 37 | 'some_counter', 38 | 'it increases', 39 | // Note: these are default label key+values, they will be sent verbatim, no changes 40 | ['someLabel' => 'defaultValue'] 41 | ); 42 | 43 | // Specify some additional labels post-construction like this (both keys and values)... 44 | $counter->incBy(3, ['type' => 'blue']); 45 | 46 | // ...or add some more default labels to the counter, creating a new counter: 47 | $newCounter = $counter->addLabels(['someOtherLabel' => 'someOtherDefaultValue']); 48 | assert($newCounter !== $counter); // true 49 | $newCounter->incBy(3, ['type' => 'blue']); 50 | 51 | 52 | // Gauges can also be used 53 | $gauge = $registry->getOrRegisterGauge( 54 | 'test', 55 | 'some_gauge', 56 | 'it sets', 57 | ['someLabel' => 'defaultValue'] 58 | ); 59 | $gauge->set(2.5, ['type' => 'blue']); 60 | 61 | 62 | 63 | // As well as histograms 64 | $histogram = $registry->getOrRegisterHistogram( 65 | 'test', 66 | 'some_histogram', 67 | 'it observes', 68 | ['someLabel' => 'defaultValue'], 69 | // [0.1, 1, 2, 3.5, 4, 5, 6, 7, 8, 9] 70 | ); 71 | $histogram->observe(3.5, ['type' => 'blue']); 72 | 73 | 74 | // And suummaries 75 | $summary = $registry->getOrRegisterSummary( 76 | 'test', 77 | 'some_summary', 78 | 'it observes a sliding window', 79 | ['someLabel' => 'defaultValue'], 80 | // 84600, 81 | // [0.01, 0.05, 0.5, 0.95, 0.99] 82 | ); 83 | 84 | $summary->observe(5, ['type' => 'blue']); 85 | ``` 86 | 87 | ## API documentation 88 | 89 | See [here »](https://daniil.it/better-prometheus/docs/) for the full API documentation. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "danog/better-prometheus", 3 | "description": "A better Prometheus library for PHP applications", 4 | "type": "library", 5 | "require": { 6 | "php": ">=8.1", 7 | "promphp/prometheus_client_php": "^2.10" 8 | }, 9 | "license": "Apache-2.0", 10 | "autoload": { 11 | "psr-4": { 12 | "danog\\BetterPrometheus\\": "lib/" 13 | } 14 | }, 15 | "require-dev": { 16 | "amphp/php-cs-fixer-config": "^2", 17 | "psalm/phar": "dev-master", 18 | "danog/phpdoc": "^0.1.24" 19 | }, 20 | "authors": [ 21 | { 22 | "name": "Daniil Gentili", 23 | "email": "daniil@daniil.it" 24 | } 25 | ], 26 | "scripts": { 27 | "cs": "php-cs-fixer fix -v --diff --dry-run", 28 | "cs-fix": "php-cs-fixer fix -v --diff" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /docs/danog/BetterPrometheus/BetterCollector.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "danog\\BetterPrometheus\\BetterCollector: A better prometheus collector." 3 | description: "" 4 | 5 | --- 6 | # `danog\BetterPrometheus\BetterCollector` 7 | [Back to index](../../index.md) 8 | 9 | > Author: Daniil Gentili 10 | 11 | 12 | A better prometheus collector. 13 | 14 | 15 | 16 | ## Properties 17 | * `$storageAdapter`: `Prometheus\Storage\Adapter` Storage adapter 18 | * `$namespace`: `string` Metric namespace 19 | * `$name`: `string` Metric name 20 | * `$help`: `string` Info about the metric 21 | * `$labels`: `array` Default labels, i.e. ['instance' => 'instance_1'] 22 | 23 | ## Method list: 24 | * [`__construct(\Prometheus\Storage\Adapter $storageAdapter, string $namespace, string $name, string $help, array $labels = [])`](#__construct) 25 | * [`addLabels(array $labels): static`](#addLabels) 26 | 27 | ## Methods: 28 | ### `__construct(\Prometheus\Storage\Adapter $storageAdapter, string $namespace, string $name, string $help, array $labels = [])` 29 | 30 | Constructor. 31 | 32 | 33 | Parameters: 34 | 35 | * `$storageAdapter`: `\Prometheus\Storage\Adapter` 36 | * `$namespace`: `string` 37 | * `$name`: `string` 38 | * `$help`: `string` 39 | * `$labels`: `array` 40 | 41 | 42 | #### See also: 43 | * `\Prometheus\Storage\Adapter` 44 | 45 | 46 | 47 | 48 | ### `addLabels(array $labels): static` 49 | 50 | Create a new instance of this collector, with these additional labels. 51 | 52 | 53 | Parameters: 54 | 55 | * `$labels`: `array` 56 | 57 | 58 | 59 | --- 60 | Generated by [danog/phpdoc](https://phpdoc.daniil.it) 61 | -------------------------------------------------------------------------------- /docs/danog/BetterPrometheus/BetterCollectorRegistry.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "danog\\BetterPrometheus\\BetterCollectorRegistry: A better collector registry." 3 | description: "" 4 | 5 | --- 6 | # `danog\BetterPrometheus\BetterCollectorRegistry` 7 | [Back to index](../../index.md) 8 | 9 | > Author: Daniil Gentili 10 | 11 | 12 | A better collector registry. 13 | 14 | 15 | 16 | ## Properties 17 | * `$storageAdapter`: `Prometheus\Storage\Adapter` 18 | 19 | ## Method list: 20 | * [`__construct(\Prometheus\Storage\Adapter $storageAdapter, bool $registerDefaultMetrics = true)`](#__construct) 21 | * [`wipeStorage(): void`](#wipeStorage) 22 | * [`getMetricFamilySamples(bool $sortMetrics = true): list<\Prometheus\MetricFamilySamples>`](#getMetricFamilySamples) 23 | * [`registerGauge(string $namespace, string $name, string $help, array $labels = []): \danog\BetterPrometheus\BetterGauge`](#registerGauge) 24 | * [`getGauge(string $namespace, string $name): \danog\BetterPrometheus\BetterGauge`](#getGauge) 25 | * [`getOrRegisterGauge(string $namespace, string $name, string $help, array $labels = []): \danog\BetterPrometheus\BetterGauge`](#getOrRegisterGauge) 26 | * [`registerCounter(string $namespace, string $name, string $help, array $labels = []): \danog\BetterPrometheus\BetterCounter`](#registerCounter) 27 | * [`getCounter(string $namespace, string $name): \danog\BetterPrometheus\BetterCounter`](#getCounter) 28 | * [`getOrRegisterCounter(string $namespace, string $name, string $help, array $labels = []): \danog\BetterPrometheus\BetterCounter`](#getOrRegisterCounter) 29 | * [`registerHistogram(string $namespace, string $name, string $help, array $labels = [], (non-empty-list|null) $buckets = NULL): \danog\BetterPrometheus\BetterHistogram`](#registerHistogram) 30 | * [`getHistogram(string $namespace, string $name): \danog\BetterPrometheus\BetterHistogram`](#getHistogram) 31 | * [`getOrRegisterHistogram(string $namespace, string $name, string $help, array $labels = [], (non-empty-list|null) $buckets = NULL): \danog\BetterPrometheus\BetterHistogram`](#getOrRegisterHistogram) 32 | * [`registerSummary(string $namespace, string $name, string $help, array $labels = [], int $maxAgeSeconds = 600, (non-empty-list|null) $quantiles = NULL): \danog\BetterPrometheus\BetterSummary`](#registerSummary) 33 | * [`getSummary(string $namespace, string $name): \danog\BetterPrometheus\BetterSummary`](#getSummary) 34 | * [`getOrRegisterSummary(string $namespace, string $name, string $help, array $labels = [], int $maxAgeSeconds = 600, (non-empty-list|null) $quantiles = NULL): \danog\BetterPrometheus\BetterSummary`](#getOrRegisterSummary) 35 | 36 | ## Methods: 37 | ### `__construct(\Prometheus\Storage\Adapter $storageAdapter, bool $registerDefaultMetrics = true)` 38 | 39 | CollectorRegistry constructor. 40 | 41 | 42 | Parameters: 43 | 44 | * `$storageAdapter`: `\Prometheus\Storage\Adapter` 45 | * `$registerDefaultMetrics`: `bool` 46 | 47 | 48 | #### See also: 49 | * `\Prometheus\Storage\Adapter` 50 | 51 | 52 | 53 | 54 | ### `wipeStorage(): void` 55 | 56 | Removes all previously stored metrics from underlying storage adapter. 57 | 58 | 59 | 60 | ### `getMetricFamilySamples(bool $sortMetrics = true): list<\Prometheus\MetricFamilySamples>` 61 | 62 | 63 | 64 | 65 | Parameters: 66 | 67 | * `$sortMetrics`: `bool` 68 | 69 | 70 | #### See also: 71 | * `\Prometheus\MetricFamilySamples` 72 | 73 | 74 | 75 | 76 | ### `registerGauge(string $namespace, string $name, string $help, array $labels = []): \danog\BetterPrometheus\BetterGauge` 77 | 78 | 79 | 80 | 81 | Parameters: 82 | 83 | * `$namespace`: `string` e.g. cms 84 | * `$name`: `string` e.g. duration_seconds 85 | * `$help`: `string` e.g. The duration something took in seconds. 86 | * `$labels`: `array` e.g. ['controller' => 'someController', 'action' => 'someAction'] 87 | 88 | 89 | #### See also: 90 | * [`\danog\BetterPrometheus\BetterGauge`: A better prometheus gauge.](../../danog/BetterPrometheus/BetterGauge.md) 91 | 92 | 93 | 94 | 95 | ### `getGauge(string $namespace, string $name): \danog\BetterPrometheus\BetterGauge` 96 | 97 | 98 | 99 | 100 | Parameters: 101 | 102 | * `$namespace`: `string` 103 | * `$name`: `string` 104 | 105 | 106 | #### See also: 107 | * [`\danog\BetterPrometheus\BetterGauge`: A better prometheus gauge.](../../danog/BetterPrometheus/BetterGauge.md) 108 | 109 | 110 | 111 | 112 | ### `getOrRegisterGauge(string $namespace, string $name, string $help, array $labels = []): \danog\BetterPrometheus\BetterGauge` 113 | 114 | 115 | 116 | 117 | Parameters: 118 | 119 | * `$namespace`: `string` e.g. cms 120 | * `$name`: `string` e.g. duration_seconds 121 | * `$help`: `string` e.g. The duration something took in seconds. 122 | * `$labels`: `array` e.g. ['controller' => 'someController', 'action' => 'someAction'] 123 | 124 | 125 | #### See also: 126 | * [`\danog\BetterPrometheus\BetterGauge`: A better prometheus gauge.](../../danog/BetterPrometheus/BetterGauge.md) 127 | 128 | 129 | 130 | 131 | ### `registerCounter(string $namespace, string $name, string $help, array $labels = []): \danog\BetterPrometheus\BetterCounter` 132 | 133 | 134 | 135 | 136 | Parameters: 137 | 138 | * `$namespace`: `string` e.g. cms 139 | * `$name`: `string` e.g. requests 140 | * `$help`: `string` e.g. The number of requests made. 141 | * `$labels`: `array` e.g. ['controller' => 'someController', 'action' => 'someAction'] 142 | 143 | 144 | #### See also: 145 | * [`\danog\BetterPrometheus\BetterCounter`: A better prometheus counter.](../../danog/BetterPrometheus/BetterCounter.md) 146 | 147 | 148 | 149 | 150 | ### `getCounter(string $namespace, string $name): \danog\BetterPrometheus\BetterCounter` 151 | 152 | 153 | 154 | 155 | Parameters: 156 | 157 | * `$namespace`: `string` 158 | * `$name`: `string` 159 | 160 | 161 | #### See also: 162 | * [`\danog\BetterPrometheus\BetterCounter`: A better prometheus counter.](../../danog/BetterPrometheus/BetterCounter.md) 163 | 164 | 165 | 166 | 167 | ### `getOrRegisterCounter(string $namespace, string $name, string $help, array $labels = []): \danog\BetterPrometheus\BetterCounter` 168 | 169 | 170 | 171 | 172 | Parameters: 173 | 174 | * `$namespace`: `string` e.g. cms 175 | * `$name`: `string` e.g. requests 176 | * `$help`: `string` e.g. The number of requests made. 177 | * `$labels`: `array` e.g. ['controller' => 'someController', 'action' => 'someAction'] 178 | 179 | 180 | #### See also: 181 | * [`\danog\BetterPrometheus\BetterCounter`: A better prometheus counter.](../../danog/BetterPrometheus/BetterCounter.md) 182 | 183 | 184 | 185 | 186 | ### `registerHistogram(string $namespace, string $name, string $help, array $labels = [], (non-empty-list|null) $buckets = NULL): \danog\BetterPrometheus\BetterHistogram` 187 | 188 | 189 | 190 | 191 | Parameters: 192 | 193 | * `$namespace`: `string` e.g. cms 194 | * `$name`: `string` e.g. duration_seconds 195 | * `$help`: `string` e.g. A histogram of the duration in seconds. 196 | * `$labels`: `array` e.g. ['controller' => 'someController', 'action' => 'someAction'] 197 | * `$buckets`: `(non-empty-list|null)` e.g. [100, 200, 300] 198 | 199 | 200 | #### See also: 201 | * `non-empty-list` 202 | * [`\danog\BetterPrometheus\BetterHistogram`: A better prometheus histogram.](../../danog/BetterPrometheus/BetterHistogram.md) 203 | 204 | 205 | 206 | 207 | ### `getHistogram(string $namespace, string $name): \danog\BetterPrometheus\BetterHistogram` 208 | 209 | 210 | 211 | 212 | Parameters: 213 | 214 | * `$namespace`: `string` 215 | * `$name`: `string` 216 | 217 | 218 | #### See also: 219 | * [`\danog\BetterPrometheus\BetterHistogram`: A better prometheus histogram.](../../danog/BetterPrometheus/BetterHistogram.md) 220 | 221 | 222 | 223 | 224 | ### `getOrRegisterHistogram(string $namespace, string $name, string $help, array $labels = [], (non-empty-list|null) $buckets = NULL): \danog\BetterPrometheus\BetterHistogram` 225 | 226 | 227 | 228 | 229 | Parameters: 230 | 231 | * `$namespace`: `string` e.g. cms 232 | * `$name`: `string` e.g. duration_seconds 233 | * `$help`: `string` e.g. A histogram of the duration in seconds. 234 | * `$labels`: `array` e.g. ['controller' => 'someController', 'action' => 'someAction'] 235 | * `$buckets`: `(non-empty-list|null)` e.g. [100, 200, 300] 236 | 237 | 238 | #### See also: 239 | * `non-empty-list` 240 | * [`\danog\BetterPrometheus\BetterHistogram`: A better prometheus histogram.](../../danog/BetterPrometheus/BetterHistogram.md) 241 | 242 | 243 | 244 | 245 | ### `registerSummary(string $namespace, string $name, string $help, array $labels = [], int $maxAgeSeconds = 600, (non-empty-list|null) $quantiles = NULL): \danog\BetterPrometheus\BetterSummary` 246 | 247 | 248 | 249 | 250 | Parameters: 251 | 252 | * `$namespace`: `string` e.g. cms 253 | * `$name`: `string` e.g. duration_seconds 254 | * `$help`: `string` e.g. A summary of the duration in seconds. 255 | * `$labels`: `array` e.g. ['controller' => 'someController', 'action' => 'someAction'] 256 | * `$maxAgeSeconds`: `int` e.g. 604800 257 | * `$quantiles`: `(non-empty-list|null)` e.g. [0.01, 0.5, 0.99] 258 | 259 | 260 | #### See also: 261 | * `non-empty-list` 262 | * [`\danog\BetterPrometheus\BetterSummary`: A better prometheus summary.](../../danog/BetterPrometheus/BetterSummary.md) 263 | 264 | 265 | 266 | 267 | ### `getSummary(string $namespace, string $name): \danog\BetterPrometheus\BetterSummary` 268 | 269 | 270 | 271 | 272 | Parameters: 273 | 274 | * `$namespace`: `string` 275 | * `$name`: `string` 276 | 277 | 278 | #### See also: 279 | * [`\danog\BetterPrometheus\BetterSummary`: A better prometheus summary.](../../danog/BetterPrometheus/BetterSummary.md) 280 | 281 | 282 | 283 | 284 | ### `getOrRegisterSummary(string $namespace, string $name, string $help, array $labels = [], int $maxAgeSeconds = 600, (non-empty-list|null) $quantiles = NULL): \danog\BetterPrometheus\BetterSummary` 285 | 286 | 287 | 288 | 289 | Parameters: 290 | 291 | * `$namespace`: `string` e.g. cms 292 | * `$name`: `string` e.g. duration_seconds 293 | * `$help`: `string` e.g. A summary of the duration in seconds. 294 | * `$labels`: `array` e.g. ['controller' => 'someController', 'action' => 'someAction'] 295 | * `$maxAgeSeconds`: `int` e.g. 604800 296 | * `$quantiles`: `(non-empty-list|null)` e.g. [0.01, 0.5, 0.99] 297 | 298 | 299 | #### See also: 300 | * `non-empty-list` 301 | * [`\danog\BetterPrometheus\BetterSummary`: A better prometheus summary.](../../danog/BetterPrometheus/BetterSummary.md) 302 | 303 | 304 | 305 | 306 | --- 307 | Generated by [danog/phpdoc](https://phpdoc.daniil.it) 308 | -------------------------------------------------------------------------------- /docs/danog/BetterPrometheus/BetterCounter.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "danog\\BetterPrometheus\\BetterCounter: A better prometheus counter." 3 | description: "" 4 | 5 | --- 6 | # `danog\BetterPrometheus\BetterCounter` 7 | [Back to index](../../index.md) 8 | 9 | > Author: Daniil Gentili 10 | 11 | 12 | A better prometheus counter. 13 | 14 | 15 | 16 | ## Properties 17 | * `$storageAdapter`: `Prometheus\Storage\Adapter` Storage adapter 18 | * `$namespace`: `string` Metric namespace 19 | * `$name`: `string` Metric name 20 | * `$help`: `string` Info about the metric 21 | * `$labels`: `array` Default labels, i.e. ['instance' => 'instance_1'] 22 | 23 | ## Method list: 24 | * [`addLabels(array $labels): static`](#addLabels) 25 | * [`inc(array $labels = []): void`](#inc) 26 | * [`incBy((int|float) $count, array $labels = []): void`](#incBy) 27 | * [`__construct(\Prometheus\Storage\Adapter $storageAdapter, string $namespace, string $name, string $help, array $labels = [])`](#__construct) 28 | 29 | ## Methods: 30 | ### `addLabels(array $labels): static` 31 | 32 | 33 | 34 | 35 | Parameters: 36 | 37 | * `$labels`: `array` 38 | 39 | 40 | 41 | ### `inc(array $labels = []): void` 42 | 43 | 44 | 45 | 46 | Parameters: 47 | 48 | * `$labels`: `array` e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 49 | 50 | 51 | 52 | ### `incBy((int|float) $count, array $labels = []): void` 53 | 54 | 55 | 56 | 57 | Parameters: 58 | 59 | * `$count`: `(int|float)` e.g. 2 60 | * `$labels`: `array` e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 61 | 62 | 63 | 64 | ### `__construct(\Prometheus\Storage\Adapter $storageAdapter, string $namespace, string $name, string $help, array $labels = [])` 65 | 66 | Constructor. 67 | 68 | 69 | Parameters: 70 | 71 | * `$storageAdapter`: `\Prometheus\Storage\Adapter` 72 | * `$namespace`: `string` 73 | * `$name`: `string` 74 | * `$help`: `string` 75 | * `$labels`: `array` 76 | 77 | 78 | #### See also: 79 | * `\Prometheus\Storage\Adapter` 80 | 81 | 82 | 83 | 84 | --- 85 | Generated by [danog/phpdoc](https://phpdoc.daniil.it) 86 | -------------------------------------------------------------------------------- /docs/danog/BetterPrometheus/BetterGauge.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "danog\\BetterPrometheus\\BetterGauge: A better prometheus gauge." 3 | description: "" 4 | 5 | --- 6 | # `danog\BetterPrometheus\BetterGauge` 7 | [Back to index](../../index.md) 8 | 9 | > Author: Daniil Gentili 10 | 11 | 12 | A better prometheus gauge. 13 | 14 | 15 | 16 | ## Properties 17 | * `$storageAdapter`: `Prometheus\Storage\Adapter` Storage adapter 18 | * `$namespace`: `string` Metric namespace 19 | * `$name`: `string` Metric name 20 | * `$help`: `string` Info about the metric 21 | * `$labels`: `array` Default labels, i.e. ['instance' => 'instance_1'] 22 | 23 | ## Method list: 24 | * [`addLabels(array $labels): static`](#addLabels) 25 | * [`set((int|double) $value, array $labels = []): void`](#set) 26 | * [`incBy(int|float $value, array $labels = []): void`](#incBy) 27 | * [`inc(array $labels = []): void`](#inc) 28 | * [`dec(array $labels = []): void`](#dec) 29 | * [`decBy(int|float $value, array $labels = []): void`](#decBy) 30 | * [`__construct(\Prometheus\Storage\Adapter $storageAdapter, string $namespace, string $name, string $help, array $labels = [])`](#__construct) 31 | 32 | ## Methods: 33 | ### `addLabels(array $labels): static` 34 | 35 | 36 | 37 | 38 | Parameters: 39 | 40 | * `$labels`: `array` 41 | 42 | 43 | 44 | ### `set((int|double) $value, array $labels = []): void` 45 | 46 | 47 | 48 | 49 | Parameters: 50 | 51 | * `$value`: `(int|double)` e.g. 123 52 | * `$labels`: `array` e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 53 | 54 | 55 | #### See also: 56 | * `double` 57 | 58 | 59 | 60 | 61 | ### `incBy(int|float $value, array $labels = []): void` 62 | 63 | 64 | 65 | 66 | Parameters: 67 | 68 | * `$value`: `int|float` 69 | * `$labels`: `array` e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 70 | 71 | 72 | 73 | ### `inc(array $labels = []): void` 74 | 75 | 76 | 77 | 78 | Parameters: 79 | 80 | * `$labels`: `array` e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 81 | 82 | 83 | 84 | ### `dec(array $labels = []): void` 85 | 86 | 87 | 88 | 89 | Parameters: 90 | 91 | * `$labels`: `array` e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 92 | 93 | 94 | 95 | ### `decBy(int|float $value, array $labels = []): void` 96 | 97 | 98 | 99 | 100 | Parameters: 101 | 102 | * `$value`: `int|float` 103 | * `$labels`: `array` e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 104 | 105 | 106 | 107 | ### `__construct(\Prometheus\Storage\Adapter $storageAdapter, string $namespace, string $name, string $help, array $labels = [])` 108 | 109 | Constructor. 110 | 111 | 112 | Parameters: 113 | 114 | * `$storageAdapter`: `\Prometheus\Storage\Adapter` 115 | * `$namespace`: `string` 116 | * `$name`: `string` 117 | * `$help`: `string` 118 | * `$labels`: `array` 119 | 120 | 121 | #### See also: 122 | * `\Prometheus\Storage\Adapter` 123 | 124 | 125 | 126 | 127 | --- 128 | Generated by [danog/phpdoc](https://phpdoc.daniil.it) 129 | -------------------------------------------------------------------------------- /docs/danog/BetterPrometheus/BetterHistogram.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "danog\\BetterPrometheus\\BetterHistogram: A better prometheus histogram." 3 | description: "" 4 | 5 | --- 6 | # `danog\BetterPrometheus\BetterHistogram` 7 | [Back to index](../../index.md) 8 | 9 | > Author: Daniil Gentili 10 | 11 | 12 | A better prometheus histogram. 13 | 14 | 15 | 16 | 17 | ## Constants 18 | * `danog\BetterPrometheus\BetterHistogram::TYPE`: 19 | 20 | ## Properties 21 | * `$storageAdapter`: `Prometheus\Storage\Adapter` Storage adapter 22 | * `$namespace`: `string` Metric namespace 23 | * `$name`: `string` Metric name 24 | * `$help`: `string` Info about the metric 25 | * `$labels`: `array` Default labels, i.e. ['instance' => 'instance_1'] 26 | 27 | ## Method list: 28 | * [`__construct(\Prometheus\Storage\Adapter $adapter, string $namespace, string $name, string $help, array $labels = [], (non-empty-list|null) $buckets = NULL)`](#__construct) 29 | * [`addLabels(array $labels): static`](#addLabels) 30 | * [`observe((double|int) $value, array $labels = []): void`](#observe) 31 | 32 | ## Methods: 33 | ### `__construct(\Prometheus\Storage\Adapter $adapter, string $namespace, string $name, string $help, array $labels = [], (non-empty-list|null) $buckets = NULL)` 34 | 35 | 36 | 37 | 38 | Parameters: 39 | 40 | * `$adapter`: `\Prometheus\Storage\Adapter` 41 | * `$namespace`: `string` 42 | * `$name`: `string` 43 | * `$help`: `string` 44 | * `$labels`: `array` 45 | * `$buckets`: `(non-empty-list|null)` 46 | 47 | 48 | #### See also: 49 | * `\Prometheus\Storage\Adapter` 50 | * `non-empty-list` 51 | 52 | 53 | 54 | 55 | ### `addLabels(array $labels): static` 56 | 57 | 58 | 59 | 60 | Parameters: 61 | 62 | * `$labels`: `array` 63 | 64 | 65 | 66 | ### `observe((double|int) $value, array $labels = []): void` 67 | 68 | 69 | 70 | 71 | Parameters: 72 | 73 | * `$value`: `(double|int)` e.g. 123 74 | * `$labels`: `array` e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 75 | 76 | 77 | #### See also: 78 | * `double` 79 | 80 | 81 | 82 | 83 | --- 84 | Generated by [danog/phpdoc](https://phpdoc.daniil.it) 85 | -------------------------------------------------------------------------------- /docs/danog/BetterPrometheus/BetterSummary.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "danog\\BetterPrometheus\\BetterSummary: A better prometheus summary." 3 | description: "" 4 | 5 | --- 6 | # `danog\BetterPrometheus\BetterSummary` 7 | [Back to index](../../index.md) 8 | 9 | > Author: Daniil Gentili 10 | 11 | 12 | A better prometheus summary. 13 | 14 | 15 | 16 | 17 | ## Constants 18 | * `danog\BetterPrometheus\BetterSummary::RESERVED_LABELS`: 19 | 20 | * `danog\BetterPrometheus\BetterSummary::TYPE`: 21 | 22 | ## Properties 23 | * `$storageAdapter`: `Prometheus\Storage\Adapter` Storage adapter 24 | * `$namespace`: `string` Metric namespace 25 | * `$name`: `string` Metric name 26 | * `$help`: `string` Info about the metric 27 | * `$labels`: `array` Default labels, i.e. ['instance' => 'instance_1'] 28 | 29 | ## Method list: 30 | * [`__construct(\Prometheus\Storage\Adapter $adapter, string $namespace, string $name, string $help, array $labels = [], int $maxAgeSeconds = 600, ?non-empty-list $quantiles = NULL)`](#__construct) 31 | * [`addLabels(array $labels): static`](#addLabels) 32 | * [`observe((double|int) $value, array $labels = []): void`](#observe) 33 | 34 | ## Methods: 35 | ### `__construct(\Prometheus\Storage\Adapter $adapter, string $namespace, string $name, string $help, array $labels = [], int $maxAgeSeconds = 600, ?non-empty-list $quantiles = NULL)` 36 | 37 | 38 | 39 | 40 | Parameters: 41 | 42 | * `$adapter`: `\Prometheus\Storage\Adapter` 43 | * `$namespace`: `string` 44 | * `$name`: `string` 45 | * `$help`: `string` 46 | * `$labels`: `array` 47 | * `$maxAgeSeconds`: `int` 48 | * `$quantiles`: `?non-empty-list` 49 | 50 | 51 | #### See also: 52 | * `\Prometheus\Storage\Adapter` 53 | * `non-empty-list` 54 | 55 | 56 | 57 | 58 | ### `addLabels(array $labels): static` 59 | 60 | 61 | 62 | 63 | Parameters: 64 | 65 | * `$labels`: `array` 66 | 67 | 68 | 69 | ### `observe((double|int) $value, array $labels = []): void` 70 | 71 | 72 | 73 | 74 | Parameters: 75 | 76 | * `$value`: `(double|int)` e.g. 123 77 | * `$labels`: `array` e.g. ['status' => '404', 'opcode' => 'SOME_OP'] 78 | 79 | 80 | #### See also: 81 | * `double` 82 | 83 | 84 | 85 | 86 | --- 87 | Generated by [danog/phpdoc](https://phpdoc.daniil.it) 88 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "A better Prometheus library for PHP applications" 3 | title: "danog/better-prometheus" 4 | 5 | --- 6 | # `danog/better-prometheus` 7 | 8 | A better Prometheus library for PHP applications 9 | 10 | 11 | 12 | ## Abstract classes 13 | * [\danog\BetterPrometheus\BetterCollector: A better prometheus collector.](danog/BetterPrometheus/BetterCollector.md) 14 | 15 | ## Classes 16 | * [\danog\BetterPrometheus\BetterCollectorRegistry: A better collector registry.](danog/BetterPrometheus/BetterCollectorRegistry.md) 17 | * [\danog\BetterPrometheus\BetterCounter: A better prometheus counter.](danog/BetterPrometheus/BetterCounter.md) 18 | * [\danog\BetterPrometheus\BetterGauge: A better prometheus gauge.](danog/BetterPrometheus/BetterGauge.md) 19 | * [\danog\BetterPrometheus\BetterHistogram: A better prometheus histogram.](danog/BetterPrometheus/BetterHistogram.md) 20 | * [\danog\BetterPrometheus\BetterSummary: A better prometheus summary.](danog/BetterPrometheus/BetterSummary.md) 21 | 22 | 23 | 24 | --- 25 | Generated by [danog/phpdoc](https://phpdoc.daniil.it). -------------------------------------------------------------------------------- /examples/1-all.php: -------------------------------------------------------------------------------- 1 | values, not just keys. 16 | $counter = $registry->getOrRegisterCounter( 17 | 'test', 18 | 'some_counter', 19 | 'it increases', 20 | // Note: these are default label key+values, they will be sent verbatim, no changes 21 | ['someLabel' => 'defaultValue'] 22 | ); 23 | 24 | // Specify some additional labels post-construction like this (both keys and values)... 25 | $counter->incBy(3, ['type' => 'blue']); 26 | 27 | // ...or add some more default labels to the counter, creating a new counter: 28 | $newCounter = $counter->addLabels(['someOtherLabel' => 'someOtherDefaultValue']); 29 | assert($newCounter !== $counter); // true 30 | $newCounter->incBy(3, ['type' => 'blue']); 31 | 32 | 33 | 34 | // Gauges can also be used 35 | $gauge = $registry->getOrRegisterGauge( 36 | 'test', 37 | 'some_gauge', 38 | 'it sets', 39 | ['someLabel' => 'defaultValue'] 40 | ); 41 | $gauge->set(2.5, ['type' => 'blue']); 42 | 43 | 44 | 45 | // As well as histograms 46 | $histogram = $registry->getOrRegisterHistogram( 47 | 'test', 48 | 'some_histogram', 49 | 'it observes', 50 | ['someLabel' => 'defaultValue'], 51 | // [0.1, 1, 2, 3.5, 4, 5, 6, 7, 8, 9] 52 | ); 53 | $histogram->observe(3.5, ['type' => 'blue']); 54 | 55 | 56 | // And suummaries 57 | $summary = $registry->getOrRegisterSummary( 58 | 'test', 59 | 'some_summary', 60 | 'it observes a sliding window', 61 | ['someLabel' => 'defaultValue'], 62 | // 84600, 63 | // [0.01, 0.05, 0.5, 0.95, 0.99] 64 | ); 65 | 66 | $summary->observe(5, ['type' => 'blue']); 67 | -------------------------------------------------------------------------------- /lib/BetterCollector.php: -------------------------------------------------------------------------------- 1 | $labels Default labels, i.e. ['instance' => 'instance_1'] */ 32 | public readonly array $labels = [] 33 | ) { 34 | self::assertValidLabels($labels); 35 | $metricName = ($namespace !== '' ? $namespace . '_' : '') . $name; 36 | Collector::assertValidMetricName($metricName); 37 | $this->metricName = $metricName; 38 | } 39 | 40 | /** @param array $labels */ 41 | protected static function assertValidLabels(array $labels): void 42 | { 43 | foreach ($labels as $labelKey => $_) { 44 | Collector::assertValidLabel($labelKey); 45 | } 46 | } 47 | 48 | /** 49 | * Create a new instance of this collector, with these additional labels. 50 | * 51 | * @param array $labels 52 | */ 53 | abstract public function addLabels(array $labels): static; 54 | } 55 | -------------------------------------------------------------------------------- /lib/BetterCollectorRegistry.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | private array $gauges = []; 23 | 24 | /** 25 | * @var array 26 | */ 27 | private array $counters = []; 28 | 29 | /** 30 | * @var array 31 | */ 32 | private array $histograms = []; 33 | 34 | /** 35 | * @var array 36 | */ 37 | private array $summaries = []; 38 | 39 | /** 40 | * @psalm-suppress UnusedProperty 41 | */ 42 | private ?BetterGauge $defaultGauge = null; 43 | 44 | /** 45 | * CollectorRegistry constructor. 46 | * 47 | */ 48 | public function __construct( 49 | public readonly Adapter $storageAdapter, 50 | bool $registerDefaultMetrics = true 51 | ) { 52 | if ($registerDefaultMetrics) { 53 | $this->defaultGauge = $this->getOrRegisterGauge( 54 | "", 55 | "php_info", 56 | "Information about the PHP environment.", 57 | ["version" => PHP_VERSION] 58 | ); 59 | $this->defaultGauge->set(1); 60 | } 61 | } 62 | 63 | /** 64 | * Removes all previously stored metrics from underlying storage adapter. 65 | */ 66 | public function wipeStorage(): void 67 | { 68 | $this->storageAdapter->wipeStorage(); 69 | } 70 | 71 | /** 72 | * @psalm-suppress TooManyArguments 73 | * 74 | * @return list 75 | */ 76 | public function getMetricFamilySamples(bool $sortMetrics = true): array 77 | { 78 | /** @var list */ 79 | return $this->storageAdapter->collect($sortMetrics); 80 | } 81 | 82 | /** 83 | * @param string $namespace e.g. cms 84 | * @param string $name e.g. duration_seconds 85 | * @param string $help e.g. The duration something took in seconds. 86 | * @param array $labels e.g. ['controller' => 'someController', 'action' => 'someAction'] 87 | * 88 | * @throws MetricsRegistrationException 89 | */ 90 | public function registerGauge(string $namespace, string $name, string $help, array $labels = []): BetterGauge 91 | { 92 | $metricIdentifier = "$namespace:$name"; 93 | if (isset($this->gauges[$metricIdentifier])) { 94 | throw new MetricsRegistrationException("Metric already registered"); 95 | } 96 | $this->gauges[$metricIdentifier] = new BetterGauge( 97 | $this->storageAdapter, 98 | $namespace, 99 | $name, 100 | $help, 101 | $labels 102 | ); 103 | return $this->gauges[$metricIdentifier]; 104 | } 105 | 106 | /** 107 | * @throws MetricNotFoundException 108 | */ 109 | public function getGauge(string $namespace, string $name): BetterGauge 110 | { 111 | $metricIdentifier = "$namespace:$name"; 112 | if (!isset($this->gauges[$metricIdentifier])) { 113 | throw new MetricNotFoundException("Metric not found:" . $metricIdentifier); 114 | } 115 | return $this->gauges[$metricIdentifier]; 116 | } 117 | 118 | /** 119 | * @param string $namespace e.g. cms 120 | * @param string $name e.g. duration_seconds 121 | * @param string $help e.g. The duration something took in seconds. 122 | * @param array $labels e.g. ['controller' => 'someController', 'action' => 'someAction'] 123 | * 124 | * @throws MetricsRegistrationException 125 | */ 126 | public function getOrRegisterGauge(string $namespace, string $name, string $help, array $labels = []): BetterGauge 127 | { 128 | try { 129 | $gauge = $this->getGauge($namespace, $name); 130 | } catch (MetricNotFoundException $e) { 131 | $gauge = $this->registerGauge($namespace, $name, $help, $labels); 132 | } 133 | return $gauge; 134 | } 135 | 136 | /** 137 | * @param string $namespace e.g. cms 138 | * @param string $name e.g. requests 139 | * @param string $help e.g. The number of requests made. 140 | * @param array $labels e.g. ['controller' => 'someController', 'action' => 'someAction'] 141 | * 142 | * @throws MetricsRegistrationException 143 | */ 144 | public function registerCounter(string $namespace, string $name, string $help, array $labels = []): BetterCounter 145 | { 146 | $metricIdentifier = "$namespace:$name"; 147 | if (isset($this->counters[$metricIdentifier])) { 148 | throw new MetricsRegistrationException("Metric already registered"); 149 | } 150 | $this->counters[$metricIdentifier] = new BetterCounter( 151 | $this->storageAdapter, 152 | $namespace, 153 | $name, 154 | $help, 155 | $labels 156 | ); 157 | return $this->counters["$namespace:$name"]; 158 | } 159 | 160 | /** 161 | * 162 | * @throws MetricNotFoundException 163 | */ 164 | public function getCounter(string $namespace, string $name): BetterCounter 165 | { 166 | $metricIdentifier = "$namespace:$name"; 167 | if (!isset($this->counters[$metricIdentifier])) { 168 | throw new MetricNotFoundException("Metric not found:" . $metricIdentifier); 169 | } 170 | return $this->counters["$namespace:$name"]; 171 | } 172 | 173 | /** 174 | * @param string $namespace e.g. cms 175 | * @param string $name e.g. requests 176 | * @param string $help e.g. The number of requests made. 177 | * @param array $labels e.g. ['controller' => 'someController', 'action' => 'someAction'] 178 | * 179 | * @throws MetricsRegistrationException 180 | */ 181 | public function getOrRegisterCounter(string $namespace, string $name, string $help, array $labels = []): BetterCounter 182 | { 183 | try { 184 | $counter = $this->getCounter($namespace, $name); 185 | } catch (MetricNotFoundException $e) { 186 | $counter = $this->registerCounter($namespace, $name, $help, $labels); 187 | } 188 | return $counter; 189 | } 190 | 191 | /** 192 | * @param string $namespace e.g. cms 193 | * @param string $name e.g. duration_seconds 194 | * @param string $help e.g. A histogram of the duration in seconds. 195 | * @param array $labels e.g. ['controller' => 'someController', 'action' => 'someAction'] 196 | * @param non-empty-list|null $buckets e.g. [100, 200, 300] 197 | * 198 | * @throws MetricsRegistrationException 199 | */ 200 | public function registerHistogram( 201 | string $namespace, 202 | string $name, 203 | string $help, 204 | array $labels = [], 205 | ?array $buckets = null 206 | ): BetterHistogram { 207 | $metricIdentifier = "$namespace:$name"; 208 | if (isset($this->histograms[$metricIdentifier])) { 209 | throw new MetricsRegistrationException("Metric already registered"); 210 | } 211 | $this->histograms[$metricIdentifier] = new BetterHistogram( 212 | $this->storageAdapter, 213 | $namespace, 214 | $name, 215 | $help, 216 | $labels, 217 | $buckets 218 | ); 219 | return $this->histograms[$metricIdentifier]; 220 | } 221 | 222 | /** 223 | * 224 | * @throws MetricNotFoundException 225 | */ 226 | public function getHistogram(string $namespace, string $name): BetterHistogram 227 | { 228 | $metricIdentifier = "$namespace:$name"; 229 | if (!isset($this->histograms[$metricIdentifier])) { 230 | throw new MetricNotFoundException("Metric not found:" . $metricIdentifier); 231 | } 232 | return $this->histograms["$namespace:$name"]; 233 | } 234 | 235 | /** 236 | * @param string $namespace e.g. cms 237 | * @param string $name e.g. duration_seconds 238 | * @param string $help e.g. A histogram of the duration in seconds. 239 | * @param array $labels e.g. ['controller' => 'someController', 'action' => 'someAction'] 240 | * @param non-empty-list|null $buckets e.g. [100, 200, 300] 241 | * 242 | * @throws MetricsRegistrationException 243 | */ 244 | public function getOrRegisterHistogram( 245 | string $namespace, 246 | string $name, 247 | string $help, 248 | array $labels = [], 249 | ?array $buckets = null 250 | ): BetterHistogram { 251 | try { 252 | $histogram = $this->getHistogram($namespace, $name); 253 | } catch (MetricNotFoundException $e) { 254 | $histogram = $this->registerHistogram($namespace, $name, $help, $labels, $buckets); 255 | } 256 | return $histogram; 257 | } 258 | 259 | /** 260 | * @param string $namespace e.g. cms 261 | * @param string $name e.g. duration_seconds 262 | * @param string $help e.g. A summary of the duration in seconds. 263 | * @param array $labels e.g. ['controller' => 'someController', 'action' => 'someAction'] 264 | * @param int $maxAgeSeconds e.g. 604800 265 | * @param non-empty-list|null $quantiles e.g. [0.01, 0.5, 0.99] 266 | * 267 | * @throws MetricsRegistrationException 268 | */ 269 | public function registerSummary( 270 | string $namespace, 271 | string $name, 272 | string $help, 273 | array $labels = [], 274 | int $maxAgeSeconds = 600, 275 | ?array $quantiles = null 276 | ): BetterSummary { 277 | $metricIdentifier = "$namespace:$name"; 278 | if (isset($this->summaries[$metricIdentifier])) { 279 | throw new MetricsRegistrationException("Metric already registered"); 280 | } 281 | $this->summaries[$metricIdentifier] = new BetterSummary( 282 | $this->storageAdapter, 283 | $namespace, 284 | $name, 285 | $help, 286 | $labels, 287 | $maxAgeSeconds, 288 | $quantiles 289 | ); 290 | return $this->summaries[$metricIdentifier]; 291 | } 292 | 293 | /** 294 | * 295 | * @throws MetricNotFoundException 296 | */ 297 | public function getSummary(string $namespace, string $name): BetterSummary 298 | { 299 | $metricIdentifier = "$namespace:$name"; 300 | if (!isset($this->summaries[$metricIdentifier])) { 301 | throw new MetricNotFoundException("Metric not found:" . $metricIdentifier); 302 | } 303 | return $this->summaries["$namespace:$name"]; 304 | } 305 | 306 | /** 307 | * @param string $namespace e.g. cms 308 | * @param string $name e.g. duration_seconds 309 | * @param string $help e.g. A summary of the duration in seconds. 310 | * @param array $labels e.g. ['controller' => 'someController', 'action' => 'someAction'] 311 | * @param int $maxAgeSeconds e.g. 604800 312 | * @param non-empty-list|null $quantiles e.g. [0.01, 0.5, 0.99] 313 | * 314 | * @throws MetricsRegistrationException 315 | */ 316 | public function getOrRegisterSummary( 317 | string $namespace, 318 | string $name, 319 | string $help, 320 | array $labels = [], 321 | int $maxAgeSeconds = 600, 322 | ?array $quantiles = null 323 | ): BetterSummary { 324 | try { 325 | $summary = $this->getSummary($namespace, $name); 326 | } catch (MetricNotFoundException $e) { 327 | $summary = $this->registerSummary($namespace, $name, $help, $labels, $maxAgeSeconds, $quantiles); 328 | } 329 | return $summary; 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /lib/BetterCounter.php: -------------------------------------------------------------------------------- 1 | storageAdapter, 22 | $this->namespace, 23 | $this->name, 24 | $this->help, 25 | $this->labels + $labels 26 | ); 27 | } 28 | 29 | /** 30 | * @param array $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 31 | */ 32 | public function inc(array $labels = []): void 33 | { 34 | $this->incBy(1, $labels); 35 | } 36 | 37 | /** 38 | * @param int|float $count e.g. 2 39 | * @param array $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 40 | */ 41 | public function incBy(int|float $count, array $labels = []): void 42 | { 43 | self::assertValidLabels($labels); 44 | $labels = $this->labels + $labels; 45 | $this->storageAdapter->updateCounter( 46 | [ 47 | 'name' => $this->metricName, 48 | 'help' => $this->help, 49 | 'type' => self::TYPE, 50 | 'labelNames' => \array_keys($labels), 51 | 'labelValues' => \array_values($labels), 52 | 'value' => $count, 53 | 'command' => \is_float($count) ? Adapter::COMMAND_INCREMENT_FLOAT : Adapter::COMMAND_INCREMENT_INTEGER, 54 | ] 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/BetterGauge.php: -------------------------------------------------------------------------------- 1 | storageAdapter, 22 | $this->namespace, 23 | $this->name, 24 | $this->help, 25 | $this->labels + $labels 26 | ); 27 | } 28 | 29 | /** 30 | * @param int|double $value e.g. 123 31 | * @param array $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 32 | */ 33 | public function set(int|float $value, array $labels = []): void 34 | { 35 | self::assertValidLabels($labels); 36 | $labels = $this->labels + $labels; 37 | $this->storageAdapter->updateGauge( 38 | [ 39 | 'name' => $this->metricName, 40 | 'help' => $this->help, 41 | 'type' => self::TYPE, 42 | 'labelNames' => \array_keys($labels), 43 | 'labelValues' => \array_values($labels), 44 | 'value' => $value, 45 | 'command' => Adapter::COMMAND_SET, 46 | ] 47 | ); 48 | } 49 | /** 50 | * @param array $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 51 | */ 52 | public function incBy(int|float $value, array $labels = []): void 53 | { 54 | self::assertValidLabels($labels); 55 | $labels = $this->labels + $labels; 56 | $this->storageAdapter->updateGauge( 57 | [ 58 | 'name' => $this->metricName, 59 | 'help' => $this->help, 60 | 'type' => self::TYPE, 61 | 'labelNames' => \array_keys($labels), 62 | 'labelValues' => \array_values($labels), 63 | 'value' => $value, 64 | 'command' => \is_float($value) ? Adapter::COMMAND_INCREMENT_FLOAT : Adapter::COMMAND_INCREMENT_INTEGER, 65 | ] 66 | ); 67 | } 68 | 69 | /** 70 | * @param array $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 71 | */ 72 | public function inc(array $labels = []): void 73 | { 74 | $this->incBy(1, $labels); 75 | } 76 | 77 | /** 78 | * @param array $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 79 | */ 80 | public function dec(array $labels = []): void 81 | { 82 | $this->decBy(1, $labels); 83 | } 84 | 85 | /** 86 | * @param array $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 87 | */ 88 | public function decBy(int|float $value, array $labels = []): void 89 | { 90 | $this->incBy(-$value, $labels); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/BetterHistogram.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | private readonly array $buckets; 24 | 25 | /** 26 | * @param array $labels 27 | * @param non-empty-list|null $buckets 28 | */ 29 | public function __construct( 30 | Adapter $adapter, 31 | string $namespace, 32 | string $name, 33 | string $help, 34 | array $labels = [], 35 | ?array $buckets = null 36 | ) { 37 | parent::__construct($adapter, $namespace, $name, $help, $labels); 38 | 39 | if (null === $buckets) { 40 | /** @var non-empty-list */ 41 | $buckets = Histogram::getDefaultBuckets(); 42 | } 43 | 44 | /** @psalm-suppress TypeDoesNotContainType */ 45 | if (0 === \count($buckets)) { 46 | throw new InvalidArgumentException("Histogram must have at least one bucket."); 47 | } 48 | 49 | for ($i = 0; $i < \count($buckets) - 1; $i++) { 50 | if ($buckets[$i] >= $buckets[$i + 1]) { 51 | throw new InvalidArgumentException( 52 | "Histogram buckets must be in increasing order: " . 53 | $buckets[$i] . " >= " . $buckets[$i + 1] 54 | ); 55 | } 56 | } 57 | $this->buckets = $buckets; 58 | } 59 | 60 | /** @param array $labels */ 61 | protected static function assertValidLabels(array $labels): void 62 | { 63 | if (isset($labels['le'])) { 64 | throw new \InvalidArgumentException("Histogram cannot have a label named 'le'."); 65 | } 66 | parent::assertValidLabels($labels); 67 | } 68 | 69 | public function addLabels(array $labels): static 70 | { 71 | return new self( 72 | $this->storageAdapter, 73 | $this->namespace, 74 | $this->name, 75 | $this->help, 76 | $this->labels + $labels, 77 | $this->buckets 78 | ); 79 | } 80 | 81 | /** 82 | * @param double|int $value e.g. 123 83 | * @param array $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP'] 84 | */ 85 | public function observe(float|int $value, array $labels = []): void 86 | { 87 | self::assertValidLabels($labels); 88 | $labels = $this->labels + $labels; 89 | 90 | $this->storageAdapter->updateHistogram( 91 | [ 92 | 'value' => $value, 93 | 'name' => $this->metricName, 94 | 'help' => $this->help, 95 | 'type' => self::TYPE, 96 | 'labelNames' => \array_keys($labels), 97 | 'labelValues' => \array_values($labels), 98 | 'buckets' => $this->buckets, 99 | ] 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib/BetterSummary.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | private readonly array $quantiles; 25 | 26 | /** 27 | * @param array $labels 28 | * @param ?non-empty-list $quantiles 29 | */ 30 | public function __construct( 31 | Adapter $adapter, 32 | string $namespace, 33 | string $name, 34 | string $help, 35 | array $labels = [], 36 | private readonly int $maxAgeSeconds = 600, 37 | ?array $quantiles = null 38 | ) { 39 | parent::__construct($adapter, $namespace, $name, $help, $labels); 40 | 41 | if (null === $quantiles) { 42 | /** @var non-empty-list */ 43 | $quantiles = Summary::getDefaultQuantiles(); 44 | } 45 | 46 | /** @psalm-suppress TypeDoesNotContainType */ 47 | if (0 === \count($quantiles)) { 48 | throw new InvalidArgumentException("Summary must have at least one quantile."); 49 | } 50 | 51 | for ($i = 0; $i < \count($quantiles) - 1; $i++) { 52 | if ($quantiles[$i] >= $quantiles[$i + 1]) { 53 | throw new InvalidArgumentException( 54 | "Summary quantiles must be in increasing order: " . 55 | $quantiles[$i] . " >= " . $quantiles[$i + 1] 56 | ); 57 | } 58 | } 59 | 60 | foreach ($quantiles as $quantile) { 61 | if ($quantile <= 0 || $quantile >= 1) { 62 | throw new InvalidArgumentException("Quantile $quantile invalid: Expected number between 0 and 1."); 63 | } 64 | } 65 | 66 | if ($maxAgeSeconds <= 0) { 67 | throw new InvalidArgumentException("maxAgeSeconds $maxAgeSeconds invalid: Expected number greater than 0."); 68 | } 69 | 70 | $this->quantiles = $quantiles; 71 | } 72 | 73 | public function addLabels(array $labels): static 74 | { 75 | return new self( 76 | $this->storageAdapter, 77 | $this->namespace, 78 | $this->name, 79 | $this->help, 80 | $this->labels + $labels, 81 | $this->maxAgeSeconds, 82 | $this->quantiles 83 | ); 84 | } 85 | 86 | /** @param array $labels */ 87 | protected static function assertValidLabels(array $labels): void 88 | { 89 | if (isset($labels['quantile'])) { 90 | throw new \InvalidArgumentException("Sumamry cannot have a label named 'quantile'."); 91 | } 92 | parent::assertValidLabels($labels); 93 | } 94 | 95 | /** 96 | * @param double|int $value e.g. 123 97 | * @param array $labels e.g. ['status' => '404', 'opcode' => 'SOME_OP'] 98 | */ 99 | public function observe(float|int $value, array $labels = []): void 100 | { 101 | self::assertValidLabels($labels); 102 | $labels = $this->labels + $labels; 103 | 104 | $this->storageAdapter->updateSummary( 105 | [ 106 | 'value' => $value, 107 | 'name' => $this->metricName, 108 | 'help' => $this->help, 109 | 'type' => self::TYPE, 110 | 'labelNames' => \array_keys($labels), 111 | 'labelValues' => \array_values($labels), 112 | 'maxAgeSeconds' => $this->maxAgeSeconds, 113 | 'quantiles' => $this->quantiles, 114 | ] 115 | ); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | --------------------------------------------------------------------------------