/php-prometheus-client

Collect and export PHP App metrics with ease

Primary LanguagePHPMIT LicenseMIT

PHP Prometheus Client

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\InMemory\InMemoryCounterStorage;
use Zlodes\PrometheusClient\Storage\InMemory\InMemoryGaugeStorage;
use Zlodes\PrometheusClient\Storage\InMemory\InMemoryHistogramStorage;
use Zlodes\PrometheusClient\Storage\InMemory\InMemorySummaryStorage;
use Zlodes\PrometheusClient\Metric\Summary;
use Zlodes\PrometheusClient\Fetcher\StoredMetricsFetcher;

$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();
    }
}