Skip to content

DogStatsD sink appends STATSD_PORT to unix:// addresses, breaking Unix Domain Socket support #1223

Description

@olemartin

Description

The DogStatsD sink (USE_DOG_STATSD=true) does not support Unix Domain Socket addresses for STATSD_HOST. When STATSD_HOST is set to a unix:// address (as documented/expected by the underlying DataDog/datadog-go client), the sink unconditionally appends STATSD_PORT to it, producing an invalid address that never resolves to the actual socket file. All stats are silently dropped — no error is logged anywhere.

Root cause

In src/godogstats/dogstatsd_sink.go, NewSink() builds the address as:

client, err := statsd.New(sink.config.host+":"+strconv.Itoa(sink.config.port), statsd.WithoutClientSideAggregation())

This always concatenates host + ":" + port, with no check for whether host is already a complete unix://, unixgram://, or unixstream:// address (all of which are documented, supported formats for datadog-go's statsd.New).

StatsdPort (STATSD_PORT) also always defaults to 8125 (src/settings/settings.go), so even if a user doesn't set STATSD_PORT at all, it's still appended.

Example: with

STATSD_HOST=unix:///var/run/datadog/dsd.socket

(and STATSD_PORT left at its default), the resulting address passed to statsd.New is:

unix:///var/run/datadog/dsd.socket:8125

datadog-go's resolveAddr/createWriter (in statsdex.go) recognize the unix:// prefix and pass the rest of the string verbatim as the socket file path — so the writer ends up trying to connect to a socket literally named /var/run/datadog/dsd.socket:8125, which does not exist (the real file is /var/run/datadog/dsd.socket, without a port suffix).

Because datadog-go's UDS writer defers the actual connect() until the first Write() call (see uds.go, "Defer connection to first Write"), NewSink() returns no error and the service logs "Stats initialized for dogstatsd" successfully at startup. Every subsequent Write() then fails with ENOENT/"no such file or directory", but FlushCounter/FlushGauge/FlushTimer in dogstatsd_sink.go discard the error returned by client.Count()/client.Gauge()/client.Timing(), so the failure is completely silent — no logs, no metrics, no crash, no indication anything is wrong.

Expected behavior

Setting STATSD_HOST to a unix://, unixgram://, or unixstream:// address should work for DogStatsD over a Unix Domain Socket, without requiring/appending a port — matching how datadog-go's own statsd.New documents its addr parameter:

New returns a pointer to a new Client given an addr in the format "hostname:port" for UDP, "unix:///path/to/socket" for UDS or "\.\pipe\path\to\pipe" for Windows Named Pipes.

Actual behavior

STATSD_PORT is always appended to STATSD_HOST, corrupting any Unix Domain Socket address and causing all metrics to be silently dropped.

Suggested fix

In NewSink(), only append :port when host does not already look like a complete address (e.g. doesn't have a unix:///unixgram:///unixstream:// prefix or a Windows named pipe prefix), similar to how datadog-go's own resolveAddr decides whether to append a port:

addr := sink.config.host
if !hasSchemePrefix(addr) { // unix://, unixgram://, unixstream://, \\.\pipe\
    addr = addr + ":" + strconv.Itoa(sink.config.port)
}
client, err := statsd.New(addr, statsd.WithoutClientSideAggregation())

Environment

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions