@autofleet/metrics
Metric transports for Autofleet microservices.
// In your service: lib/metrics.ts (or wherever you wire singletons)
const { createMetricsClient } = require('@autofleet/metrics');
const logger = require('../logger');
const dogstatsd = createMetricsClient({ logger });
module.exports = { dogstatsd };
// At emit sites
const { dogstatsd } = require('./lib/metrics');
dogstatsd.increment('vehicle.data.locations.writes', 1, { source: 'motorq' });
dogstatsd.gauge('vehicle.data.events.has_recent_data', 1);No app-side setup needed in Autofleet pods — the Datadog admission webhook injects dd-trace and the package resolves it eagerly when createMetricsClient() runs. Outside the cluster, see Using outside the cluster.
Where this package works
| Context | Behaviour |
|---|---|
In-cluster pod with global.datadogEnabled: true |
Full functionality, no app-side config. Metrics flow over UDS to the node-local DD Agent; env / service / version tags are auto-applied. See How dd-trace gets into the pod for the mechanism. |
| Local dev, CI, unit tests | Safe by default. If dd-trace isn't loadable → the package returns its own no-op stub. If dd-trace IS loadable but never had tracer.init() called (e.g., workspaces with autoInstallPeers: true) → dd-trace's own NoopDogStatsDClient takes over, calls succeed silently, no metrics get emitted. Either way: no errors, nothing leaks. |
| Other contexts (non-k8s pods, Lambdas, CLI scripts) | Works if you set up dd-trace yourself. See Using outside the cluster. |
Boot log caveat: instance loaded means tracer.dogstatsd exists on the loaded module — not that the agent is reachable or tracer.init() ran. If metrics aren't flowing despite instance loaded, verify tracer.init() was called and DD_AGENT_HOST / DD_DOGSTATSD_URL are set.
Installation
Inside autorepo (other workspace packages):
pnpm add @autofleet/metrics --filter @autofleet/<service-name>Outside autorepo (standalone service repos: vehicle-ms, vld-ms, vehicle-search-ms, etc.):
npm install @autofleet/metricsFor beta versions:
npm install @autofleet/metrics@betaPublic API
| Export | Purpose |
|---|---|
createMetricsClient(options?) |
Resolves the dd-trace dogstatsd client (or a no-op stub), emits a one-time boot log, returns the client. Call once per app, typically in lib/metrics.ts. |
MetricsClient (type) |
The shape returned — the stable subset of dd-trace's tracer.DogStatsD interface (see Supported methods). |
MetricsClientOptions (type) |
{ logger?, debug? } — see Boot logging. |
MetricsLogger (type) |
The logger shape any transport's logger option accepts: { info, warn }. @autofleet/logger satisfies it as-is. |
Underscore-prefixed names (_tryLoad, _logBootSummary, _LoadResult) are exported only for the package's own tests. Don't import them.
Recommended call-site pattern
createMetricsClient() returns the raw tracer.dogstatsd surface — call sites using it directly are coupled to dd-trace's API shape. To keep call sites clean and metric names + tags consistent, hold the client in a service-local module and define typed wrapper functions on top:
// In your service: lib/metrics.ts
import { createMetricsClient } from '@autofleet/metrics';
import logger from '../logger';
const dogstatsd = createMetricsClient({ logger });
const LOCATIONS_WRITES = 'vehicle.data.locations.writes';
export function recordLocationWrite(source?: string): void {
dogstatsd.increment(LOCATIONS_WRITES, 1, { source: source ?? 'unknown' });
}Then call recordLocationWrite(source) at emit sites. This keeps metric names + tag shapes in one place per service, and lets you swap the transport (e.g. add an HTTP client for non-pod contexts) without touching call sites.
Boot logging (optional but recommended)
createMetricsClient() emits one boot log when called: info if dd-trace's dogstatsd instance loaded, warn if it fell back to no-op. To surface it, pass a logger:
const { createMetricsClient } = require('@autofleet/metrics');
const logger = require('../logger');
const dogstatsd = createMetricsClient({
logger, // any { info, warn } pair; @autofleet/logger works as-is
debug: process.env.LOG_LEVEL === 'debug',
});| Option | Type | Effect |
|---|---|---|
logger |
{ info(msg, meta?), warn(msg, meta?) } |
Receives the boot log. Omitting it leaves the package silent (no console output). |
debug |
boolean (default false) |
When true, the boot log's meta includes DD_AGENT_HOST, DD_DOGSTATSD_URL, DD_ENV, DD_SERVICE, DD_VERSION — useful for triaging the no-op fallback (a missing DD_DOGSTATSD_URL points at admission webhook misconfiguration). |
Each call fires its own boot log. Calling createMetricsClient() more than once in a process is fine but uncommon — typical pattern is one call per app, held in lib/metrics.ts.
Using outside the cluster
If you're not running in an Autofleet pod, provide what the admission webhook would have, by hand:
npm install dd-trace// At the very top of your entry file, before any other require:
const tracer = require('dd-trace').init({
service: 'my-service',
env: process.env.NODE_ENV,
});Set DD_AGENT_HOST (or DD_DOGSTATSD_URL) so the tracer can reach an agent. From there, createMetricsClient() works the same way as in-cluster. See element-adapter/src/datadog/index.ts for a working example.
Useful commands
For working on this package inside autorepo:
pnpm nx test metrics
pnpm nx build metrics
pnpm nx lint metrics
pnpm nx typecheck metricsTechnical details
How dd-trace gets into the pod
The Datadog Cluster Agent's admission webhook mutates eligible pods at admission time and ships a pre-initialised dd-trace with them. Useful to know when debugging "why did my pod land on the no-op fallback."
Trigger. Pods labelled admission.datadoghq.com/enabled: "true" get mutated. The base microservice chart sets that label when global.datadogEnabled is true (base-microservice/templates/deployment.yaml).
What gets added to the pod:
- Two init containers copy
dd-trace(currentlyv5.24.0) into/opt/datadog/apm/library/jsand install anLD_PRELOADshim that pre-loads it at Node startup. - App container env vars:
DD_SERVICE/DD_ENV/DD_VERSION(fromtags.datadoghq.com/*labels for Unified Service Tagging),DD_AGENT_HOST(fromstatus.hostIP),DD_DOGSTATSD_URL=unix:///var/run/datadog/dsd.socket,DD_TRACE_AGENT_URL=unix:///var/run/datadog/apm.socket, plusLD_PRELOAD. - Hostpath mount of
/var/run/datadogso the app reaches the node-local DD Agent over Unix sockets.
By the time application code runs, require('dd-trace') returns an already-initialised tracer — we never call tracer.init(). APM tracing, runtime metrics, log correlation, and tracer.dogstatsd all work with no app-side config.
This package's loader. Tries require('dd-trace') first (matches the LD_PRELOAD'd instance or a consumer's own install), then /opt/datadog/apm/library/js/node_modules/dd-trace as a fallback. Both fail → no-op fallback. If a pod unexpectedly lands on the no-op fallback, check that admission.datadoghq.com/enabled: "true" is set and the DD_* env vars made it in (kubectl describe pod ...).
Supported methods
tracer.dogstatsd exposes a subset of the DogStatsD protocol. The package's MetricsClient exposes the stable subset — see dd-trace's DogStatsD interface for the source of truth.
| Method | dd-trace | This package |
|---|---|---|
increment / decrement |
✓ | ✓ |
gauge |
✓ | ✓ |
distribution |
✓ | ✓ |
histogram |
✓ | ✓ |
flush |
✓ (beta) | ✗ — omitted; dd-trace marks it experimental |
set / timing / event / check |
✗ | ✗ — not in tracer.dogstatsd |
distribution vs histogram: distributions aggregate server-side (DDSketch) so percentiles are globally accurate across hosts; histograms aggregate client-side per Agent. Prefer distribution when you need cross-host percentile accuracy or percentile-based alerts. Both are valid — DD doesn't deprecate either. See Datadog's distributions docs.
@autofleet/metrics vs hot-shots vs the DD HTTP API
| This package (dd-trace) | hot-shots | DD HTTP API | |
|---|---|---|---|
| New dependency | none (admission-injected) | yes | none |
| Transport | UDS (auto from DD_DOGSTATSD_URL) |
UDP (default) | HTTPS |
env/service/version tagging |
automatic | manual globalTags |
manual |
| Methods | increment / decrement / gauge / distribution / histogram |
+ set / timing / event / check |
full metrics submit API |
| Best for | in-cluster Autofleet pods, or anywhere dd-trace is already initialised |
needs set / timing / event / check |
outside k8s / CI / one-shot scripts |
Takeaway: if you're in a pod, use this package. If you need a method dd-trace doesn't expose, reach for hot-shots. If dd-trace isn't an option (Lambda, CLI, external worker), use the HTTP API — or file an issue so we can build an HTTP transport here.