Skip to content

Commit 99cb046

Browse files
authored
ext/datadog: add environment variable configuration (#836)
1 parent a7f0b75 commit 99cb046

File tree

3 files changed

+112
-10
lines changed

3 files changed

+112
-10
lines changed

ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@
44
USER_KEEP = 2
55
SAMPLE_RATE_METRIC_KEY = "_sample_rate"
66
SAMPLING_PRIORITY_KEY = "_sampling_priority_v1"
7+
ENV_KEY = "env"
8+
VERSION_KEY = "version"

ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from opentelemetry.trace.status import StatusCanonicalCode
2626

2727
# pylint:disable=relative-beyond-top-level
28-
from .constants import DD_ORIGIN, SAMPLE_RATE_METRIC_KEY
28+
from .constants import DD_ORIGIN, ENV_KEY, SAMPLE_RATE_METRIC_KEY, VERSION_KEY
2929

3030
logger = logging.getLogger(__name__)
3131

@@ -56,16 +56,24 @@ class DatadogSpanExporter(SpanExporter):
5656
5757
Args:
5858
agent_url: The url of the Datadog Agent or use ``DD_TRACE_AGENT_URL`` environment variable
59-
service: The service to be used for the application or use ``DD_SERVICE`` environment variable
59+
service: The service name to be used for the application or use ``DD_SERVICE`` environment variable
60+
env: Set the application’s environment or use ``DD_ENV`` environment variable
61+
version: Set the application’s version or use ``DD_VERSION`` environment variable
62+
tags: A list of default tags to be added to every span or use ``DD_TAGS`` environment variable
6063
"""
6164

62-
def __init__(self, agent_url=None, service=None):
65+
def __init__(
66+
self, agent_url=None, service=None, env=None, version=None, tags=None
67+
):
6368
self.agent_url = (
6469
agent_url
6570
if agent_url
6671
else os.environ.get("DD_TRACE_AGENT_URL", DEFAULT_AGENT_URL)
6772
)
68-
self.service = service if service else os.environ.get("DD_SERVICE")
73+
self.service = service or os.environ.get("DD_SERVICE")
74+
self.env = env or os.environ.get("DD_ENV")
75+
self.version = version or os.environ.get("DD_VERSION")
76+
self.tags = _parse_tags_str(tags or os.environ.get("DD_TAGS"))
6977
self._agent_writer = None
7078

7179
@property
@@ -133,6 +141,17 @@ def _translate_to_datadog(self, spans):
133141

134142
datadog_span.set_tags(span.attributes)
135143

144+
# add configured env tag
145+
if self.env is not None:
146+
datadog_span.set_tag(ENV_KEY, self.env)
147+
148+
# add configured application version tag to only root span
149+
if self.version is not None and parent_id == 0:
150+
datadog_span.set_tag(VERSION_KEY, self.version)
151+
152+
# add configured global tags
153+
datadog_span.set_tags(self.tags)
154+
136155
# add origin to root span
137156
origin = _get_origin(span)
138157
if origin and parent_id == 0:
@@ -230,3 +249,35 @@ def _get_sampling_rate(span):
230249
and isinstance(span.sampler, trace_api.sampling.ProbabilitySampler)
231250
else None
232251
)
252+
253+
254+
def _parse_tags_str(tags_str):
255+
"""Parse a string of tags typically provided via environment variables.
256+
257+
The expected string is of the form::
258+
"key1:value1,key2:value2"
259+
260+
:param tags_str: A string of the above form to parse tags from.
261+
:return: A dict containing the tags that were parsed.
262+
"""
263+
parsed_tags = {}
264+
if not tags_str:
265+
return parsed_tags
266+
267+
for tag in tags_str.split(","):
268+
try:
269+
key, value = tag.split(":", 1)
270+
271+
# Validate the tag
272+
if key == "" or value == "" or value.endswith(":"):
273+
raise ValueError
274+
except ValueError:
275+
logger.error(
276+
"Malformed tag in tag pair '%s' from tag string '%s'.",
277+
tag,
278+
tags_str,
279+
)
280+
else:
281+
parsed_tags[key] = value
282+
283+
return parsed_tags

ext/opentelemetry-ext-datadog/tests/test_datadog_exporter.py

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,26 +72,73 @@ def test_constructor_explicit(self):
7272
"""Test the constructor passing all the options."""
7373
agent_url = "http://localhost:8126"
7474
exporter = datadog.DatadogSpanExporter(
75-
agent_url=agent_url, service="explicit"
75+
agent_url=agent_url, service="explicit",
7676
)
7777

7878
self.assertEqual(exporter.agent_url, agent_url)
7979
self.assertEqual(exporter.service, "explicit")
80-
self.assertIsNotNone(exporter.agent_writer)
80+
self.assertIsNone(exporter.env)
81+
self.assertIsNone(exporter.version)
82+
self.assertEqual(exporter.tags, {})
83+
84+
exporter = datadog.DatadogSpanExporter(
85+
agent_url=agent_url,
86+
service="explicit",
87+
env="test",
88+
version="0.0.1",
89+
tags="",
90+
)
91+
92+
self.assertEqual(exporter.agent_url, agent_url)
93+
self.assertEqual(exporter.service, "explicit")
94+
self.assertEqual(exporter.env, "test")
95+
self.assertEqual(exporter.version, "0.0.1")
96+
self.assertEqual(exporter.tags, {})
97+
98+
exporter = datadog.DatadogSpanExporter(
99+
agent_url=agent_url,
100+
service="explicit",
101+
env="test",
102+
version="0.0.1",
103+
tags="team:testers,layer:app",
104+
)
105+
106+
self.assertEqual(exporter.agent_url, agent_url)
107+
self.assertEqual(exporter.service, "explicit")
108+
self.assertEqual(exporter.env, "test")
109+
self.assertEqual(exporter.version, "0.0.1")
110+
self.assertEqual(exporter.tags, {"team": "testers", "layer": "app"})
81111

82112
@mock.patch.dict(
83113
"os.environ",
84-
{"DD_TRACE_AGENT_URL": "http://agent:8126", "DD_SERVICE": "environ"},
114+
{
115+
"DD_TRACE_AGENT_URL": "http://agent:8126",
116+
"DD_SERVICE": "test-service",
117+
"DD_ENV": "test",
118+
"DD_VERSION": "0.0.1",
119+
"DD_TAGS": "team:testers",
120+
},
85121
)
86122
def test_constructor_environ(self):
87123
exporter = datadog.DatadogSpanExporter()
88124

89125
self.assertEqual(exporter.agent_url, "http://agent:8126")
90-
self.assertEqual(exporter.service, "environ")
126+
self.assertEqual(exporter.service, "test-service")
127+
self.assertEqual(exporter.env, "test")
128+
self.assertEqual(exporter.version, "0.0.1")
129+
self.assertEqual(exporter.tags, {"team": "testers"})
91130
self.assertIsNotNone(exporter.agent_writer)
92131

93132
# pylint: disable=too-many-locals
94-
@mock.patch.dict("os.environ", {"DD_SERVICE": "test-service"})
133+
@mock.patch.dict(
134+
"os.environ",
135+
{
136+
"DD_SERVICE": "test-service",
137+
"DD_ENV": "test",
138+
"DD_VERSION": "0.0.1",
139+
"DD_TAGS": "team:testers",
140+
},
141+
)
95142
def test_translate_to_datadog(self):
96143
# pylint: disable=invalid-name
97144
self.maxDiff = None
@@ -174,6 +221,7 @@ def test_translate_to_datadog(self):
174221
duration=durations[0],
175222
error=0,
176223
service="test-service",
224+
meta={"env": "test", "team": "testers"},
177225
),
178226
dict(
179227
trace_id=trace_id_low,
@@ -185,6 +233,7 @@ def test_translate_to_datadog(self):
185233
duration=durations[1],
186234
error=0,
187235
service="test-service",
236+
meta={"env": "test", "team": "testers", "version": "0.0.1"},
188237
),
189238
dict(
190239
trace_id=trace_id_low,
@@ -196,12 +245,12 @@ def test_translate_to_datadog(self):
196245
duration=durations[2],
197246
error=0,
198247
service="test-service",
248+
meta={"env": "test", "team": "testers", "version": "0.0.1"},
199249
),
200250
]
201251

202252
self.assertEqual(datadog_spans, expected_spans)
203253

204-
@mock.patch.dict("os.environ", {"DD_SERVICE": "test-service"})
205254
def test_export(self):
206255
"""Test that agent and/or collector are invoked"""
207256
# create and save span to be used in tests

0 commit comments

Comments
 (0)