Skip to content

Add DogStatsD metrics exporter alongside Graphite#11

Merged
jdevera merged 4 commits into
mainfrom
statsd-exporter
May 21, 2026
Merged

Add DogStatsD metrics exporter alongside Graphite#11
jdevera merged 4 commits into
mainfrom
statsd-exporter

Conversation

@jdevera
Copy link
Copy Markdown
Owner

@jdevera jdevera commented May 21, 2026

Summary

Adds a third metrics exporter — DogStatsD over UDP — that runs alongside the existing Graphite and extensible exporters. Strictly additive: Graphite stays untouched, no existing config keys change, no new dependencies.

Why DogStatsD specifically: the dimension-as-tag model means receivers (Prometheus via statsd_exporter, the Datadog agent, OTel collector, Telegraf, Vector, or a hand-rolled receiver) can slice on any combination of dimensions without sender-side templating. The same on-wire packets work everywhere — no need to decide at code-write time which dimensions belong in a prefix.

Commits

What
fc793fe cmd/metrics/statsd.go + tests. Two packets per invocation (<prefix>duration|ms, <prefix>count|c) with tags launcher, repo, pkg, cmd, subcmd, partition, status. Off by default — an empty host returns a no-op collector that never opens a socket. Stdlib net only, no external dep. Tests cover format generation, tag sanitisation, end-to-end UDP loopback, and exit-code → status mapping.
a7fdb93 Three new config keys: METRIC_STATSD_HOST (empty default — disables), METRIC_STATSD_PORT (8125 default), METRIC_STATSD_PREFIX ("launcher." default). Adds a tiny setIntConfig helper for the port.
1bdcfde One-line wiring in cmd/root.go to add the StatsD collector to the existing composite alongside Graphite + extensible.
8d92670 gofmt cleanup in the statsd files (whitespace only).

What changes on the wire

Nothing for existing users. Graphite still emits the same dotted-prefix metrics on its existing UDP port. Setting METRIC_STATSD_HOST to a real host activates the new exporter in parallel.

What gets collected (in case anyone asks)

Same shape as Graphite: command identity (repo, pkg, cmd, subcmd), partition, duration, count, success/failure. Plus the launcher's own name as a tag (Graphite hardcoded \"cdt\" in the prefix; we put it in a tag). No arguments, no env vars, no stdout, no user identity.

End-to-end verification

Built fresh from this branch, ran against prom/statsd-exporter in Docker. Sample output after one cola bonjour and one cola exit1:

launcher_count{cmd=\"default\",launcher=\"cola\",partition=\"0\",pkg=\"bonjour\",repo=\"dropin\",status=\"ok\",subcmd=\"bonjour\"} 1
launcher_count{cmd=\"default\",launcher=\"cola\",partition=\"0\",pkg=\"exit-code-test\",repo=\"dropin\",status=\"ko\",subcmd=\"exit1\"} 1
launcher_duration{...status=\"ok\",quantile=\"0.5\"} 0.611

All seven tags came through, both metric types parsed cleanly by statsd_exporter with no custom mapping rules.

Test plan

  • go build ./... clean
  • go vet ./... clean
  • go test -race -count=1 ./... all packages pass
  • ./test/integration.sh 19/19 suites pass
  • Cross-platform build matrix (linux/darwin/windows × amd64/arm64) all OK
  • govulncheck ./... no vulnerabilities
  • gh-pages markdown lint passes
  • End-to-end against `prom/statsd-exporter` in Docker — verified above
  • Fork CI green across the matrix

jdevera added 4 commits May 19, 2026 23:53
Implements the Metrics interface alongside graphiteMetrics and
extensibleMetrics. Emits two packets per command invocation over
UDP:

  <prefix>duration:<ms>|ms|#launcher=,repo=,pkg=,cmd=,subcmd=,partition=,status=
  <prefix>count:1|c|#<same tags>

Dimensions live in DogStatsD tags rather than the metric name, so
the same emit code reaches the Datadog agent, prometheus
statsd_exporter, the OTel collector, Telegraf, Vector, or a custom
receiver — the receiver decides how to roll up.

Off by default: an empty host returns a no-op collector that never
opens a socket. Mirrors how Graphite is wired in today.

No external dep. Single net.Dial("udp", ...) per Send; UDP
fire-and-forget so the CLI doesn't block on a downed receiver.

Tests cover format generation, tag sanitisation, end-to-end UDP
loopback, and the exit-code → status mapping. Wired into the
composite collector in a follow-up commit so this lands as a pure
addition.
Three new keys, all opt-in:

  METRIC_STATSD_HOST   ""        — set to enable; "" leaves the exporter disabled
  METRIC_STATSD_PORT   8125      — de-facto StatsD/DogStatsD port
  METRIC_STATSD_PREFIX "launcher." — namespace prefix for emitted metric names

Adds a small setIntConfig helper for the port; uses
existing string/duration helpers for the rest.

Same disabled-by-default posture as Graphite — both exporters can
be set independently and either, both, or neither will emit.
Add the StatsD collector alongside Graphite + extensible in the
composite. Passes the launcher's runtime app name as the launcher
tag so the exporter doesn't need to reach into the context itself.

No behaviour change at default config: METRIC_STATSD_HOST is empty
by default, so the constructor returns a no-op. Setting the host
activates the exporter without touching Graphite — both run side by
side, emitting to whatever receivers you point them at.
Tightens whitespace in two places gofmt flagged after the initial
commits: a double-space in the noopMetrics.Send signature, and
struct-field column alignment in TestStatsd_StatusReflectsExitCode.
No behaviour change.
@jdevera jdevera merged commit e0dfc3f into main May 21, 2026
16 checks passed
@jdevera jdevera deleted the statsd-exporter branch May 21, 2026 23:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant