tweedegolf/prometheus-client
Archived/unmaintained PHP Prometheus client by Tweede Golf. Provides Counter and Gauge metrics with multiple storage backends (e.g., APC/APCu) and a text formatter to expose a /metrics endpoint for Prometheus scraping.
Installation:
composer require tweedegolf/prometheus-client
Ensure your Laravel app has Redis and OPcache extensions installed (php -m | grep redis and php -m | grep opcache).
Basic Setup:
use TweedeGolf\PrometheusClient\CollectorRegistry;
use TweedeGolf\PrometheusClient\Storage\ApcuAdapter;
$registry = new CollectorRegistry(new ApcuAdapter());
Define Metrics:
$registry->createCounter('api_requests_total', [], 'Total API requests');
$registry->createGauge('api_latency_seconds', [], 'API latency in seconds');
Expose Metrics Endpoint (e.g., /metrics):
use TweedeGolf\PrometheusClient\Format\TextFormatter;
Route::get('/metrics', function () use ($registry) {
$formatter = new TextFormatter();
header('Content-Type', $formatter->getMimeType());
return $formatter->format($registry->collect());
});
Update Metrics in Code:
$registry->getCounter('api_requests_total')->inc();
$registry->getGauge('api_latency_seconds')->set($executionTime);
Metric Lifecycle:
AppServiceProvider)./metrics).Middleware Integration:
$registry->getCounter('api_requests_total')->inc();
$registry->getGauge('api_latency_seconds')->set(microtime(true) - LARAVEL_START);
Event-Based Updates:
event(new JobProcessed($job));
// In listener:
$registry->getCounter('jobs_processed_total')->inc();
Labelled Metrics:
$registry->createCounter('http_requests_total', ['method', 'endpoint']);
$registry->getCounter('http_requests_total')->inc(['method' => 'GET', 'endpoint' => '/users']);
Service Container Binding:
$app->singleton('prometheus.registry', function () {
return new CollectorRegistry(new ApcuAdapter());
});
Inject via constructor: public function __construct(private CollectorRegistry $registry).
Queue Job Metrics:
$registry->getCounter('queue_jobs_total')->inc();
$registry->getGauge('queue_job_duration_seconds')->set($duration);
Database Query Metrics:
Use DB::listen() to track query counts/latency:
DB::listen(function ($query) use ($registry) {
$registry->getCounter('db_queries_total')->inc();
$registry->getGauge('db_query_duration_seconds')->set($query->time);
});
Storage Backend Quirks:
mget is supported (Redis ≥2.6). Fallback to individual get calls if needed:
if (!method_exists($redis, 'mget')) {
$registry->setStorage(new RedisAdapter($redis, false)); // Disable mget
}
Label Cardinality:
user_id). Prometheus struggles with >100 unique label values per metric.Metric Naming:
api_requests_total), not camelCase. Prometheus expects snake_case.OPcache Dependencies:
false check for opcache_get_status may log warnings if OPcache is disabled. Suppress with:
error_reporting(E_ALL & ~E_NOTICE); // Temporarily disable notices
Concurrency Issues:
dispatch(new UpdateMetricsJob($registry, 'api_requests_total', 1));
Metric Not Updating?:
curl http://localhost/metrics | grep api_requests_total.KEYS *prometheus*).High Memory Usage:
mget can spike memory. Monitor with:
redis-cli info memory
Prometheus Scrape Failures:
/metrics endpoint returns 200 OK and valid Prometheus format:
curl -I http://localhost/metrics
Custom Storage:
Implement StorageAdapterInterface for databases or other backends:
class DatabaseAdapter implements StorageAdapterInterface {
public function get($name) { /* ... */ }
public function set($name, $value) { /* ... */ }
public function delete($name) { /* ... */ }
}
Custom Formatters:
Extend TextFormatter for JSON/Graphite output:
class JsonFormatter extends TextFormatter {
public function format($metrics) { /* ... */ }
public function getMimeType() { return 'application/json'; }
}
Laravel Events:
Hook into Illuminate\Queue\Events\JobProcessed or Illuminate\Http\KernelEvents:
event(new JobProcessed($job));
// In listener:
$registry->getCounter('queue_jobs_total')->inc();
Prometheus Rules:
Define alerting rules in prometheus.rules.yml:
groups:
- name: laravel-alerts
rules:
- alert: HighAPILatency
expr: api_latency_seconds > 1
for: 5m
How can I help you explore Laravel packages today?