Skip to content

Commit

Permalink
Adding support for DD_DOGSTATSD_HOST env var
Browse files Browse the repository at this point in the history
DD_DOGSTATSD_HOST takes precedence over DD_AGENT_HOST but works in the
exact same way. This is useful when running multiple DogStatsD clients
on the same host where each of them use a different UDS socket.

For example the Trace agent might have its own UDS socket,
"/var/run/datadog/apm.socket", while the Agent is listening on
"/var/run/datadog/dsd.socket" for DogStatsD traffic. Setting
"DD_AGENT_HOST" env var would set the same socket for both app. The
dedicated "DD_DOGSTATSD_HOST" now allow users to set a socket/host for
DogStatsD only.
  • Loading branch information
hush-hush committed Oct 5, 2021
1 parent a5c74c8 commit 8634aae
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 17 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,16 @@ Find a list of all the available options for your DogStatsD Client in the [Datad

### Supported environment variables

* If the `addr` parameter is empty, the client uses the `DD_AGENT_HOST` environment variables to build a target address.
Example: `DD_AGENT_HOST=127.0.0.1:8125` for UDP, `DD_AGENT_HOST=unix:///path/to/socket` for UDS and `DD_AGENT_HOST=\\.\pipe\my_windows_pipe` for Windows
* If the `DD_ENTITY_ID` environment variable is found, its value is injected as a global `dd.internal.entity_id` tag. The Datadog Agent uses this tag to insert container tags into the metrics. To avoid overwriting this global tag, only `append` to the `c.Tags` slice.
* If the `addr` parameter is empty, the client uses the `DD_DOGSTATSD_HOST` and then `DD_AGENT_HOST` environment
variables to build a target address. `DD_DOGSTATSD_HOST` takes precedence over `DD_AGENT_HOST`, but both use the same
format.
Example: `DD_AGENT_HOST=127.0.0.1:8125` for UDP, `DD_AGENT_HOST=unix:///path/to/socket` for UDS and
`DD_AGENT_HOST=\\.\pipe\my_windows_pipe` for named pipes
* `DD_DOGSTATSD_PORT` can be use to set the UDP port. If `DD_DOGSTATSD_HOST` or `DD_AGENT_HOST` already contains a port
then `DD_DOGSTATSD_PORT` is ignored.
Example: `DD_AGENT_HOST=127.0.0.1` with `DD_DOGSTATSD_PORT=8125`.
* If the `DD_ENTITY_ID` environment variable is found, its value is injected as a global `dd.internal.entity_id` tag.
The Datadog Agent uses this tag to insert container tags into the metrics.

To enable origin detection and set the `DD_ENTITY_ID` environment variable, add the following lines to your application manifest:

Expand Down
15 changes: 10 additions & 5 deletions statsd/statsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@ traffic instead of UDP.
const WindowsPipeAddressPrefix = `\\.\pipe\`

const (
agentHostEnvVarName = "DD_AGENT_HOST"
agentPortEnvVarName = "DD_DOGSTATSD_PORT"
defaultUDPPort = "8125"
agentHostEnvVarName = "DD_AGENT_HOST"
dogstatsdHostEnvVarName = "DD_DOGSTATSD_HOST"
dogstatsdPortEnvVarName = "DD_DOGSTATSD_PORT"
defaultUDPPort = "8125"
)

/*
Expand Down Expand Up @@ -230,8 +231,12 @@ var _ ClientInterface = &Client{}
func resolveAddr(addr string) string {
envPort := ""
if addr == "" {
addr = os.Getenv(agentHostEnvVarName)
envPort = os.Getenv(agentPortEnvVarName)
if dsdHost := os.Getenv(dogstatsdHostEnvVarName); dsdHost != "" {
addr = dsdHost
} else {
addr = os.Getenv(agentHostEnvVarName)
}
envPort = os.Getenv(dogstatsdPortEnvVarName)
}

if addr == "" {
Expand Down
100 changes: 96 additions & 4 deletions statsd/statsd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,11 @@ func TestResolveAddressFromEnvironment(t *testing.T) {
} else {
defer os.Unsetenv(agentHostEnvVarName)
}
portInitialValue, portInitiallySet := os.LookupEnv(agentPortEnvVarName)
portInitialValue, portInitiallySet := os.LookupEnv(dogstatsdPortEnvVarName)
if portInitiallySet {
defer os.Setenv(agentPortEnvVarName, portInitialValue)
defer os.Setenv(dogstatsdPortEnvVarName, portInitialValue)
} else {
defer os.Unsetenv(agentPortEnvVarName)
defer os.Unsetenv(dogstatsdPortEnvVarName)
}

for _, tc := range []struct {
Expand Down Expand Up @@ -224,7 +224,7 @@ func TestResolveAddressFromEnvironment(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
os.Setenv(agentHostEnvVarName, tc.hostEnv)
os.Setenv(agentPortEnvVarName, tc.portEnv)
os.Setenv(dogstatsdPortEnvVarName, tc.portEnv)

addr := resolveAddr(tc.addrParam)
assert.Equal(t, tc.expectedAddr, addr)
Expand Down Expand Up @@ -269,3 +269,95 @@ func TestGetTelemetry(t *testing.T) {
assert.Equal(t, uint64(1), tlm.AggregationNbContextDistribution, "telmetry AggregationNbContextDistribution was wrong")
assert.Equal(t, uint64(2), tlm.AggregationNbContextTiming, "telmetry AggregationNbContextTiming was wrong")
}

func TestEnvAgentHost(t *testing.T) {
defer func() { os.Unsetenv("DD_AGENT_HOST") }()

os.Setenv("DD_AGENT_HOST", "localhost:8765")

ts, client := newClientAndTestServerCustomAddr(t,
"udp",
"",
"localhost:8765",
nil,
)

ts.sendAllAndAssert(t, client)
}

func TestEnvAgentHostAndPort(t *testing.T) {
defer func() { os.Unsetenv("DD_AGENT_HOST") }()
defer func() { os.Unsetenv("DD_DOGSTATSD_PORT") }()

// Check that DD_AGENT_PORT is ignored when DD_AGENT_HOST already contains a port
os.Setenv("DD_AGENT_HOST", "localhost:8765")
os.Setenv("DD_DOGSTATSD_PORT", "1234")

ts, client := newClientAndTestServerCustomAddr(t,
"udp",
"",
"localhost:8765",
nil,
)

ts.sendAllAndAssert(t, client)

// Check that DD_DOGSTATSD_PORT is used
os.Setenv("DD_AGENT_HOST", "localhost")
os.Setenv("DD_DOGSTATSD_PORT", "8766")

ts, client = newClientAndTestServerCustomAddr(t,
"udp",
"",
"localhost:8766",
nil,
)

ts.sendAllAndAssert(t, client)
}

func TestEnvDogstatsddHost(t *testing.T) {
defer func() { os.Unsetenv("DD_DOGSTATSD_HOST") }()

os.Setenv("DD_DOGSTATSD_HOST", "localhost:8765")

ts, client := newClientAndTestServerCustomAddr(t,
"udp",
"",
"localhost:8765",
nil,
)

ts.sendAllAndAssert(t, client)
}

func TestEnvDogstatsddHostAndPort(t *testing.T) {
defer func() { os.Unsetenv("DD_DOGSTATSD_HOST") }()
defer func() { os.Unsetenv("DD_DOGSTATSD_PORT") }()

// Check that DD_DOGSTATSD_PORT is ignored when DD_AGENT_HOST already contains a port
os.Setenv("DD_DOGSTATSD_HOST", "localhost:8765")
os.Setenv("DD_DOGSTATSD_PORT", "1234")

ts, client := newClientAndTestServerCustomAddr(t,
"udp",
"",
"localhost:8765",
nil,
)

ts.sendAllAndAssert(t, client)

// Check that DD_DOGSTATSD_PORT is used
os.Setenv("DD_DOGSTATSD_HOST", "localhost")
os.Setenv("DD_DOGSTATSD_PORT", "8766")

ts, client = newClientAndTestServerCustomAddr(t,
"udp",
"",
"localhost:8766",
nil,
)

ts.sendAllAndAssert(t, client)
}
11 changes: 6 additions & 5 deletions statsd/test_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ type testServer struct {
errors []string
readData []string
proto string
addr string
stopped chan struct{}
tags string
namespace string
Expand All @@ -66,14 +65,16 @@ type testServer struct {
}

func newClientAndTestServer(t *testing.T, proto string, addr string, tags []string, options ...Option) (*testServer, *Client) {
return newClientAndTestServerCustomAddr(t, proto, addr, addr, tags, options...)
}

func newClientAndTestServerCustomAddr(t *testing.T, proto string, addrClient string, addrTest string, tags []string, options ...Option) (*testServer, *Client) {
opt, err := resolveOptions(options)
require.NoError(t, err)

ts := &testServer{
proto: proto,
data: []string{},
addr: addr,
stopped: make(chan struct{}),
aggregation: opt.aggregation,
extendedAggregation: opt.extendedAggregation,
Expand All @@ -88,14 +89,14 @@ func newClientAndTestServer(t *testing.T, proto string, addr string, tags []stri

switch proto {
case "udp":
udpAddr, err := net.ResolveUDPAddr("udp", addr)
udpAddr, err := net.ResolveUDPAddr("udp", addrTest)
require.NoError(t, err)

conn, err := net.ListenUDP("udp", udpAddr)
require.NoError(t, err)
ts.conn = conn
case "uds":
socketPath := addr[7:]
socketPath := addrTest[7:]
address, err := net.ResolveUnixAddr("unixgram", socketPath)
require.NoError(t, err)
conn, err := net.ListenUnixgram("unixgram", address)
Expand All @@ -107,7 +108,7 @@ func newClientAndTestServer(t *testing.T, proto string, addr string, tags []stri
require.FailNow(t, "unknown proto '%s'", proto)
}

client, err := New(addr, options...)
client, err := New(addrClient, options...)
require.NoError(t, err)

go ts.start()
Expand Down

0 comments on commit 8634aae

Please sign in to comment.