hot-shots
A Node.js client for emitting StatsD metrics to Datadog's DogStatsD agent, Telegraf, the OpenTelemetry Collector StatsD receiver or a classic StatsD daemon. It sends counters, gauges, sets, timings and histograms over UDP, TCP, Unix domain sockets or a supplied stream, with tags, sampling, buffering, child clients and optional client-side aggregation. Datadog mode also supports distributions, events, service checks, origin detection, cardinality hints and client telemetry.
The practical Node choice when a StatsD-compatible agent is already part of your platform, especially for DogStatsD features and high-volume local emission. Do not mistake a local socket write for durable telemetry, and choose OpenTelemetry directly when StatsD is only an accidental legacy hop.
Use it if
- Your infrastructure already runs a StatsD-compatible local agent or collector and application code only needs to emit metrics
- You need DogStatsD tags, distributions, events, service checks or Unix socket delivery from Node
- You want to reduce packet volume through buffering or client-side aggregation of hot counters, gauges and sets
- You need the same client to target Datadog, Telegraf, OpenTelemetry Collector or classic StatsD with documented protocol switches
- You do not operate a StatsD agent or collector: hot-shots is only the client and defaults to sending UDP at loopback port 8125, where missing infrastructure can make metrics disappear quietly
- You need end-to-end delivery guarantees: UDP is lossy, and even a successful send callback proves only that the local transport accepted bytes, not that a backend stored the metric
- You target browsers, edge isolates or Node older than 18; version 17.1.0 is a Node client and declares Node 18 as its minimum runtime
- You require Unix domain sockets on Windows or in an environment without node-gyp: the README says the optional unix-dgram dependency cannot provide UDS there
- You are standardizing metrics, traces and logs on OpenTelemetry semantic conventions: the OTel Metrics SDK avoids retaining a StatsD translation layer and its backend-specific feature gaps
Setup reality
npm install hot-shots has no mandatory dependencies, but it does not install or configure a metrics daemon. With no transport settings it resolves Datadog-related environment variables and otherwise sends UDP to local port 8125, so a development process can appear healthy while every packet goes nowhere. Decide on StatsD, DogStatsD, Telegraf or the OpenTelemetry receiver first because tags, histograms, sets, timestamps, distributions, events and checks are not supported equally. Node 18 or newer is required; the shipped declarations require TypeScript 4 or newer. UDP and TCP need host, port and network policy. Datadog UDS needs the optional unix-dgram native module, a mounted socket path, node-gyp tooling and a non-Windows host. Buffering changes callbacks from asynchronous send results into synchronous queued notifications; later flush failures go to errorHandler. Aggregation further delays counts, gauges and sets, caps distinct contexts at 5,000 by default, and bypasses several metric forms. Set errorHandler on the root client, monitor client telemetry, and control tag cardinality before production. Metrics sent in process exit handlers will not complete because the event loop has already stopped. Flush and close from SIGTERM, SIGINT or another graceful shutdown path, and wait for the callback before exiting.
Patterns
Create a production UDP clientcreate-client
import StatsD from 'hot-shots';
const metrics = new StatsD({
host: process.env.STATSD_HOST,
port: 8125,
prefix: 'checkout',
globalTags: { env: process.env.NODE_ENV, service: 'api' },
errorHandler(error) {
console.error('StatsD error', error);
},
});Without an explicit host or Datadog environment variables, UDP resolves to the loopback interface. Confirm an agent is actually listening on port 8125.
Increment a tagged countersend-counter
metrics.increment('requests', 1, {
tags: { route: '/orders', status: '200' },
});Tags work with DogStatsD and Telegraf, not every classic StatsD server. Avoid user IDs, request IDs and other unbounded tag values.
Record gauges, histograms and timingssend-gauge-histogram
metrics.gauge('queue.depth', 17);
metrics.histogram('payload.bytes', body.length);
metrics.timing('request.duration', elapsedMs);Histogram semantics vary by receiver. The README lists histogram support for Datadog, Telegraf and the OpenTelemetry Collector, not classic StatsD.
Wrap a promise-returning function with a timertime-async-function
const timedFetch = metrics.asyncTimer(
async (url, metricContext) => {
const response = await fetch(url);
metricContext.addTags({ status: response.status });
return response.json();
},
'upstream.duration',
);
const data = await timedFetch('https://api.example.com/data');The wrapper appends a metrics context argument to your function. Functions that depend on an exact argument count or use the final argument position need adjustment.
Add subsystem tags with a child clientcreate-child-client
const paymentsMetrics = metrics.childClient({
prefix: 'payments',
globalTags: { component: 'payments' },
});
paymentsMetrics.increment('attempts');Child clients share the parent transport and aggregator. Their distinct global tags also create distinct aggregation contexts.
Sample a high-volume countersample-counter
metrics.increment('cache.lookup', 1, {
sampleRate: 0.1,
tags: { result: 'hit' },
});Sampling is probabilistic and the daemon compensates the count. Do not sample gauges, rare events or values where every individual observation matters.
Aggregate hot metrics before sendingenable-aggregation
const metrics = new StatsD({
aggregation: {
flushInterval: 2000,
maxContexts: 5000,
},
errorHandler: console.error,
});Only counts, gauges and sets are combined. Timings, histograms, distributions, events, checks, timestamped metrics and per-call sampling bypass aggregation.
Send DogStatsD over a Unix socketuse-datadog-socket
const metrics = new StatsD({
datadog: true,
protocol: 'uds',
path: '/var/run/datadog/dsd.socket',
udsRetryOptions: { retries: 3, retryDelayMs: 100 },
errorHandler: console.error,
});UDS relies on the optional unix-dgram native dependency, does not work on Windows and needs the agent socket mounted into the process container.
Use Telegraf's StatsD line protocolconfigure-telegraf
const metrics = new StatsD({
host: 'telegraf.monitoring.svc',
port: 8125,
telegraf: true,
globalTags: { service: 'checkout' },
});Telegraf mode changes tag formatting and disables Datadog mode. Datadog-only distributions, events, service checks and timestamps do not carry over.
Emit a Datadog event and service checksend-datadog-event
metrics.event('Deployment finished', 'checkout-api 2.4.0', {
alert_type: 'success',
aggregation_key: 'checkout-deploy',
}, ['env:production']);
metrics.check('checkout.health', metrics.CHECKS.OK, {
message: 'ready',
});event and check are DogStatsD extensions. They are not portable to classic StatsD, Telegraf or the OpenTelemetry StatsD receiver.
Capture metrics without opening a sockettest-with-mock
const metrics = new StatsD({ mock: true });
metrics.increment('jobs.completed', { queue: 'email' });
expect(metrics.mockBuffer).toContain('jobs.completed:1|c|#queue:email');
metrics.mockBuffer.length = 0;mockBuffer grows without a limit. Use mock mode only in tests and clear the array between cases.
Flush and close during graceful shutdownclose-on-sigterm
process.once('SIGTERM', () => {
metrics.increment('app.shutdown', ['signal:SIGTERM']);
metrics.close((error) => {
if (error) console.error('Metrics close failed', error);
process.exit(error ? 1 : 0);
});
});Do not send from process exit or uncaughtExceptionMonitor handlers; Node cannot complete asynchronous socket work after the event loop has stopped.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @opentelemetry/sdk-metrics | npm | Choose it for vendor-neutral metrics with resource attributes, views and exporters alongside OpenTelemetry traces |
| prom-client | npm | Choose it when Prometheus will scrape your Node process and pull-based collection fits the deployment model |
| datadog-metrics | npm | Choose it for a Datadog-only process that must submit metrics through the HTTP API rather than a local DogStatsD agent |