m6web/statsd-bundle
Symfony bundle for sending StatsD metrics over UDP. It provides service configuration and helpers for counters, gauges, timers, and sets, making it easy to instrument applications with lightweight performance and business metrics.
Architecture fit — poor as delivered, salvageable as a pattern. Despite the task framing, this is not a Laravel package: m6web/statsd-bundle is a Symfony bundle. It ships a Bundle subclass, DI extension, tagged-service collectors, and kernel event subscribers wired through symfony/framework-bundle and symfony/dependency-injection. Laravel uses illuminate/container and service providers; it consumes several Symfony components (http-foundation, console, routing, translation, process) but not HttpKernel or FrameworkBundle. The bundle's composition model — compile-time tagged collectors — has no direct analogue in Laravel's runtime container. What is genuinely portable is the inner layer: the StatsD client wrapper and the metric/decorator classes that translate application events into counters, gauges, timings, and sets.
Integration feasibility — moderate, via extraction rather than installation. composer require m6web/statsd-bundle into a Laravel app will pull Symfony DI/HttpKernel/Config and produce a bundle that nothing boots. The realistic path is to lift the client abstraction and the collector pattern into a first-party Laravel package: one service provider, one configuration file, a singleton StatsD client bound to an interface, request-timing as terminable middleware, and domain metrics emitted from listeners or explicit calls. The underlying transport (typically a domnikl/statsd-style UDP client, which the bundle wraps) is small enough that a fork is cheaper than fighting the container mismatch.
Technical risk — concentrated, not diffuse.
Key questions.
timing()/increment() calls sufficient for v1?Stack fit. Treat this as a reference implementation, not a dependency. The extractable assets are the metric naming conventions, the decorator/timer helpers, and the request-lifecycle hooks. The non-transferable assets are the bundle class, the DI extension, and the YAML configuration tree.
Migration path (target: ~1–2 engineer-weeks for a first-party Laravel package).
league/statsd or vendor the client class directly) — no Symfony packages.MetricsClient interface in the app (increment, gauge, timing, histogram, set), with a no-op implementation for tests and local dev.config/statsd.php (host, port, prefix, sample rate, enabled, tags/global labels) and bind the concrete client as a singleton in a service provider.handle(), emit the timing and status-code counter in terminate(), so metrics never sit on the response path.Metrics facade for ad-hoc emission.composer.json once parity is confirmed; keep the fork under an internal namespace if any collector logic was reused verbatim.Compatibility. Pin the transport library to a version verified against the current PHP runtime; validate the UDP socket path under both CLI (queue workers, schedulers) and FPM. Confirm the app's Symfony component versions (via illuminate/*) are not constrained by a transitive bundle dependency — this is the most likely hidden conflict and should be checked with composer why symfony/dependency-injection before writing any code.
Sequencing. Interface and no-op client first (unblocks instrumentation work in parallel), then transport, then request middleware, then domain collectors, then dashboards and alerts. Emit metrics behind the interface from day one so the backend decision never blocks application code.
Maintenance. Low ongoing code churn — the client surface is ~hundreds of lines — but zero upstream leverage. Budget for owning the fork indefinitely: PHP major upgrades, transport library bumps, and review of naming/tag conventions as the metric catalog grows. The real maintenance cost is not the code; it is metric catalog hygiene — unbounded tag values are the most common way this becomes expensive.
Support. No Laravel ecosystem support, no maintainer SLA, no LTS. Documentation is Symfony-oriented and thin. Internal runbooks for the statsd/DogStatsD agent, its config, and its restart behavior are mandatory deliverables of adoption.
Scaling. UDP emission is cheap and effectively non-blocking — typically sub-millisecond per emit with negligible CPU, so per-request overhead is not the constraint. Constraints are elsewhere: (a) the StatsD agent is a single point of failure on every host — if it dies, metrics vanish silently; (b) UDP is lossy under load and subject to MTU fragmentation on multi-metric packets, so counts drift and are acceptable only for trend/alerting, never for billing; (c) high-cardinality tags (user IDs, request IDs, raw paths) can overwhelm Graphite or inflate Datadog costs quickly — enforce a naming/tag allowlist in review; (d) for queue workers and long-lived processes, verify the client does not accumulate in-memory buffers that grow unbounded.
Failure modes.
try/catch and log at debug.Ramp-up. Small and fast for the extraction itself (an engineer familiar with Laravel service providers and middleware can port the pattern in under two weeks). The slower curve is operational literacy: the team needs working knowledge of the StatsD wire protocol, sampling semantics, agent configuration, and the difference between counter/gauge/timing/histogram — otherwise instrumentation gets emitted with the wrong type and dashboards lie. Given the package's low adoption signal, treat this as building an internal capability with a public reference, not as integrating a dependency.
How can I help you explore Laravel packages today?