envoyproxy / envoyproxy/ratelimit
DogStatsD sink appends STATSD_PORT to unix:// addresses, breaking Unix Domain Socket support
- Dominant language
- Go
- Stars
- 2.7k
- Forks
- 535
- PR merge metrics
- No merged PRs in 30d
Description
## 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`](https://github.com/envoyproxy/ratelimit/blob/main/src/godogstats/dogstatsd_sink.go), `NewSink()` builds the address as:
```go
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`](https://github.com/envoyproxy/ratelimit/blob/main/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:
```go
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
- `envoyproxy/ratelimit` built from a recent `main` (confirmed present as of commit range including #625/#627, June 2024, and still present in `main` as of August 2026)
- Deployed on Kubernetes/AKS with `USE_DOG_STATSD=true`, `STATSD_HOST=unix:///var/run/datadog/dsd.socket` mounted via a `hostPath` volume shared with the Datadog node agent's DogStatsD Unix Domain Socket
Contributor guide
Research direction
Read NewSink in src/godogstats/dogstatsd_sink.go and review StatsdPort in src/settings/settings.go first. Check how the underlying datadog-go client expects unix://, unixgram://, and unixstream:// addresses. Done means socket addresses reach statsd.New unchanged while hostnames still receive the configured port.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- observability-sre
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100