dbstudios / prometheus-client
A client library for Prometheus
Installs: 11 057
Dependents: 1
Suggesters: 0
Security: 0
Stars: 2
Watchers: 2
Forks: 1
Open Issues: 0
Requires
- php: >=7.3
- ext-json: *
Requires (Dev)
- ext-apcu: *
- ext-redis: *
- phpunit/phpunit: ^5.0
- symfony/stopwatch: ^4.4|^5.1
Suggests
- ext-apcu: If you want to use the APCu adapter
- ext-redis: If you want to use the Redis adapter
- symfony/stopwatch: If you want to use the Histogram timing features
README
Run the following command in your project root.
$ composer require dbstudios/prometheus-client
Getting Started
There are two components that you'll need to set up in order to start using this library. The first is the
CollectorRegistry
, which acts as a repository for your collectors. There isn't any special configuration to worry
about, all you need is an instance you can can access anywhere in your application.
<?php use DaybreakStudios\PrometheusClient\CollectorRegistry; $registry = new CollectorRegistry();
Next up is an adapter, which acts as an interface between the library code, and your chosen storage system. At the time of writing, this library ships with support for the following adapters.
Instantiation will vary from adapter to adapter, so please check the documentation for the adapter you're using.
<?php use DaybreakStudios\PrometheusClient\Adapter\Apcu\ApcuAdapter; $adapter = new ApcuAdapter();
And finally, you'll need one or more collectors.
<?php use DaybreakStudios\PrometheusClient\Collector\Counter; use DaybreakStudios\PrometheusClient\Collector\Gauge; use DaybreakStudios\PrometheusClient\Collector\Histogram; $counter = new Counter($adapter, 'test_counter', 'Please ignore'); $registry->register($counter); $gauge = new Gauge($adapter, 'test_gauge', 'Please ignore'); $registry->register($gauge); $histogram = new Histogram($adapter, 'test_histogram', 'Please ignore', [ 1, 5, 15, 50, 100 ]); $registry->register($histogram);
Once a collector is registered, you can either expose them as global variables, or by name via the CollectorRegistry
(which needs to be globally accessible in some way).
<?php $testCounter = $registry->get('test_counter'); $testCounter->increment(); $testHistogram = $registry->get('test_histogram'); $testHistogram->observe(153);
"Strong" Typing
To help enforce types when retrieving collectors from the registry, you can use the getCounter()
, getGauge()
, and
getHistogram()
methods in place of the basic get()
method.
<?php $counter = $registry->getCounter('test_counter'); $counter->increment(); $histogram = $registry->getHistogram('test_gauge'); // throws DaybreakStudios\PrometheusClient\Exception\CollectorRegistryException due to type mismatch
In addition to performing the same null
checking that get()
performs, each of those methods will also check that the
collector is of the expected type, and throw an exception if the collector is not. They'll also correctly enable IDE
autocompletion, since those three methods specify the proper return type in their PHPDoc block.
Using Labels
You must define all of a collector's labels when its registered.
<?php use DaybreakStudios\PrometheusClient\Collector\Counter; $counter = new Counter($adapter, 'api_calls_total', 'Number of API calls made', [ 'path', 'method', ]); $counter->increment([ 'method' => 'GET', 'path' => '/users/me', ]);
The order in which you specify the labels when using the collector (i.e. in Counter::increment()
in the example above)
does not matter, however ALL label values must be provided each time.
Exporting
You can export data from your registry by setting up an endpoint in your application with code similar to the code below.
<?php use DaybreakStudios\PrometheusClient\Export\Render\TextRenderer; $renderer = new TextRenderer(); header('Content-Type: ' . $renderer->getMimeType()); echo $renderer->render($registry->collect());
Collectors
Collectors are the interface between your code and Prometheus. The Prometheus spec currently defines 3 supported collector types, which are documented below.
For more information on the concepts of collectors, please refer to the official Prometheus documentation.
Counters
Counters are the simplest form of collector. The official Prometheus documentation describes counters like so.
A counter is a cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart.
Basically, a counter is a collector that can only increase, usually by increments of one. For example, you might use it to track the number of requests an application has served, or the number of database queries an application has executed.
<?php use DaybreakStudios\PrometheusClient\Collector\Counter; /** * @var \DaybreakStudios\PrometheusClient\Adapter\AdapterInterface $adapter */ $counter = new Counter($adapter, 'requests_served', 'The number of requests processed by the application'); $counter->increment(); $counter->increment([], 3);
All arguments to Counter::increment()
are optional. The first argument is the labels to use for
the counter being incremented. The second argument is the step for the increment (defaults to one).
Gauges
A gauge is very similar to a counter. However, unlike counters, they can increase or decrease, and can even have their current value set to a specific number. The official Prometheus documentation describes gauges like so.
A gauge is a metric that represents a single numerical value that can arbitrarily go up and down.
A gauge is useful for tracking things like temperature, or CPU or memory usage.
<?php use DaybreakStudios\PrometheusClient\Collector\Gauge; /** * @var \DaybreakStudios\PrometheusClient\Adapter\AdapterInterface $adapter */ $gauge = new Gauge($adapter, 'cpu_usage_last5', 'CPU usage average over the last 5 minutes'); $gauge->increment(); $gauge->decrement(); $gauge->set(1.7);
Much like a counter, the arguments to Gauge::increment()
and Gauge::decrement()
are optional. Both
accept up to two arguments: labels and the step, in that order.
You can use Gauge::set()
to update the gauge to a specific value. It accepts one or two arguments, the first being
the required value to set the gauge to, and the second optional value being the labels for the gauge.
Histograms
A histogram is (compared to counters and gauges) a relatively complex collector. The official Prometheus documentation describes histograms like so.
A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets. It also provides a sum of all observed values.
Histograms can be useful when you're dealing with a metric that might vary wildly each time it's recorded, and can be used to separate the recorded metrics into quantiles for easier analysis. For example, a histogram may be useful in analyzing execution times for different code paths, and recording data in a way that outliers can be easily identified (such as that one particularly slow piece of code that you can't make run any faster no matter how many hundreds of hours you throw at it).
<?php use DaybreakStudios\PrometheusClient\Collector\Histogram; /** * @var \DaybreakStudios\PrometheusClient\Adapter\AdapterInterface $adapter */ $histogram = new Histogram( $adapter, 'api_call_execution_time_milliseconds', 'Call time to external API', [50, 1000, 5000] ); $histogram->observe(17); $histogram->observe(120); $histogram->observe(2700); $histogram->observe(7010);
Since histograms are often used for timing, there are two built-in utility methods for working with time. Please note
that you will need to install the symfony/stopwatch
component in order to use timing features.
<?php /** * @var \DaybreakStudios\PrometheusClient\Collector\Histogram $histogram */ $timer = $histogram->startTimer(); usleep(10000); // ~10 ms $timer->observe(); // Tracks time since Histogram::startTime() was invoked and adds it to the histogram $histogram->time(function() { usleep(100000); // ~100 ms }); // Invokes the callable passed to Histogram::time() and adds the execution time as a value of the histogram
By default, histograms track time with millisecond precision. However, a histogram accepts an optional constructor argument to change this to second precision, if that's better suited for your needs.
<?php use DaybreakStudios\PrometheusClient\Collector\Histogram; use DaybreakStudios\PrometheusClient\Collector\HistogramTimer; /** * @var \DaybreakStudios\PrometheusClient\Adapter\AdapterInterface $adapter */ $histogram = new Histogram( $adapter, 'api_call_execution_time_seconds', 'Call time to external API', [100, 1000, 5000], [], HistogramTimer::PRECISION_SECONDS, ); $histogram->time(function() { usleep(1100000); // ~1100 ms or 1.1 s }); // Adds 1 to the histogram (e.g. `$histogram->observe(1)`)
Adapters
This library provides access to the underlying storage system via adapters. The built-in adapters are documented below.
Redis
The DaybreakStudios\PrometheusClient\Adapter\Redis\RedisAdapter
uses Redis to store metrics. To use the Redis adapter,
you simply need to provide it with the host of the Redis instance.
<?php use DaybreakStudios\PrometheusClient\Adapter\Redis\RedisAdapter; use DaybreakStudios\PrometheusClient\Adapter\Redis\RedisClientConfiguration; $config = new RedisClientConfiguration('localhost'); // You can also supply other information, such as port or password, using the setters // available on the configuration object, e.g.: // - $config->setPort(1234) // - $config->setPassword('MyTotallySecurePassword') $adapter = new RedisAdapter($config);
Keys in the Redis adapter are automatically prefixed in order to prevent collisions with other keys that might be in
your Redis instance. By default, the prefix is "dbstudios_prom:", but you can change this by providing a second argument
to the constructor of RedisClientConfiguration
.
Filesystem
The DaybreakStudios\PrometheusClient\Adapter\Filesystem\FilesystemAdapter
uses files to store metrics. Data written to
the adapter's files is encoded using PHP's serialize()
function, so
types will be properly preserved. To use the FilesystemAdapter
, you will need to specify which directory the adapter
should use to store it's files. In order to prevent data loss, the directory you specify should only be used by
Prometheus.
<?php use DaybreakStudios\PrometheusClient\Adapter\Filesystem\FilesystemAdapter; $adapter = new FilesystemAdapter('/var/www/html/prometheus');
Unlike the APCu adapter, cached data will persist, even if your server reboots. You can use the
FilesystemAdapter::clear()
method to remove all files from the adapter's cache. Please keep in mind that this will
delete everything in the directory you specified as the adapter's base directory.
APCu
The DaybreakStudios\PrometheusClient\Adapter\Apcu\ApcuAdapter
uses APCu to
store metrics. The APCU adapter uses no additional configuration.
There are a few pitfalls to be aware of, however. APCu, by default, does not persist stored data through certain events, such as a server reboot. Additionally, it also wipes its entire cache once the cache fills up. Neither of those should cause problems for your Prometheus installation, but it's something you should keep in mind if you choose to use the APCu adapter.
Additionally, APCu does not properly support accessing its cache for PHP sessions started from the command line.
Under a default configuration, every call to an apcu_*
function is "black holed", meaning that they'll always return
false
, and will not store any data in the cache. You can enable the CLI cache by adding apc.enable_cli=1
to your
php.ini
, but that will only keep information in the cache for the run time of the script. Once the script is done
executing, the cache data will be purged. As far as I'm aware, there is no way to alter this behavior.