Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Prometheus Client Php Laravel Package

promphp/prometheus_client_php

Prometheus client library for PHP with Redis or APCu-based aggregation (plus in-memory adapter). Register and update counters, gauges, histograms, and summaries via CollectorRegistry, then expose metrics in Prometheus text format for scraping.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:

    composer require promphp/prometheus_client_php
    
  2. Choose a storage adapter (default: Redis):

    • For local development with Redis:
      docker run -p 6379:6379 redis
      
    • For APCu (no external dependencies): Ensure apcu PHP extension is installed (pecl install apcu).
  3. First metric in Laravel: Add this to a controller or service:

    use Prometheus\CollectorRegistry;
    use Prometheus\RenderTextFormat;
    
    // Increment a counter
    CollectorRegistry::getDefault()
        ->getOrRegisterCounter('app', 'requests_total', 'Total HTTP requests')
        ->inc();
    
    // Expose metrics endpoint (e.g., `/metrics`)
    Route::get('/metrics', function () {
        $registry = CollectorRegistry::getDefault();
        $renderer = new RenderTextFormat();
        header('Content-type: ' . RenderTextFormat::MIME_TYPE);
        return $renderer->render($registry->getMetricFamilySamples());
    });
    

Key First Use Cases

  • HTTP Request Tracking:
    $requestCounter = CollectorRegistry::getDefault()
        ->getOrRegisterCounter('http', 'requests_total', 'Total HTTP requests', ['method']);
    $requestCounter->inc(['method' => request()->method()]);
    
  • Latency Measurement:
    $histogram = CollectorRegistry::getDefault()
        ->getOrRegisterHistogram('http', 'request_duration_seconds', 'Request duration', ['route'], [0.1, 0.5, 1, 2.5, 5]);
    $start = microtime(true);
    // ... execute logic ...
    $histogram->observe(microtime(true) - $start, ['route' => request()->path()]);
    

Implementation Patterns

1. Metric Registration Strategies

Service-Level Metrics

Centralize metric registration in a service class:

class MetricsService {
    protected $registry;

    public function __construct() {
        $this->registry = CollectorRegistry::getDefault();
    }

    public function registerRequestMetrics() {
        $this->registry->getOrRegisterCounter('app', 'requests_total', 'Total requests');
        $this->registry->getOrRegisterGauge('app', 'active_requests', 'Active requests');
        $this->registry->getOrRegisterHistogram('app', 'request_latency_seconds', 'Request latency', [], [0.01, 0.05, 0.1, 0.5, 1]);
    }
}

Register in AppServiceProvider:

public function boot() {
    app(MetricsService::class)->registerRequestMetrics();
}

Middleware for Automatic Metrics

class MetricsMiddleware {
    public function handle($request, Closure $next) {
        $start = microtime(true);
        $response = $next($request);
        $latency = microtime(true) - $start;

        $histogram = CollectorRegistry::getDefault()
            ->getCounter('http', 'requests_total')
            ->inc(['method' => $request->method(), 'path' => $request->path()]);

        $histogram = CollectorRegistry::getDefault()
            ->getHistogram('http', 'request_latency_seconds')
            ->observe($latency, ['path' => $request->path()]);

        return $response;
    }
}

2. Labeling Patterns

Dynamic Labels

Use closures or helper methods to generate labels dynamically:

$counter = CollectorRegistry::getDefault()
    ->getOrRegisterCounter('app', 'errors_total', 'Total errors', ['type', 'severity']);

$counter->inc([
    'type' => 'database',
    'severity' => $this->getErrorSeverity($exception)
]);

Label Sets for Reusability

class LabelSets {
    public static function getUserLabels($userId) {
        return ['user_id' => $userId, 'role' => auth()->user()->role];
    }
}

// Usage:
$counter->inc(array_merge(
    ['type' => 'auth'],
    LabelSets::getUserLabels($userId)
));

3. Storage Adapter Integration

Redis (Recommended for Production)

Configure Redis connection in config/services.php:

'redis' => [
    'host' => env('REDIS_HOST', '127.0.0.1'),
    'port' => env('REDIS_PORT', 6379),
    'password' => env('REDIS_PASSWORD', null),
],

Override default Redis options:

\Prometheus\Storage\Redis::setDefaultOptions([
    'host' => config('services.redis.host'),
    'port' => config('services.redis.port'),
    'password' => config('services.redis.password'),
    'timeout' => 0.5,
]);

APCu (For Single-Process Apps)

$registry = new CollectorRegistry(new \Prometheus\Storage\APCu());
CollectorRegistry::setDefault($registry);

Predis (Alternative Redis Client)

$client = new \Predis\Client(['host' => '127.0.0.1', 'password' => 'secret']);
$registry = new CollectorRegistry(\Prometheus\Storage\Predis::fromExistingConnection($client));

4. Custom Metric Types

Extend the library for domain-specific metrics:

class CustomSummary extends \Prometheus\Summary {
    public function observeWithQuantiles(float $value, array $labels, array $quantiles) {
        // Custom logic
    }
}

5. Testing Metrics

Use CollectorRegistry in tests to verify metrics:

public function testRequestMetrics() {
    $counter = CollectorRegistry::getDefault()
        ->getOrRegisterCounter('test', 'counter', 'Test counter');

    $counter->inc();

    $samples = CollectorRegistry::getDefault()->getMetricFamilySamples();
    $this->assertCount(1, $samples);
}

Gotchas and Tips

1. Storage Adapter Pitfalls

Adapter Gotcha Tip
Redis Connection timeouts if Redis is overloaded. Use timeout and read_timeout in config.
Data loss if Redis crashes. Enable Redis persistence (appendonly yes).
APCu Metrics reset on cache eviction. Use apcu.enable_cli=1 for CLI scripts.
APCng Memory leaks with large label sets. Prefer Redis for high-cardinality labels.
PDO Slow performance with frequent writes. Use batch inserts or a dedicated metrics database.
InMemory Metrics lost between requests. Only use for short-lived scripts (e.g., cron jobs).

2. Labeling Quirks

  • Label Value Types: Must be strings. Non-string values (e.g., int, bool) are auto-converted.
    // Works:
    $counter->inc(['status' => 200]); // Converted to "200"
    // Avoid:
    $counter->inc(['status' => true]); // Converted to "1" (unexpected)
    
  • Label Collisions: Avoid dynamic labels with identical keys (e.g., ['id' => 1] and ['id' => 2]). Fix: Use a prefix (e.g., ['user_id' => 1]).
  • Label Limits: Redis has a 512MB limit for keys. Large label sets may cause issues. Fix: Limit labels to 5-10 per metric.

3. Performance Tips

  • Batch Writes: For high-throughput apps, batch metric updates:
    $registry->batchUpdate(function ($registry) {
        $registry->getCounter('app', 'events_total')->inc();
        $registry->getGauge('app', 'active_users')->set(42);
    });
    
  • Disable Metrics in Production: Use an environment variable to toggle metrics:
    if (!env('DISABLE_METRICS', false)) {
        $registry = CollectorRegistry::getDefault();
        // ... register metrics ...
    }
    
  • Histogram Buckets: Use exponential buckets for latency metrics:
    $histogram = CollectorRegistry::getDefault()
        ->getOrRegisterHistogram('http', 'latency_seconds', 'Request latency', [], Histogram::exponentialBuckets(0.01, 2, 10));
    

4. Debugging

  • Metric Not Found: Throws MetricNotFoundException. Check:
    • Typos in metric names/labels.
    • Storage adapter connectivity (Redis/APCu).
    try {
        $counter = $registry->getCounter('nonexistent', 'metric');
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport