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 Php Client Laravel Package

liuggio/statsd-php-client

A lightweight PHP client for StatsD and Etsy StatsD metrics. Send counters, gauges, timers, and sets with sampling and batching support, plus configurable backends, so you can instrument your application and feed data into Graphite dashboards.

Deep Wiki
Context7

Getting Started

liuggio/statsd-php-client is a thin, dependency-light PHP client for the StatsD line protocol over UDP. It is an old package (last tagged release July 2015) but its surface area is small enough that you can read the whole thing in an hour. Treat it as a "protocol formatter + socket wrapper," not as a full observability SDK.

Install and verify

composer require liuggio/statsd-php-client

Then run a smoke test against a local listener (nc -ul 8125 or the official statsd docker image) to confirm packets leave the process.

Where to look first (in order)

  1. src/Liuggio/StatsdClient/StatsdClient.php – the orchestrator; send() accepts a single StatsdDataInterface or an array.
  2. src/Liuggio/StatsdClient/Factory/StatsdDataFactory.php – the metric vocabulary (increment, decrement, timing, gauge, …). This is where you'll spend most of your time.
  3. src/Liuggio/StatsdClient/Sender/SocketSender.php – the UDP transport. Read open()/write()/close() so you understand where failures are swallowed.
  4. src/Liuggio/StatsdClient/Entity/StatsdData.php – __toString() is the actual wire format (name:value|type, optionally |@sampleRate).

Minimal wiring

use Liuggio\StatsdClient\Factory\StatsdDataFactory;
use Liuggio\StatsdClient\Sender\SocketSender;
use Liuggio\StatsdClient\StatsdClient;

$sender  = new SocketSender('127.0.0.1', 8125);
$client  = new StatsdClient($sender);
$factory = new StatsdDataFactory();

$client->send($factory->increment('app.requests'));
$client->send($factory->timing('app.render_ms', 128));
$client->send($factory->gauge('app.queue_depth', 42));

First real use case — instrument one endpoint. Count the request, time the handler, emit a gauge for a queue depth, and don't refactor the rest of the app yet. Confirm the metrics arrive in your backend before standardizing names.

Laravel wiring (one provider, one config file):

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

// AppServiceProvider::register()
$this->app->singleton(StatsdClient::class, fn () => new StatsdClient(
    new SocketSender(config('statsd.host'), config('statsd.port'))
));
$this->app->singleton(StatsdDataFactory::class, fn () => new StatsdDataFactory());

Implementation Patterns

1. Factory + client is the unit of work. The factory produces immutable StatsdData value objects; the client writes them out. Keep the factory available anywhere (container singleton) and only hand the client to code that actually transmits.

2. Batch aggressively. StatsdClient::send() accepts an array, so accumulate in a request-scoped buffer and flush once. This is the biggest throughput lever you have.

$buffer = [];
$buffer[] = $factory->increment('api.requests');
$buffer[] = $factory->timing('api.latency_ms', $ms);
$client->send($buffer); // one datagram

3. StatsdService for ergonomic sugar. The bundled service wrapper gives you increment(), decrement(), timing(), gauge() shortcuts that chain and buffer, plus a send() to flush. It's convenient in a controller; verify its exact flush semantics in your pinned version before relying on auto-send in long-running workers (Octane/Swoole), where an un-flushed buffer will leak across requests.

4. Middleware is the canonical Laravel integration point. A StatsdMiddleware that records http.requests, http.latency_ms, and http.status.<code> gives you 80 % of your operational visibility for ~20 lines. Flush in terminate() so you're off the response-critical path:

public function handle($request, Closure $next)
{
    $start = hrtime(true);
    $response = $next($request);
    $this->buffer->push($this->factory->increment('http.requests'));
    $this->buffer->push($this->factory->timing(
        'http.latency_ms',
        (int) ((hrtime(true) - $start) / 1e6)
    ));
    return $response;
}

public function terminate($request, $response) { $this->buffer->flush(); }

5. Centralize metric names. Build a small constants class or enum (Metrics::HTTP_REQUESTS) and prepend the configured prefix inside a thin wrapper. This prevents the app.foo / foo / Foo drift that makes dashboards useless six months later.

6. Sampling. Every factory method takes a $sampleRate argument (default 1); pass 0.1 to send roughly 10 % of events with a |@0.1 suffix and let StatsD scale the counts. Sample counters and timings by all means, but don't sample gauges — you'll get misleading spiky values. For high-volume endpoints, sampling is preferable to batching larger payloads, because datagram size is bounded.

7. Timing helper. The package has no stopwatch abstraction, so write one yourself (hrtime(true) for monotonicity, microtime(true) if you must support PHP < 7.3). A tiny Timing::start() / Timing::stop() utility keeps every call site measuring the same way.

8. Testing. Two options, both cheap: (a) inject a mock SenderInterface into StatsdClient and assert on write() calls; (b) use the bundled EchoSender, which prints instead of sending, for a fast integration test against the real factory/entity pipeline. Never let tests touch a real UDP socket — a dropped packet is a flaky assertion.

9. Custom senders are the natural extension point. Implement SenderInterface and hand it to StatsdClient to ship metrics to a log, a queue, a Unix-domain socket, or a Telegraf/vector sidecar. This is how you get off UDP later without touching call sites.

10. Framework-agnostic use is fine. Outside Laravel, hold the client in whatever container or global you already have; there's no framework coupling in the library.

Gotchas and Tips

  • The package is effectively unmaintained. Last release July 2015. It still works on modern PHP because it's so simple, but run your suite after upgrading PHP; you may hit deprecation notices from undeclared/implicit-nullable parameters. Pin the version and vendor-vet rather than trusting composer update blindly.
  • UDP is fire-and-forget. A full or unreachable socket is silent. The Sender may return false or throw depending on your version — always wrap send() in a try/catch and log at debug level. Never let metrics failures fail a user request.
  • A socket is opened per send (in the stock SocketSender). On a busy web app this is real overhead. Batch every request into one send() call; if you need more, implement a sender that keeps a persistent socket or writes to a sidecar.
  • Datagram size matters. StatsD typically accepts ~1432 bytes per UDP packet (often clamped to 512 in some servers). Large batches of long metric names will be truncated or dropped. Shorter names, fewer metrics per flush, or sampling all help. Newlines are the batch delimiter — never allow \n in a metric name (also sanitize user-supplied labels).
  • Sample rates and gauges don't mix. As above: sampling a gauge produces nonsense. Also remember |@x is a scaling hint for counters/timers, not a filter for the client.
  • Gauge semantics depend on the backend. Some servers treat gauge as "last value wins," others support relative +n/-n updates. Don't assume update()/gauge() do the same thing across StatsD, Telegraf, and Datadog; check the emitted type character (g vs c vs ms) with EchoSender if unsure.
  • Counter decrement is just a negative counter. There is no separate wire type; decrement('x') sends roughly x:-1|c. Keep that in mind when writing dashboards.
  • No tags/DogStatsD support out of the box. If you're on Datadog or OpenTelemetry and need dimensions, this package won't emit them. Either encode dimensions into the metric name (cardinality!) or move to a modern client. This is the single biggest reason to look elsewhere for greenfield work.
  • Don't measure cold-start code paths. Timing everything from bootstrap will drown you in noise; instrument handler-level boundaries, DB calls, and queue jobs.
  • Long-running processes (Octane, queue workers) need explicit flushes. Request-scoped buffers that are flushed in terminate() are fine in classic PHP-FPM; in Octane you must make sure the buffer is reset per request, or you'll attribute metrics to the wrong request.
  • Configuration should come from env, not code. Host, port, prefix, and a global enable/disable flag (STATSD_ENABLED=false for local dev) belong in config. The client has no NullSender, but a one-line SenderInterface that returns true is a 30-second write and makes local dev noise-free.
  • Reading the source is a genuine debugging strategy. There's no logging, no introspection, and almost no documentation beyond the README. When a metric doesn't appear, the fastest path is EchoSender + var_dump((string) $data) to see exactly what would hit the wire, then nc -ul 8125 to confirm the packet arrives.
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.
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
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