Weave 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

Symfony Prometheus Exporter Laravel Package

tasko-products/symfony-prometheus-exporter

A Symfony bundle that exposes application and runtime metrics to Prometheus, wiring up a scrape endpoint and collectors so you can monitor requests, performance, and custom counters without hand-rolling exposition code.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a Symfony bundle that wires promphp/prometheus_client_php into a DI container and exposes a /metrics scrape endpoint. Version 3.0.0 is a maintenance major (dependency bumps + code-style fixes) — the integration surface is unchanged from 2.2.0, so the steps below apply to both.

Minimal steps

  1. composer require tasko-products/symfony-prometheus-exporter:^3.0
  2. Register the bundle (config/bundles.php) and a storage adapter — in_memory for tests, APCu/Redis/PDO for anything that runs across multiple PHP-FPM workers.
  3. Declare your exporter/collector classes in the bundle config (tasko_prometheus_exporter.yaml → exporter: list).
  4. Hit /metrics and confirm the exposition text format.

Where to look first

  • src/Exporter/ and src/Collector/ — the ExporterInterface / CollectorInterface contracts and the Abstract* base classes you'll extend.
  • src/Controller/MetricsController.php — the scrape endpoint, its route and content-type handling.
  • DependencyInjection/Configuration.php — the exact config keys for storage adapter and exporter registration.
  • tests/ — the most reliable documentation of how services are expected to be wired.
  • composer.json — read the PHP, Symfony, and promphp/prometheus_client_php constraints before upgrading.

First use case

Expose two things and stop there: a counter for HTTP requests (method, route template, status) and a gauge for queue depth. That gives you a working dashboard, validates your storage adapter choice under real traffic, and forces you to confront cardinality discipline early — which is the only genuinely hard part of this package.

Implementation Patterns

Exporter vs. Collector — pick deliberately

Both are registered the same way, but they differ in when work happens:

  • Exporters declare a namespace/subsystem and register metric families. Use them for metrics your app owns and pushes (business counters, gauges set on state changes).
  • Collectors are invoked on every scrape and read live state from injected services. Use them for "current value" metrics — queue size, cache hit ratio, connection pool occupancy — where you want a fresh read without instrumenting every mutation site.

Rule of thumb: if the value is cheap to maintain incrementally, use an exporter. If it's cheap to read but expensive to maintain, use a collector.

Namespace/subsystem conventions

Treat the pair as part of your public API. app_, queue_, billing_ — stable, lowercase, no environment suffix baked in (environment belongs in a Prometheus label or a separate scrape target). Renaming a namespace is a breaking change for every dashboard and alert downstream.

Label discipline

Labels multiply series. Keep the set closed and enumerable:

  • Route templates (/orders/{id}), never resolved paths.
  • Status classes or a bounded code set, not raw exception messages.
  • Never: user IDs, request IDs, emails, raw SQL, unbounded hostnames.

If you need per-tenant breakdowns, emit a separate aggregate metric and use recording rules, not a tenant_id label.

Laravel adaptation

There is no Laravel service provider or package auto-discovery — this is Symfony-first. Wire it manually in a PrometheusServiceProvider:

  • Bind the storage adapter and the exporter registry as singletons, so metrics accumulate within a request and share a single backend connection.
  • Register the metrics route explicitly (Route::get('/metrics', ...)) and exclude it from auth, CSRF, and session middleware.
  • Record latency in terminating middleware, not in the controller — you want the full middleware stack included.
  • Keep the route behind network-level restriction (internal ingress / IP allowlist), not application auth, or Prometheus can't scrape it.

Testing

Bind the in-memory adapter in the test container, drive the code path, then render the endpoint and assert on the exposition text (# TYPE, # HELP, sample lines, label ordering). Asserting on rendered output rather than on internal counters catches namespace and label regressions that unit tests miss.

Gotchas and Tips

Treat 3.0.0 as potentially breaking despite the "nothing special" note. A major bump justified only as "housekeeping" is exactly where silent breaks hide:

  • Reread composer.json for raised PHP/Symfony minimums and any jump in promphp/prometheus_client_php. A transitive major there can change adapter behaviour or constructor signatures.
  • "Fixed some legacy code styles" can touch AbstractExporter / AbstractCollector — visibility, protected → private, return types, renamed helpers. If you subclass these, diff them and re-run your suite against 3.0.0 on a branch before merging.
  • If you're dependency-locked elsewhere, pin ^2.2 until you've verified; the upgrade has no feature payoff to weigh against the risk.

In-memory adapter is a test-only tool. Under PHP-FPM each request gets a fresh process and the registry resets, so counters restart and histograms stay near-empty. Symptom: /metrics looks fine in dev, goes flat or resets in production. Switch to APCu (single host), Redis (multi-host, preferred), or PDO (no extra infra) before shipping.

Don't share the Redis adapter's connection with your cache. Prometheus keys are long-lived and monotonically written; a FLUSHDB on cache invalidation, or a cache eviction policy, will silently destroy metrics history. Use a dedicated database index and noeviction.

Collectors run on every scrape — keep them O(1). A collector that issues a COUNT(*) over a large table, or fans out to several services, turns your monitoring into a load source and will get you rate-limited by Prometheus's scrape timeout (default 10s). Cache expensive reads for a few seconds.

Counters must be monotonic. Any code path that reconstructs an exporter or resets an in-memory registry mid-process breaks rate() and increase(), producing resets and bogus spikes. Set absolute values via gauges; increment counters.

Metric name validation is silent. Invalid characters or a namespace starting with a digit get sanitized, and the resulting metric name won't match your dashboards. Verify with promtool check metrics < <(curl -s localhost:8080/metrics) in CI.

Debugging workflow: curl -sv localhost:8080/metrics | head -50 to confirm content type (text/plain; version=0.0.4) and that your families appear at all; then check the registry — if the family is missing entirely, the exporter isn't registered (config list, service tag, or config cache). Clear the container cache after any config change; Symfony caches the exporter list aggressively.

Extension point to prefer: subclass AbstractExporter and keep constructors to a single injectable dependency, so autowiring stays trivial across both Symfony and hand-rolled Laravel bindings. Compose behaviour (a MetricsRecorder service injected into controllers and jobs) rather than adding a second base class layer — the abstracts are thin and not designed for deep hierarchies.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin