Skip to content

Commit 60eea4b

Browse files
author
Remi Hakim
committed
[dogstatsd] Allow to dynamically use default route as a statsd host
When you run in a containerized environment, the dogstatsd server will likely expose its port on the host network. If you have an application that runs within a container, it needs to connect to it using the default route.
1 parent 55bf806 commit 60eea4b

File tree

3 files changed

+44
-3
lines changed

3 files changed

+44
-3
lines changed

datadog/__init__.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636

3737
def initialize(api_key=None, app_key=None, host_name=None, api_host=None,
38-
statsd_host=None, statsd_port=None, **kwargs):
38+
statsd_host=None, statsd_port=None, statsd_use_default_route=False, **kwargs):
3939
"""
4040
Initialize and configure Datadog.api and Datadog.statsd modules
4141
@@ -57,6 +57,10 @@ def initialize(api_key=None, app_key=None, host_name=None, api_host=None,
5757
:param statsd_port: Port of DogStatsd server or statsd daemon
5858
:type statsd_port: port
5959
60+
:param statsd_use_default_route: Dynamically set the statsd host to the default route
61+
(Useful when running the client in a container)
62+
:type statsd_use_default_route: boolean
63+
6064
:param cacert: Path to local certificate file used to verify SSL \
6165
certificates. Can also be set to True (default) to use the systems \
6266
certificate store, or False to skip SSL verification
@@ -74,10 +78,15 @@ def initialize(api_key=None, app_key=None, host_name=None, api_host=None,
7478
os.environ.get('DATADOG_HOST', 'https://app.datadoghq.com')
7579

7680
# Statsd configuration -overrides default statsd instance attributes-
77-
if statsd_host and statsd_port:
81+
if statsd_host:
7882
statsd.host = statsd_host
83+
84+
if statsd_port:
7985
statsd.port = int(statsd_port)
8086

87+
if statsd_use_default_route:
88+
statsd.use_default_route = statsd_use_default_route
89+
8190
# HTTP client and API options
8291
for key, value in iteritems(kwargs):
8392
attribute = "_{0}".format(key)

datadog/dogstatsd/base.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from random import random
99
from time import time
1010
import socket
11+
import struct
1112
from functools import wraps
1213

1314
try:
@@ -23,7 +24,7 @@ class DogStatsd(object):
2324
OK, WARNING, CRITICAL, UNKNOWN = (0, 1, 2, 3)
2425

2526
def __init__(self, host='localhost', port=8125, max_buffer_size=50, namespace=None,
26-
constant_tags=None, use_ms=False):
27+
constant_tags=None, use_ms=False, use_default_route=False):
2728
"""
2829
Initialize a DogStatsd object.
2930
@@ -50,6 +51,11 @@ def __init__(self, host='localhost', port=8125, max_buffer_size=50, namespace=No
5051
5152
:envvar DATADOG_TAGS: Tags to attach to every metric reported by dogstatsd client
5253
:type constant_tags: list of strings
54+
55+
:param use_default_route: Dynamically set the statsd host to the default route
56+
(Useful when running the client in a container)
57+
:type use_default_route: boolean
58+
5359
"""
5460
self.host = host
5561
self.port = int(port)
@@ -63,6 +69,9 @@ def __init__(self, host='localhost', port=8125, max_buffer_size=50, namespace=No
6369
self.constant_tags = constant_tags + env_tags
6470
self.namespace = namespace
6571
self.use_ms = use_ms
72+
self.use_default_route = use_default_route
73+
if self.use_default_route:
74+
self.host = self._get_default_route()
6675

6776
def __enter__(self):
6877
self.open_buffer(self.max_buffer_size)
@@ -71,6 +80,18 @@ def __enter__(self):
7180
def __exit__(self, type, value, traceback):
7281
self.close_buffer()
7382

83+
def _get_default_route(self):
84+
try:
85+
with open('/proc/net/route') as f:
86+
for line in f.readlines():
87+
fields = line.strip().split()
88+
if fields[1] == '00000000':
89+
return socket.inet_ntoa(struct.pack('<L', int(fields[2], 16)))
90+
except IOError as e:
91+
log.error('Unable to open /proc/net/route: %s', e)
92+
93+
return None
94+
7495
def get_socket(self):
7596
"""
7697
Return a connected socket.
@@ -79,6 +100,9 @@ def get_socket(self):
79100
avoid bad thread race conditions.
80101
"""
81102
if not self.socket:
103+
if self.use_default_route:
104+
self.host = self._get_default_route()
105+
82106
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
83107
sock.connect((self.host, self.port))
84108
self.socket = sock

tests/unit/dogstatsd/test_statsd.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ def test_initialization(self):
6565
t.assert_equal(statsd.host, "myhost")
6666
t.assert_equal(statsd.port, 1234)
6767

68+
def test_default_route(self):
69+
options = {
70+
'statsd_use_default_route': True,
71+
}
72+
73+
initialize(**options)
74+
t.assert_equal(statsd.use_default_route, True)
75+
6876
def test_set(self):
6977
self.statsd.set('set', 123)
7078
assert self.recv() == 'set:123|s'

0 commit comments

Comments
 (0)