artprima/prometheus-metrics-bundle
Symfony bundle integrating promphp/prometheus_client_php to expose Prometheus metrics for your app. Supports Symfony 5.4–8.x and PHP 8.2–8.5, with configurable metric namespace, ignored routes, and custom labels for HTTP/request metrics.
Installation:
composer require artprima/prometheus-metrics-bundle
Enable the Bundle:
Add to config/bundles.php:
Artprima\PrometheusMetricsBundle\ArtprimaPrometheusMetricsBundle::class => ['all' => true],
Expose Metrics Endpoint:
Add to config/routes.yaml:
app_metrics:
resource: '@ArtprimaPrometheusMetricsBundle/Resources/config/routing.yaml'
Verify:
Access /metrics/prometheus in your browser or via curl to see default metrics.
The bundle automatically tracks:
http_requests_total)http_2xx_responses_total, http_5xx_responses_total)http_request_duration_seconds)Example output:
# HELP symfony_http_requests_total total request count
# TYPE symfony_http_requests_total counter
symfony_http_requests_total{action="GET-homepage",method="GET"} 42
Create a Collector Class:
Implement RequestMetricsCollectorInterface or ResponseMetricsCollectorInterface:
use Artprima\PrometheusMetricsBundle\Metrics\RequestMetricsCollectorInterface;
use Prometheus\CollectorRegistry;
class CustomCollector implements RequestMetricsCollectorInterface {
public function collectRequest(RequestEvent $event) {
$registry = $this->getRegistry(); // Injected via init()
$counter = $registry->getOrRegisterCounter(
'myapp',
'custom_metric',
'Description',
['label']
);
$counter->inc(['value']);
}
}
Automatic Registration: With autoconfigure enabled (default), tag your service:
services:
App\Metrics\CustomCollector:
tags: ['prometheus_metrics_bundle.metrics_collector']
Lifecycle Hooks: Use interfaces for specific events:
PreRequestMetricsCollectorInterface: Pre-request processingExceptionMetricsCollectorInterface: Error trackingConsoleCommandMetricsCollectorInterface: CLI metrics (enable via config)kernel.request, kernel.response, and kernel.exception events for granular control.labels config to attach request attributes/headers:
labels:
- { name: "user_id", type: "request_attribute", value: "_user_id" }
in_memory, redis, or apcu via config:
storage:
type: redis
host: redis.example.com
grafana/symfony-app-overview.json) for pre-built visualizations.Dynamic Metrics:
Use MetricInfoResolverInterface to customize metric naming/labeling:
class DynamicResolver implements MetricInfoResolverInterface {
public function resolve(MetricInfo $metricInfo) {
$metricInfo->setName('dynamic_' . $metricInfo->getName());
}
}
Custom Storage:
Implement StorageFactoryInterface for non-standard backends (e.g., database):
class DatabaseFactory implements StorageFactoryInterface {
public function create(array $options): Adapter {
return new DatabaseAdapter($options['dsn']);
}
}
Conditional Collection: Skip metrics for specific routes:
ignored_routes: ['api/v1/health', 'admin/*']
Double Registration:
disable_default_metrics: true in config.Label Collisions:
action).MetricInfoResolver.Redis Connection Issues:
storage.options:
storage:
options:
retry_attempts: 3
retry_delay: 100
Console Metrics:
enable_console_metrics: true doesn’t work if CLI events aren’t dispatched.ConsoleApplication is properly configured.curl -s http://localhost/metrics/prometheus | grep "symfony_http"
bin/console debug:event-dispatcher to confirm collectors are attached.in_memory, dump the registry:
$registry = $container->get('prometheus.registry');
print_r($registry->getMetricFamilySamples());
High Cardinality Labels:
user_id) can bloat storage.user_segment).Histogram Buckets:
buckets in config:
buckets: [0.05, 0.1, 0.5, 1, 2, 5]
APCu/APCng:
in_memory or Redis.Metric Filtering:
Override Artprima\PrometheusMetricsBundle\Metrics\MetricsCollector to filter metrics dynamically:
public function shouldCollect(RequestEvent $event): bool {
return !$event->getRequest()->isXmlHttpRequest();
}
Custom Exporters:
Extend Artprima\PrometheusMetricsBundle\Exporter\MetricsExporter to add OpenTelemetry or other formats.
Rate Limiting:
Use PreRequestMetricsCollectorInterface to enforce metric collection quotas:
if ($this->rateLimiter->isOverLimit($request)) {
return;
}
namespace: "service-payments"
dev):
# config/packages/dev/artprima_prometheus_metrics.yaml
disable_default_metrics: true
ignored_routes: '%env(IGNORED_ROUTES)%'
How can I help you explore Laravel packages today?