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

Statsd Prometheus Bundle Laravel Package

m6web/statsd-prometheus-bundle

Symfony bundle that bridges StatsD metrics to Prometheus: it collects counters, gauges and timings via the m6web/statsd library and exposes them in Prometheus exposition format for scraping, typically through a /metrics endpoint.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and register

    composer require m6web/statsd-prometheus-bundle
    
    // config/bundles.php
    M6Web\Bundle\StatsdPrometheusBundle\M6WebStatsdPrometheusBundle::class => ['all' => true],
    

    Requires at least v8.0.2 if you are on a modern Symfony: that patch widened the constraints to symfony/contract 3.* and psr/container 2.*, which is what Symfony 6.3+/7.x ships. Older tags will fight your lockfile on psr/container.

  2. Configure the bridge — the bundle wires two things: a DogStatsD client that emits tagged metrics, and a Prometheus exporter that turns those metrics into a scrapeable exposition format. A typical config block looks like this (confirm the exact alias and keys against the bundle's DependencyInjection/Configuration.php, since the package is lightly documented):

    # config/packages/m6web_statsd_prometheus.yaml
    m6web_statsd_prometheus:
        statsd:
            host: '%env(STATSD_HOST)%'
            port: '%env(int:STATSD_PORT)%'
        prometheus:
            namespace: 'app'
            # storage adapter (in-memory / apcu / redis) is usually configurable here
    
  3. Where to look first

    • README.md for the canonical usage example (there is usually one end-to-end snippet).
    • DependencyInjection/Configuration.php — the real source of truth for config keys and defaults.
    • Resources/config/services.yaml — service IDs and aliases you can autowire.
    • Run bin/console debug:container --tag=statsd (and search for prometheus) to see what's actually injected in your app.
  4. First use case — inject the client into a controller/service and emit a tagged counter:

    use M6Web\Bundle\StatsdPrometheusBundle\Client\Client; // confirm class/interface name
    
    final class CheckoutController
    {
        public function __construct(private Client $statsd) {}
    
        public function pay(): Response
        {
            $this->statsd->increment('checkout.payment', 1, [
                'gateway' => 'stripe',
                'status'  => 'ok',
            ]);
            // ...
        }
    }
    

    Then hit the Prometheus exposition endpoint (or bin/console command) the bundle registers and verify the metric appears with the tags translated to labels.

Implementation Patterns

  • Tagged metrics as labels. The core value is that #gateway:stripe,status:ok in DogStatsD becomes {gateway="stripe",status="ok"} in Prometheus. Design your tag set once and reuse it via constants/enums so the client call sites and the scrape output stay consistent.
  • One call site, two sinks. Because the bundle records to a Prometheus registry while also emitting DogStatsD (or vice versa depending on config), treat the metric name and tag keys as a public contract. Renaming a tag is a breaking change for any dashboards/alerts built on top.
  • Service-level instrumentation with a decorator. Wrap domain services rather than sprinkling client calls:
    final class InstrumentedPaymentGateway implements PaymentGateway
    {
        public function __construct(
            private PaymentGateway $inner,
            private Client $statsd,
        ) {}
    
        public function charge(int $cents): void
        {
            $start = microtime(true);
            try {
                $this->inner->charge($cents);
                $this->statsd->increment('payment.charge', 1, ['result' => 'success']);
            } catch (\Throwable $e) {
                $this->statsd->increment('payment.charge', 1, ['result' => 'error']);
                throw $e;
            } finally {
                $this->statsd->timing('payment.charge.duration', (microtime(true) - $start) * 1000);
            }
        }
    }
    
    Wire it in services.yaml with decorates: App\PaymentGateway.
  • Request-level metrics via a kernel listener. A kernel.terminate subscriber is the natural place to emit http.request counters and duration histograms, keyed by route, method, and status class — keeping tag cardinality bounded.
  • Scrape endpoint. Point Prometheus at the bundle's exporter route (or a custom controller wrapping the registry). Keep it on the internal port/network and behind the same auth as your other /metrics endpoints.
  • Testing. In tests, replace the client with a spy/stub collecting (name, value, tags) triples and assert on those; this is more reliable than trying to read the Prometheus registry back.
  • Long-running workers. For Messenger consumers or custom daemons, emit metrics after each message rather than per-message-inside-a-batch, and flush/close on kernel.terminate — otherwise the Prometheus side may not see updates until the process exits (depending on storage adapter).

Gotchas and Tips

  • Cardinality is the trap. Every tag key becomes a Prometheus label. Tagging with user_id, order_id, email, or raw URLs will explode the time series and take down your Prometheus server. Restrict tags to low-cardinality dimensions (status, route, gateway, region).
  • Naming rules differ. DogStatsD tolerates dots (payment.charge.duration); Prometheus prefers snake_case with units suffixes (payment_charge_duration_seconds). The bundle usually normalizes, but verify the output — a silent rename breaks dashboards. Reserve a naming convention up front.
  • Metric type mismatch. A name registered as a counter cannot later be used as a gauge/histogram in the same registry; you'll get a duplicate/conflict error at runtime. Keep a metric catalogue (name → type → allowed tags) in the repo.
  • StatsD is fire-and-forget over UDP. No errors if the statsd agent is down. If metrics "disappear," check the agent first: nc -u -l 8125 and look for packets, then check the Prometheus endpoint separately.
  • Storage backend matters. The Prometheus PHP client defaults to in-memory storage, which is per-PHP-process and therefore useless behind FPM. For real scraping you need APCu or Redis storage; verify the bundle exposes that config and that the adapter is installed. Without it, your /metrics will often look empty.
  • Long-running and FPM lifecycle. In-memory state resets per request; Redis/APCu persists. Choose deliberately and document it, because dashboards will otherwise "flicker" depending on which worker served the scrape.
  • Dependency constraints (v8.0.2). The only recent change is dependency-related: symfony/contract ^3.0 and psr/container ^2.0 are now allowed. That is what unblocks Symfony 6.3+/7.x installs (Symfony ships psr/container: ^2.0 since 6.3). It is a patch-level constraint widening with no API change, so it does not by itself prove runtime compatibility with Symfony 7 — run your test suite and boot the kernel before assuming it. If you are still on 5.4, stay on the older tag; if you are on 6.4/7.x, pin ^8.0.2.
  • Debugging workflow. Emit a metric, hit the exposition endpoint, curl it, and grep for the name. If it's absent, check: (1) was the client actually called, (2) is the tag set valid, (3) is the storage backend the one you think, (4) is the route registered (bin/console debug:router | grep metrics).
  • Extension points. The client and the exporter are ordinary services — override them in services.yaml (decorates: or alias replacement) to add sampling, redaction of sensitive tags, or to route to an alternate registry. The exporter is also the right seam to add a prometheus_client_php adapter if the bundled default doesn't fit.
  • Vendor risk. The project lives under BedrockStreaming/StatsdTagsPrometheusBundle upstream, with low star count and no meaningful dependent base. The v8.0.2 patch is maintenance-only — dependency bumps, no features, no roadmap. Pin the exact version in composer.json ("m6web/statsd-prometheus-bundle": "8.0.2"), commit the lockfile, and re-check constraints on every Symfony upgrade; be prepared to fork. Note that a two-year-old patch release means the next Symfony minor may well break the install again with no upstream fix. If you only need one of the two halves (tagged statsd or a /metrics endpoint), a smaller hand-rolled service plus promphp/prometheus_client_php may be lower risk than adopting the whole bundle.
  • Config quirks. Bundle aliases for m6web packages are easy to guess wrong; if debug:config doesn't recognize your YAML block, dump Configuration.php and copy the root key exactly.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core