How to Create a StatsD UDP Metric Mock Server in Python
Run a lightweight mock UDP server that captures StatsD metrics over a short window for local testing.
Python code
41 linesimport socket
import threading
import time
def start_mock_statsd_server(host="127.0.0.1", port=8125, timeout=3):
"""Run a mock StatsD UDP server that captures metrics for a short window."""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((host, port))
sock.settimeout(timeout)
metrics = []
def collect():
while True:
try:
data, _ = sock.recvfrom(4096)
metrics.append(data.decode("utf-8").strip())
except socket.timeout:
break
thread = threading.Thread(target=collect, daemon=True)
thread.start()
thread.join(timeout + 1)
sock.close()
return metrics
if __name__ == "__main__":
# Simulate a client sending metrics while the mock server is listening
mock_thread = threading.Thread(target=lambda: time.sleep(0.2))
mock_thread.start()
results = start_mock_statsd_server(timeout=1)
client_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_sock.sendto(b"page.visits:5|c", ("127.0.0.1", 8125))
client_sock.sendto(b"api.latency:124|ms", ("127.0.0.1", 8125))
client_sock.sendto(b"errors:1|c", ("127.0.0.1", 8125))
client_sock.close()
mock_thread.join()
print("Captured metrics:", results)
Output
Captured metrics: ['page.visits:5|c', 'api.latency:124|ms', 'errors:1|c']
How it works
The mock server binds a UDP socket to a local port and listens until the timeout expires. recvfrom blocks until datagrams arrive or the socket times out, incrementally storing each decoded metric string. A daemon thread keeps collecting in the background while the main flow can test client behavior concurrently. This pattern works cleanly because UDP is fire-and-forget — no acknowledgment handshake — making it ideal for lightweight, in-process metric validation. The explicit sett timeout guarantees the server doesn't hang forever, enabling deterministic test runs.
Common mistakes
- Forgetting to call `sock.settimeout` — the server blocks forever without a timeout
- Sending datagrams after the mock exits — the collection window ends once `join` returns
- Binding to the wrong host (0.0.0.0 vs 127.0.0.1) when testing from external threads
- Not reusing the same port — UDP sockets require a unique bind unless multiple sockets reuse the address
Variations
- Use a context manager with `with socket.socket(...)` to auto-close the socket.
- Collect metrics into a list inside a lock to protect concurrent thread access.
Real-world use cases
- Simulating StatsD servers in integration tests to assert that app metrics are emitted correctly.
- Validating metrics format and content in CI before deploying code that sends observability telemetry.
- Building a lightweight traffic generator to exercise metric handling paths without a real metrics backend.
Sponsored
More from Observability & SRE
- Adding a Correlation ID to Log Context in Python medium
- Calculate Error Rate from Log Stream in Python easy
- Check if a Timestamp Falls in a Daily Maintenance Window in Python easy
- Export Metrics with OTLP Mock in Python medium
- Generate Mock CPU and Memory Metrics in Python easy
- Generate Prometheus Text Exposition Format in Python easy
Keep learning
Related tutorials and quizzes for this topic.