Skip to content

Commit

Permalink
Add support for DD_DOGSTATSD_HOST
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 Datadog clients on
the same host where each of them use a different UDS socket for example
(ex: running the trace client and the dogstatsd client).
  • Loading branch information
hush-hush committed Jul 30, 2021
1 parent 499f2da commit 5a2ec53
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 11 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,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.
* 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 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.
* `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 avoid overwriting this global tag, only
`append` to the `c.Tags` slice.

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

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

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

/*
Expand Down Expand Up @@ -240,7 +241,11 @@ var _ ClientInterface = &Client{}
func resolveAddr(addr string) string {
envPort := ""
if addr == "" {
addr = os.Getenv(agentHostEnvVarName)
if dsdHost := os.Getenv(agentDsdHostEnvVarName); dsdHost != "" {
addr = dsdHost
} else {
addr = os.Getenv(agentHostEnvVarName)
}
envPort = os.Getenv(agentPortEnvVarName)
}

Expand Down
98 changes: 98 additions & 0 deletions statsd/statsd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,3 +419,101 @@ func TestEnvTagsEmptyString(t *testing.T) {
assert.Len(t, client.Tags, 0)
ts.sendAllAndAssert(t, client)
}

func TestDdAgentHost(t *testing.T) {
defer func() { os.Unsetenv(agentHostEnvVarName) }()

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

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

assert.Len(t, client.Tags, 0)
ts.sendAllAndAssert(t, client)
}

func TestDdAgentHostAndPort(t *testing.T) {
defer func() { os.Unsetenv(agentHostEnvVarName) }()
defer func() { os.Unsetenv(agentPortEnvVarName) }()

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

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

assert.Len(t, client.Tags, 0)
ts.sendAllAndAssert(t, client)

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

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

assert.Len(t, client.Tags, 0)
ts.sendAllAndAssert(t, client)
}

func TestDdDsdHost(t *testing.T) {
defer func() { os.Unsetenv(agentDsdHostEnvVarName) }()

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

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

assert.Len(t, client.Tags, 0)
ts.sendAllAndAssert(t, client)
}

func TestDdDsdHostAndPort(t *testing.T) {
defer func() { os.Unsetenv(agentDsdHostEnvVarName) }()
defer func() { os.Unsetenv(agentPortEnvVarName) }()

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

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

assert.Len(t, client.Tags, 0)
ts.sendAllAndAssert(t, client)

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

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

assert.Len(t, client.Tags, 0)
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 @@ -52,7 +52,6 @@ type testServer struct {
data []string
errors []string
proto string
addr string
stopped chan struct{}
tags string
namespace string
Expand All @@ -65,14 +64,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{}),
devMode: opt.DevMode,
aggregation: opt.Aggregation,
Expand All @@ -88,21 +89,21 @@ 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":
conn, err := net.Dial("unix", addr[:7]) // we remove the 'unix://' prefix
conn, err := net.Dial("unix", addrTest[:7]) // we remove the 'unix://' prefix
require.NoError(t, err)
ts.conn = conn
default:
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 5a2ec53

Please sign in to comment.