zlodes/prometheus-client

The Prometheus Metrics client for PHP

2.0.3 2024-04-21 13:13 UTC

README

codecov

This package provides you an ability to collect and export Prometheus metrics from any modern PHP application.

Why?

  • Until now, there was no working Prometheus client for modern PHP
  • Framework-agnostic
  • Almost zero dependencies
  • Won't break your business logic even if something is wrong with Metrics Storage
  • Ready to use with static analysis tools (PHPStan, Psalm)

Adapters

Installation

composer require zlodes/prometheus-client

Flow

TL;DR: Read Simple example.

1. Preparation

  1. Set up a storage to store metrics. There are four interfaces can be implemented:
    1. CounterStorage
    2. GaugeStorage
    3. HistogramStorage
    4. SummaryStorage
  2. Set up a Registry to register your metrics. ArrayRegistry is a default implementation.
  3. Register your metrics using the Registry from step 2.

2. Collecting

  1. Get a collector for your metric from a CollectorFactory
  2. Call metric update method (e.g. increment on CounterCollector)

3. Exporting

  1. Create a controller to export metrics. Your controller should use Exporter. FetcherExporter is a default implementation.
  2. Set up a Prometheus to scrape metrics from your application using the controller from step 1.

Simple example

<?php

use Psr\Log\NullLogger;
use Zlodes\PrometheusClient\Collector\CollectorFactory;
use Zlodes\PrometheusClient\Exporter\FetcherExporter;
use Zlodes\PrometheusClient\Metric\Counter;
use Zlodes\PrometheusClient\Metric\Gauge;
use Zlodes\PrometheusClient\Metric\Histogram;
use Zlodes\PrometheusClient\Registry\ArrayRegistry;
use Zlodes\PrometheusClient\Storage\InMemoryStorage;

$registry = new ArrayRegistry();

$counterStorage = new InMemoryCounterStorage();
$gaugeStorage = new InMemoryGaugeStorage();
$histogramStorage = new InMemoryHistogramStorage();
$summaryStorage = new InMemorySummaryStorage();

// Register your metrics
$registry
    ->registerMetric(
        new Gauge('body_temperature', 'Body temperature in Celsius')
    )
    ->registerMetric(
        new Counter('steps', 'Steps count')
    )
    ->registerMetric(
        (new Histogram('http_request_duration_seconds', 'HTTP Request duration'))
            ->withBuckets([0.1, 0.5, 1]),
    )
    ->registerMetric(
        (new Summary('memory_used', 'Used memory in bytes'))
            ->withQuantiles([0.5, 0.9, 0.99])
    );

// Create a Collector factory

$collectorFactory = new CollectorFactory(
    $registry,
    $counterStorage,
    $gaugeStorage,
    $histogramStorage,
    $summaryStorage,
    new NullLogger(),
);

// Collect metrics
$bodyTemperatureGauge = $collectorFactory->gauge('body_temperature');

$bodyTemperatureGauge
    ->withLabels(['source' => 'armpit'])
    ->update(36.6);

$bodyTemperatureGauge
    ->withLabels(['source' => 'ass'])
    ->update(37.2);

$collectorFactory
    ->counter('steps')
    ->increment();

$requestTimer = $collectorFactory
    ->histogram('http_request_duration_seconds')
    ->startTimer();

usleep(50_000);

$requestTimer->stop();

$collectorFactory
    ->summary('memory_used')
    ->update(100);

$collectorFactory
    ->summary('memory_used')
    ->update(200);

// Export metrics
$fetcher = new StoredMetricsFetcher(
    $registry,
    $counterStorage,
    $gaugeStorage,
    $histogramStorage,
    $summaryStorage,
);

$exporter = new FetcherExporter($fetcher);

foreach ($exporter->export() as $metricOutput) {
    echo $metricOutput . "\n\n";
}

Output example:

# HELP steps Steps count
# TYPE steps counter
steps 1

# HELP body_temperature Body temperature in Celsius
# TYPE body_temperature gauge
body_temperature{source="armpit"} 36.6
body_temperature{source="ass"} 37.2

# HELP http_request_duration_seconds HTTP Request duration
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds{le="0.1"} 1
http_request_duration_seconds{le="0.5"} 1
http_request_duration_seconds{le="1"} 1
http_request_duration_seconds{le="+Inf"} 1
http_request_duration_seconds_sum 0.050071506
http_request_duration_seconds_count 1

# HELP memory_used Used memory in bytes
# TYPE memory_used summary
memory_used{quantile="0.5"} 150
memory_used{quantile="0.9"} 190
memory_used{quantile="0.99"} 199
memory_used_sum 300
memory_used_count 2

Testing

Run tests

php ./vendor/bin/phpunit

Creating your own Storage

Keys serialization

There is a Serializer interface (with JSON-based implementation) to simplify work with a key-value storage.

Example can be found in InMemoryStorage.

Storage Testing

There are four useful traits to simplify your storage testing:

  1. CounterStorageTesting
  2. GaugeStorageTesting
  3. HistogramStorageTesting
  4. SummaryStorageTesting

They provide you a set of tests to check your storage implementation to be compatible with the library.

Example:

<?php

use PHPUnit\Framework\TestCase;
use Zlodes\PrometheusClient\Storage\Contracts\CounterStorage;
use Zlodes\PrometheusClient\Storage\InMemory\InMemoryCounterStorage;
use Zlodes\PrometheusClient\Storage\Testing\CounterStorageTesting;

class InMemoryCounterStorageTest extends TestCase
{
    use CounterStorageTesting;

    private function createStorage(): CounterStorage
    {
        return new InMemoryCounterStorage();
    }
}