statsd

statsd implements the nurago metrics contract over the StatsD protocol, pushing counters, gauges and timers to a local agent over UDP or TCP.

Part of nurago, a collection of independent Go packages for backend services.

import "github.com/tecnickcom/nurago/pkg/metrics/statsd"

A metrics.Client that pushes to a StatsD daemon, which aggregates and forwards to whatever sits behind it. Built on github.com/tecnickcom/statsd.

m, err := statsd.New(
    statsd.WithPrefix("myservice"),
    statsd.WithNetwork("udp"),
    statsd.WithAddress("127.0.0.1:8125"),
    statsd.WithFlushPeriod(time.Second),
)

Push, and what UDP buys

The process sends metrics out rather than waiting to be scraped, normally to an agent on the same host or in the same pod.

Over UDP that send is fire-and-forget: no connection, no handshake, no acknowledgement, no blocking. Instrumentation cannot slow the code it measures and cannot fail it. The price is that delivery is not guaranteed, and a lost datagram is a lost sample with no way to detect it.

For high-frequency counters and timers that is usually the right trade, because the aggregate is what matters and individual samples are not. For a metric where every event counts, WithNetwork("tcp") gives delivery at the cost of a connection and back-pressure.

WithFlushPeriod controls batching. Metrics accumulate in a buffer and go out in one packet per period, which cuts syscalls sharply on a busy service. A longer period means fewer packets and staler data, plus more to lose if the process dies.

Two capabilities the interface has and this backend does not

Stated plainly because they are easy to be surprised by:

MetricsHandlerFunc returns HTTP 501 Not Implemented. There is nothing to scrape; the data has already gone. The route still exists and responds, so mounting it unconditionally is safe.

InstrumentDB is a no-op. SQL pool statistics are not collected by this backend, so the pool saturation metrics available through prometheus are not available here.

SqlOpen still returns a working *sql.DB.

Naming, and why the prefix matters

StatsD has no label dimensions. A metric is a dotted name and a value, so everything you would express as a label in Prometheus has to be in the name: myservice.http.request.GET.users.200.

WithPrefix sets the leading segment, keeping one service’s metrics separable from another’s when they share a daemon. Without it, two services emitting http.request.count are indistinguishable at the aggregator.

The same cardinality caution applies as anywhere else, for a different reason: a name built from a request path or a user identifier creates an unbounded set of metric names at the aggregator, which is worse than unbounded labels.

When push suits better than scrape

A short-lived process can exit before a scraper ever reaches it. Batch jobs, CLI tools, migration runners and cron work all report nothing under a pull model, and report normally under this one.

The same applies to a process that is not addressable by the scraper: behind NAT, on an ephemeral worker, or in an environment where inbound connections are not available.

Close flushes what is buffered. Skipping it on a short-lived process loses the final window, which for a job that runs for two seconds may be all of it.

Dependencies

Reaches github.com/tecnickcom/statsd.