Skip to main content
StatsD is the metrics provider: it emits counters, gauges, timings, and histograms to any StatsD-compatible backend (Datadog, Telegraf, Graphite) and ships a mock: true flag that silently no-ops all calls in dev and test.

The MetricsType interface

Every metrics provider satisfies the same contract:
type MetricsType = {
  increment: (stat: string, value?: number, tags?: Record<string, string>) => void;
  decrement: (stat: string, value?: number, tags?: Record<string, string>) => void;
  gauge:     (stat: string, value: number,  tags?: Record<string, string>) => void;
  timing:    (stat: string, value: number,  tags?: Record<string, string>) => void;
  histogram: (stat: string, value: number,  tags?: Record<string, string>) => void;
};
Keep tag values bounded and low-cardinality — never use user IDs, emails, or timestamps as tag values:
// ✅ Good — low-cardinality tag
tools.metrics.increment('subscription.created', 1, { plan: subscription.plan });

// ❌ Bad — high-cardinality tag
tools.metrics.increment('subscription.created', 1, { userId: user.id });

Why

StatsD is a widely supported, UDP-based metrics protocol with near-zero overhead. The hot-shots client supports Datadog DogStatsD extensions (tags, histograms) and runs equally well against a local Telegraf agent or a cloud service like Datadog. The mock: true option means you don’t need a StatsD server running in dev at all.

Setup

  1. In apps/backend/package.json, ensure "tooling-metrics-statsd": "workspace:*" is listed under dependencies.
  2. Run make pnpm-lockfile-update then make pnpm-install.
  3. In development.ts and test.ts, use mock mode:
    metrics: {
      client: MetricsClientType.STATSD,
      options: { mock: true },
    }
    
  4. In production.ts, point at your StatsD agent:
    metrics: {
      client: MetricsClientType.STATSD,
      options: {
        host: process.env.STATSD_HOST,
        port: Number(process.env.STATSD_PORT ?? 8125),
        globalTags: { env: 'production', service: 'backend' },
      },
    }
    
  5. In your production environment, set:
    STATSD_HOST=your-statsd-agent-host
    STATSD_PORT=8125
    
  6. Run make test module=tooling-metrics-statsd and make test module=backend.

Emitting metrics

// Counter — how many times something happened
tools.metrics.increment('webhook.received', 1, { provider: 'stripe' });

// Gauge — current state
tools.metrics.gauge('queue.depth', queueDepth, { queue: 'email' });

// Timing — how long something took (ms)
tools.metrics.timing('subscription.upgrade.duration', durationMs, { plan: newPlan });

// Histogram — distribution of a value
tools.metrics.histogram('invoice.amount', amountCents, { currency: 'usd' });

Gotchas

  • StatsD uses UDP — metric sends are fire-and-forget and will not throw on failure. Errors are routed to tools.logger.error via the errorHandler in buildStatsdClient.
  • globalTags are merged into every metric. Keep them low-cardinality (env, service) — never put per-request data in global tags.
  • mock: true must be removed in production. If you accidentally leave it on, all metrics are silently swallowed — your dashboards will appear empty.
  • Never omit host and port in production. hot-shots defaults to localhost:8125, which silently drops metrics if no agent is listening there.

What’s next?