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

Laravel Statsd Laravel Package

rcrowe/laravel-statsd

Laravel StatsD provides a simple facade and configuration for sending application metrics—counters, gauges, timers, and sets—to a StatsD server. Instrument Laravel events, requests, and custom code with straightforward metric calls.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require rcrowe/laravel-statsd

Register the service provider — this package predates package auto-discovery, so add it manually to config/app.php:

'providers' => [
    // ...
    RCrowe\Statsd\StatsdServiceProvider::class,
],

'aliases' => [
    // ...
    'Statsd' => RCrowe\Statsd\Facades\Statsd::class,
],

Publish the config so you can point it at your StatsD daemon:

php artisan vendor:publish --provider="RCrowe\Statsd\StatsdServiceProvider"

That drops config/statsd.php into your app. At minimum set host, port (usually 8125), and prefix (namespace your metrics per app/env, e.g. myapp.production). Drive these from .env so each environment gets its own bucket:

// config/statsd.php
return [
    'host'     => env('STATSD_HOST', '127.0.0.1'),
    'port'     => env('STATSD_PORT', 8125),
    'prefix'   => env('STATSD_PREFIX', 'myapp'),
    'disabled' => env('STATSD_DISABLED', false),
];

Where to look first

  1. vendor/rcrowe/laravel-statsd/src/ — the service provider shows the container binding (statsd) and which config keys the client actually reads. The facade shows the accessor.
  2. vendor/rcrowe/statsd-php/src/ — the underlying client. This is where the real API lives (increment, decrement, timing, gauge, gaugeDelta, set, time, beginTiming/endTiming).
  3. config/statsd.php — the only config surface you'll touch day to day.

First use case — the smallest thing that proves the pipeline end to end:

use Statsd;

Route::get('/ping', function () {
    Statsd::increment('ping.hit');
    return 'ok';
});

Then watch the metric land in Graphite/Grafana/Datadog (or tcpdump -i any -n udp port 8125 if you don't have a dashboard yet). If nothing appears, the problem is almost always the daemon, not the package — see Gotchas.

Implementation Patterns

Namespace every metric with the prefix. The config prefix is applied to every key automatically, so write metric names relative to it: Statsd::increment('web.ping') becomes myapp.production.web.ping. Don't repeat the app name in the key — you'll get myapp.myapp.web.ping.

Counters in the request lifecycle. The idiomatic Laravel spot is middleware, not controllers:

class MetricsMiddleware
{
    public function handle($request, Closure $next)
    {
        Statsd::increment('http.requests');
        Statsd::increment('http.status.'.$response->getStatusCode());
        return $response;
    }
}

Tagging by status code / route / method is cheap because StatsD keys are just strings — but see the cardinality warning in Gotchas.

Timing — use the closure form. The wrapper exposes the underlying time() helper, which handles the start/stop pair for you and is exception-safe:

$result = Statsd::time('report.generate', function () use ($user) {
    return $this->builder->for($user)->generate();
});

If you need to time something that doesn't fit a closure, use beginTiming('key') / endTiming('key'). Values are milliseconds — the client does not convert for you.

Gauges for point-in-time state (queue depth, cache size, active users). Gauges are absolute values, not deltas:

Statsd::gauge('queue.jobs', Queue::size('default'));
Statsd::gaugeDelta('queue.jobs', 1); // increment/decrement a gauge

Wrap the facade in your own service. Calling Statsd:: directly in twenty places makes testing painful and makes swapping backends later a refactor. A thin binding is worth it:

class Metrics
{
    public function __construct(private $statsd) {}

    public function count(string $key, int $by = 1): void
    {
        $this->statsd->increment($key, $by);
    }

    public function time(string $key, Closure $fn) { /* ... */ }
}

Bind it in a MetricsServiceProvider and resolve Metrics everywhere else. This also gives you one place to enforce naming conventions.

Jobs and listeners. Because transport is UDP, sending from a queue worker is effectively free and non-blocking — no reason to defer metrics into a separate job. Just call it inline in the listener.

Faking in tests. Two options:

  • Set STATSD_DISABLED=true in phpunit.xml so nothing leaves the test process.
  • Better: swap the statsd binding for a spy object that records calls, so you can assert Metrics was invoked with the right key:
$this->swap('statsd', $spy = new class {
    public array $increments = [];
    public function increment($k, $v = 1) { $this->increments[] = $k; }
    // ...mirror the methods you use
});

Only mirror the methods your code actually calls — the underlying client is a plain class, and PHP's dynamic nature means missing methods will blow up loudly enough during tests.

Metric naming convention. Pick one and stick to it: domain.subject.verb or app.area.metric. Dots nest into folders in Graphite; keep them consistent or your dashboards become a hunting expedition.

Gotchas and Tips

Nothing shows up until a StatsD daemon is listening. The package sends UDP and never reads a response — a misconfigured host or a stopped daemon fails silently. Debug with:

# On the statsd host
sudo tcpdump -i any -n -A udp port 8125

If packets arrive but no graphs appear, the issue is downstream (statsd config, carbon, Graphite retention), not Laravel.

UDP is fire-and-forget. Packets can be dropped under load or by a full socket buffer. Do not use this for anything you need accounting accuracy on (billing, audit counts). For those, write to a durable store and emit StatsD as a convenience signal.

timing() is milliseconds. Sending seconds (e.g. 1.2) looks like a sub-millisecond request. Use hrtime()/microtime() and multiply by 1000.

No-op in local/testing. Always set STATSD_DISABLED=true in .env.testing and local dev. Otherwise every test run sprays fake metrics into the shared staging bucket and pollutes dashboards.

Config cache bites. After changing config/statsd.php or the env vars, run php artisan config:cache (or clear it). The service provider reads config at boot, so stale cache = stale host/port/prefix.

Metric key characters matter. Colons, pipes, and newlines are part of the StatsD wire protocol (used to separate value and type). Stick to [a-zA-Z0-9._-]. Also avoid cardinality explosions: Statsd::increment('user.login.'.$userId) creates one metric per user and will kill your dashboard. Bucket into low-cardinality dimensions (user.login.success / user.login.fail).

Long-running processes (Octane, Horizon workers). The client is a thin UDP socket wrapper, so there's no persistent connection to leak or time out — but if you hold a resolved Statsd instance in a singleton across a config change, you keep the old prefix. Resolve from the container per-use if you're toggling config at runtime (rare; usually not worth it).

Don't let metrics break requests. Wrap calls in your Metrics wrapper with a try/catch (or a rescue()) if you're nervous — a metrics failure should never surface as a 500. The underlying client is unlikely to throw in normal operation, but an unreachable host combined with a future transport change could.

Extension point: swap the container binding. If you outgrow the bundled client (need TCP, batching, DogStatsD tags), rebind statsd in your own service provider to any object exposing the same methods. The facade doesn't care what's behind it — this is the cleanest migration path off the package without rewriting call sites.

Lumen. Register the provider in bootstrap/app.php with $app->register(...) and alias Statsd with class_alias(...) — no config/app.php there, and vendor publishing works differently (php artisan vendor:publish may not be available; copy the config file manually).

Version drift. 22 stars and an old wrapper means it likely targets older Laravel. If you're on Laravel 10+/11, verify the provider signature and facade against your version; if it chokes, bind rcrowe/statsd-php directly and skip this wrapper entirely — it's only a few lines of glue.

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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums