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

Metrics Bundle Laravel Package

itk-dev/metrics-bundle

A PHP metrics bundle from ITK Dev for integrating application metrics into your project, making it easier to collect and expose operational and business measurements through a reusable package.

View on GitHub
Deep Wiki
Context7

Getting Started

Before you type a single composer require, resolve the framework question: this is a Symfony bundle, not a Laravel package. It declares Symfony dependencies (symfony/framework-bundle, DI compiler passes, tagged services) and expects a kernel that registers bundles. In a Laravel codebase it will not boot, its collectors will not autowire, and its route import will not resolve. So there are two starting paths:

Path A — you are actually on Symfony. Install, register, configure:

composer require itk-dev/metrics-bundle
// config/bundles.php
return [
    // ...
    ItkDev\MetricsBundle\ItkDevMetricsBundle::class => ['all' => true],
];
# config/packages/itk_metrics.yaml
itk_metrics:
    storage:
        adapter: apcu        # not in_memory — see Gotchas
    namespace: myapp

Then import the bundle's routing entry (check vendor/itk-dev/metrics-bundle/Resources/config/routing.yaml for the exact import name — there is no Flex recipe, so you wire it manually).

Path B — you are on Laravel (the realistic case). Do not install this as a dependency. Treat it as a reference implementation: read Collector/, the storage adapter wrapper, and the controller that renders the exposition payload, then reproduce that ~100-line surface with promphp/prometheus_client_php behind a service provider. That is a smaller, safer job than maintaining a Symfony bundle fork.

Where to look first

  1. vendor/itk-dev/metrics-bundle/README.md — setup, storage adapters, config key.
  2. DependencyInjection/Configuration.php — the authoritative config tree. The README can drift; this file cannot.
  3. The exposition controller — this is the part you copy into Laravel.
  4. bin/console debug:config itk_metrics and bin/console debug:router | grep -i metrics to confirm it loaded.

First use case — scrape it by hand before Prometheus ever sees it:

curl -s http://localhost:8000/metrics | head -40

You want # HELP / # TYPE lines and counters in text/plain; version=0.0.4. A 404 means the route was not imported. An empty body means the storage adapter is not persisting across requests.

Prometheus side:

scrape_configs:
  - job_name: myapp
    metrics_path: /metrics
    static_configs:
      - targets: ['myapp.internal:8000']

Laravel equivalent first milestone (if you took Path B):

// routes/web.php — behind auth middleware, not public
Route::get('/metrics', fn () => response(
    Prometheus::render(),
    200,
    ['Content-Type' => 'text/plain; version=0.0.4']
))->middleware(['metrics.auth']);

Implementation Patterns

Instrument in middleware, not in controllers. The bundle's collectors hang off the request lifecycle. Reproduce that in Laravel so every route is measured for free:

final class ObserveRequest
{
    public function handle($request, Closure $next)
    {
        $timer = Histogram::get('http_request_duration_seconds');
        $start = microtime(true);
        try {
            return $next($request);
        } finally {
            $timer->observe(microtime(true) - $start, [
                'method' => $request->method(),
                'status' => (string) $response->getStatusCode(),
            ]);
        }
    }
}

Use the tagged-collector pattern (Symfony) or a Collector registry (Laravel). The bundle exposes a collector interface; each implementation is DI-tagged and called on scrape. Mirror that with a container tag in Laravel (Metrics::register() in a service provider) so adding a new metric source is a class, not an edit to a controller. This is the one abstraction worth preserving from the bundle.

Pick the storage adapter deliberately.

Adapter Scope Use when
in_memory Per-request only Unit tests and a 5-minute smoke test
apcu Per-host, survives requests Single-node or sticky sessions
redis Cross-host Multi-replica behind a load balancer

Under PHP-FPM, in_memory will produce an empty or all-zeros /metrics. Choose APCu or Redis before you instrument anything.

Start with RED/USE, not a full taxonomy. Request rate, error rate, duration; for hosts, utilisation/saturation/errors. Add business metrics only once a dashboard consumes them. A metric nobody alerts on is a maintenance cost with no return — that is the whole lesson of the assessment-pm.md discussion around ROADmap and adoption telemetry.

Prefix and name consistently. Set namespace: myapp so every metric comes out myapp_http_request_duration_seconds. Follow Prometheus conventions: counters end _total, durations end _seconds, labels are snake_case, and never put unbounded values in a label.

Wire Prometheus, dashboard, then broaden. Sequence: scrape target → one Grafana panel → alerting rule → then more instrumentation. Instrumenting broadly first means you cannot tell a broken exporter from a quiet system.

Multi-instance behaviour. The pull model means every replica gets scraped. With Redis storage, the collector adds a network hop on the scrape path — isolate it, set a scrape timeout, and ensure a Redis outage degrades the endpoint rather than the app.

Gotchas and Tips

No release since July 2022. Expect declared constraints around PHP 8.0/8.1 and Symfony 5.4/6. On PHP 8.3/8.4 or Symfony 7 you will need to relax composer.json constraints in a fork. Pin the fork (Composer VCS or path repository), name an internal owner, and never depend on upstream Packagist for anything production-critical.

Symfony's DI tags are inert in Laravel. Compiler passes, autoconfigured collector tags, and bundle routing do not translate. Any adoption on Laravel is a port, not an integration — price it as such.

The /metrics endpoint must not be public. It leaks internal topology: hostnames, versions, SQL timing, sometimes user counts. Put it behind a middleware, a basic-auth wall, or an ingress IP allowlist. This is the single most common real-world incident with self-hosted exporters.

Cardinality is the failure mode that takes down Prometheus, not your app. Never label by user ID, request ID, raw path, or email. Allow-list labels at the instrumentation layer. A single unbounded label will OOM Prometheus within weeks and is the least fun outage to debug.

Scrape timeouts block PHP-FPM workers. A slow collector on /metrics consumes a worker per scrape per replica. Keep collectors O(1) — no queries against tables with millions of rows, no outbound HTTP in a collector.

Debugging by symptom:

Symptom Likely cause
404 Routing entry not imported
200, empty body in_memory adapter under PHP-FPM
Counters reset to 0 APCu cleared / new host in the pool
Prometheus target down Path mismatch, auth wall, or scrape timeout
Metric appears but no data Wrong namespace, or collector never registered

Verify the exposition format, not just the HTTP status. Add a smoke test that parses the payload (or run promtool check metrics) after every dependency bump. Abandoned packages break silently on PHP upgrades — you will not get a maintainer issue thread to tell you.

Extension point worth keeping. The collector interface plus the storage-abstraction layer are the reusable parts. Everything else — bundle registration, compiler passes, Symfony routing — is framework glue you would rewrite anyway. If you fork, keep the interface and the storage adapter, drop the rest.

When it is the wrong answer entirely. If you need tracing, exemplars, log correlation, or high-cardinality business analytics, skip this and go OpenTelemetry. This bundle does counters, gauges, and timings over a text scrape endpoint — that is genuinely the whole surface, and it is cheaper to reach for promphp/prometheus_client_php or spatie/laravel-prometheus directly than to accumulate custom instrumentation you will later have to migrate.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource