UDP StatsD metrics are a way for your applications to send performance data to a monitoring system incredibly quickly.

Here’s a basic Python application sending a counter metric:

import socket

def send_statsd_metric(metric_name, metric_value, metric_type='c'):
    """Sends a metric to StatsD via UDP."""
    host = 'localhost'  # Replace with your StatsD host
    port = 8125         # Default StatsD port

    message = f"{metric_name}:{metric_value}|{metric_type}"
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(message.encode('utf-8'), (host, port))
    sock.close()

# Example usage: increment a counter
send_statsd_metric('my_app.requests_processed', 1)

# Example usage: send a gauge value
send_statsd_metric('my_app.current_users', 42, 'g')

This code opens a UDP socket, formats the metric as name:value|type, and fires it off to the StatsD server. The server listens on UDP port 8125 by default.

The power of UDP here is its speed. Unlike TCP, UDP doesn’t establish a connection, guarantee delivery, or retransmit lost packets. For metrics, this is often a good trade-off. If a single metric packet gets lost, it’s usually not critical; the next one will arrive shortly. This minimal overhead allows applications to spew metrics without significant performance impact.

StatsD supports several metric types:

  • Counter (c): For values that increase over time, like requests processed or errors encountered. You send the increment amount (e.g., my_app.errors:1|c). StatsD aggregates these into rates.
  • Gauge (g): For values that can go up or down, like current memory usage or active users. You send the absolute value (e.g., my_app.memory_usage:150|g).
  • Timer (ms): For measuring durations, like request latency. You send the time in milliseconds (e.g., my_app.request_latency:55|ms). StatsD calculates percentiles, averages, etc.
  • Set (s): For counting unique occurrences, like unique visitors. You send a unique identifier (e.g., my_app.unique_visitors:user123|s). StatsD counts distinct values.

The StatsD server (like the official Etsy one or Telegraf’s StatsD input) collects these raw metrics and then forwards them to a backend system (like Graphite, InfluxDB, Prometheus) for storage and visualization. The aggregation and processing happen on the StatsD server or its backend, not in your application.

The key configuration on the StatsD server side (e.g., in statsd.conf for the original StatsD) involves setting the host and port it listens on, and defining where to flush metrics. For example, a statsd.conf might look like this:

# statsd.conf
host = 0.0.0.0
port = 8125
flush_interval = 10000 # Flush every 10 seconds

[graphite]
host = 127.0.0.1
port = 2003
prefix = myapp

This tells StatsD to listen on all interfaces on port 8125, aggregate metrics for 10 seconds, and then send them to a Graphite instance at 127.0.0.1:2003 with a myapp prefix.

When your application sends my_app.requests_processed:1|c and the StatsD server flushes after 10 seconds, it might send myapp.my_app.requests_processed:120|c (if 120 requests came in 10 seconds) to Graphite. The myapp prefix is added by StatsD, and the actual count is derived from the individual increments.

The fact that StatsD aggregates metrics before flushing to the backend is crucial. Your application doesn’t need to know the IP address of Graphite or InfluxDB, nor does it need to handle the potentially high volume of data if you have many application instances. It just fires UDP packets. This decoupling is a major benefit.

If you’re sending metrics and they aren’t appearing, a common trap is assuming the StatsD server is running and accessible. Always verify the StatsD daemon is active and listening on the correct UDP port. A simple netstat -lunp | grep 8125 will show if anything is listening.

The next hurdle is understanding how to configure different aggregation strategies within StatsD itself, like sampling rates or custom aggregators.

Want structured learning?

Take the full Udp course →