Skip to content

Commit d6a355a

Browse files
btouegbrettlangdon
authored andcommitted
Propagate x-datadog-origin (#821)
* Propagate x-datadog-origin * Add tests * More tests * Apply suggestions from code review (Brett) Co-Authored-By: btoueg <[email protected]> * Don't forget to tag * Fix test * Rename tag to _dd.origin * Make use of constants * Use new extract_header_value helper
1 parent 64e15f5 commit d6a355a

File tree

8 files changed

+53
-7
lines changed

8 files changed

+53
-7
lines changed

ddtrace/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
FILTERS_KEY = 'FILTERS'
22
SAMPLE_RATE_METRIC_KEY = '_sample_rate'
33
SAMPLING_PRIORITY_KEY = '_sampling_priority_v1'
4+
ORIGIN_KEY = '_dd.origin'
45
EVENT_SAMPLE_RATE_KEY = '_dd1.sr.eausr'
56

67
NUMERIC_TAGS = (EVENT_SAMPLE_RATE_KEY, )

ddtrace/context.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
import threading
33

4-
from .constants import SAMPLING_PRIORITY_KEY
4+
from .constants import SAMPLING_PRIORITY_KEY, ORIGIN_KEY
55
from .utils.formats import asbool, get_env
66

77
log = logging.getLogger(__name__)
@@ -25,7 +25,7 @@ class Context(object):
2525
_partial_flush_enabled = asbool(get_env('tracer', 'partial_flush_enabled', 'false'))
2626
_partial_flush_min_spans = int(get_env('tracer', 'partial_flush_min_spans', 500))
2727

28-
def __init__(self, trace_id=None, span_id=None, sampled=True, sampling_priority=None):
28+
def __init__(self, trace_id=None, span_id=None, sampled=True, sampling_priority=None, _dd_origin=None):
2929
"""
3030
Initialize a new thread-safe ``Context``.
3131
@@ -41,6 +41,7 @@ def __init__(self, trace_id=None, span_id=None, sampled=True, sampling_priority=
4141
self._parent_span_id = span_id
4242
self._sampled = sampled
4343
self._sampling_priority = sampling_priority
44+
self._dd_origin = _dd_origin
4445

4546
@property
4647
def trace_id(self):
@@ -184,6 +185,10 @@ def get(self):
184185
# attach the sampling priority to the context root span
185186
if sampled and sampling_priority is not None and trace:
186187
trace[0].set_metric(SAMPLING_PRIORITY_KEY, sampling_priority)
188+
origin = self._dd_origin
189+
# attach the origin to the root span tag
190+
if sampled and origin is not None and trace:
191+
trace[0].set_tag(ORIGIN_KEY, origin)
187192

188193
# clean the current state
189194
self._trace = []
@@ -202,6 +207,10 @@ def get(self):
202207
# attach the sampling priority to the context root span
203208
if sampled and sampling_priority is not None and trace:
204209
trace[0].set_metric(SAMPLING_PRIORITY_KEY, sampling_priority)
210+
origin = self._dd_origin
211+
# attach the origin to the root span tag
212+
if sampled and origin is not None and trace:
213+
trace[0].set_tag(ORIGIN_KEY, origin)
205214

206215
# Any open spans will remain as `self._trace`
207216
# Any finished spans will get returned to be flushed

ddtrace/propagation/http.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
HTTP_HEADER_TRACE_ID = "x-datadog-trace-id"
1212
HTTP_HEADER_PARENT_ID = "x-datadog-parent-id"
1313
HTTP_HEADER_SAMPLING_PRIORITY = "x-datadog-sampling-priority"
14+
HTTP_HEADER_ORIGIN = "x-datadog-origin"
1415

1516

1617
# Note that due to WSGI spec we have to also check for uppercased and prefixed
@@ -24,6 +25,9 @@
2425
POSSIBLE_HTTP_HEADER_SAMPLING_PRIORITIES = frozenset(
2526
[HTTP_HEADER_SAMPLING_PRIORITY, get_wsgi_header(HTTP_HEADER_SAMPLING_PRIORITY)]
2627
)
28+
POSSIBLE_HTTP_HEADER_ORIGIN = frozenset(
29+
[HTTP_HEADER_ORIGIN, get_wsgi_header(HTTP_HEADER_ORIGIN)]
30+
)
2731

2832

2933
class HTTPPropagator(object):
@@ -54,6 +58,9 @@ def parent_call():
5458
# Propagate priority only if defined
5559
if sampling_priority is not None:
5660
headers[HTTP_HEADER_SAMPLING_PRIORITY] = str(span_context.sampling_priority)
61+
# Propagate origin only if defined
62+
if span_context._dd_origin is not None:
63+
headers[HTTP_HEADER_ORIGIN] = str(span_context._dd_origin)
5764

5865
@staticmethod
5966
def extract_header_value(possible_header_names, headers, default=None):
@@ -86,6 +93,12 @@ def extract_sampling_priority(headers):
8693
POSSIBLE_HTTP_HEADER_SAMPLING_PRIORITIES, headers,
8794
)
8895

96+
@staticmethod
97+
def extract_origin(headers):
98+
return HTTPPropagator.extract_header_value(
99+
POSSIBLE_HTTP_HEADER_ORIGIN, headers,
100+
)
101+
89102
def extract(self, headers):
90103
"""Extract a Context from HTTP headers into a new Context.
91104
@@ -111,6 +124,7 @@ def my_controller(url, headers):
111124
trace_id = HTTPPropagator.extract_trace_id(headers)
112125
parent_span_id = HTTPPropagator.extract_parent_span_id(headers)
113126
sampling_priority = HTTPPropagator.extract_sampling_priority(headers)
127+
origin = HTTPPropagator.extract_origin(headers)
114128

115129
if sampling_priority is not None:
116130
sampling_priority = int(sampling_priority)
@@ -119,15 +133,17 @@ def my_controller(url, headers):
119133
trace_id=trace_id,
120134
span_id=parent_span_id,
121135
sampling_priority=sampling_priority,
136+
_dd_origin=origin,
122137
)
123138
# If headers are invalid and cannot be parsed, return a new context and log the issue.
124139
except Exception as error:
125140
try:
126141
log.debug(
127-
"invalid x-datadog-* headers, trace-id: %s, parent-id: %s, priority: %s, error: %s",
142+
"invalid x-datadog-* headers, trace-id: %s, parent-id: %s, priority: %s, origin: %s, error: %s",
128143
headers.get(HTTP_HEADER_TRACE_ID, 0),
129144
headers.get(HTTP_HEADER_PARENT_ID, 0),
130145
headers.get(HTTP_HEADER_SAMPLING_PRIORITY),
146+
headers.get(HTTP_HEADER_ORIGIN, ''),
131147
error,
132148
)
133149
# We might fail on string formatting errors ; in that case only format the first error

tests/contrib/pyramid/test_pyramid.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from nose.tools import eq_, ok_
22

3+
from ddtrace.constants import SAMPLING_PRIORITY_KEY, ORIGIN_KEY
4+
35
from .utils import PyramidTestCase, PyramidBase
46

57

@@ -32,6 +34,7 @@ def test_distributed_tracing(self):
3234
'x-datadog-trace-id': '100',
3335
'x-datadog-parent-id': '42',
3436
'x-datadog-sampling-priority': '2',
37+
'x-datadog-origin': 'synthetics',
3538
}
3639
self.app.get('/', headers=headers, status=200)
3740
writer = self.tracer.writer
@@ -41,7 +44,8 @@ def test_distributed_tracing(self):
4144
span = spans[0]
4245
eq_(span.trace_id, 100)
4346
eq_(span.parent_id, 42)
44-
eq_(span.get_metric('_sampling_priority_v1'), 2)
47+
eq_(span.get_metric(SAMPLING_PRIORITY_KEY), 2)
48+
eq_(span.get_tag(ORIGIN_KEY), 'synthetics')
4549

4650

4751
class TestPyramidDistributedTracingDisabled(PyramidBase):
@@ -58,6 +62,7 @@ def test_distributed_tracing_disabled(self):
5862
'x-datadog-trace-id': '100',
5963
'x-datadog-parent-id': '42',
6064
'x-datadog-sampling-priority': '2',
65+
'x-datadog-origin': 'synthetics',
6166
}
6267
self.app.get('/', headers=headers, status=200)
6368
writer = self.tracer.writer
@@ -67,4 +72,5 @@ def test_distributed_tracing_disabled(self):
6772
span = spans[0]
6873
ok_(span.trace_id != 100)
6974
ok_(span.parent_id != 42)
70-
ok_(span.get_metric('_sampling_priority_v1') != 2)
75+
ok_(span.get_metric(SAMPLING_PRIORITY_KEY) != 2)
76+
ok_(span.get_tag(ORIGIN_KEY) != 'synthetics')

tests/contrib/pyramid/test_pyramid_autopatch.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def test_distributed_tracing(self):
2727
'x-datadog-trace-id': '100',
2828
'x-datadog-parent-id': '42',
2929
'x-datadog-sampling-priority': '2',
30+
'x-datadog-origin': 'synthetics',
3031
}
3132
self.app.get('/', headers=headers, status=200)
3233
writer = self.tracer.writer

tests/contrib/tornado/test_tornado_web.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from .web.app import CustomDefaultHandler
44
from .utils import TornadoTestCase
55

6-
from ddtrace.constants import SAMPLING_PRIORITY_KEY, EVENT_SAMPLE_RATE_KEY
6+
from ddtrace.constants import SAMPLING_PRIORITY_KEY, ORIGIN_KEY, EVENT_SAMPLE_RATE_KEY
77

88
from opentracing.scope_managers.tornado import TornadoScopeManager
99
from tests.opentracer.utils import init_tracer
@@ -321,7 +321,8 @@ def test_no_propagation(self):
321321
headers = {
322322
'x-datadog-trace-id': '1234',
323323
'x-datadog-parent-id': '4567',
324-
'x-datadog-sampling-priority': '2'
324+
'x-datadog-sampling-priority': '2',
325+
'x-datadog-origin': 'synthetics',
325326
}
326327
response = self.fetch('/success/', headers=headers)
327328
eq_(200, response.code)
@@ -342,6 +343,7 @@ def test_no_propagation(self):
342343
assert request_span.trace_id != 1234
343344
assert request_span.parent_id != 4567
344345
assert request_span.get_metric(SAMPLING_PRIORITY_KEY) != 2
346+
assert request_span.get_tag(ORIGIN_KEY) != 'synthetics'
345347

346348

347349
class TestCustomTornadoWeb(TornadoTestCase):

tests/propagation/test_http.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
HTTP_HEADER_TRACE_ID,
88
HTTP_HEADER_PARENT_ID,
99
HTTP_HEADER_SAMPLING_PRIORITY,
10+
HTTP_HEADER_ORIGIN,
1011
)
1112

1213

@@ -21,6 +22,7 @@ def test_inject(self):
2122

2223
with tracer.trace("global_root_span") as span:
2324
span.context.sampling_priority = 2
25+
span.context._dd_origin = "synthetics"
2426
headers = {}
2527
propagator = HTTPPropagator()
2628
propagator.inject(span.context, headers)
@@ -31,6 +33,10 @@ def test_inject(self):
3133
int(headers[HTTP_HEADER_SAMPLING_PRIORITY]),
3234
span.context.sampling_priority,
3335
)
36+
eq_(
37+
headers[HTTP_HEADER_ORIGIN],
38+
span.context._dd_origin,
39+
)
3440

3541
def test_extract(self):
3642
tracer = get_dummy_tracer()
@@ -39,6 +45,7 @@ def test_extract(self):
3945
"x-datadog-trace-id": "1234",
4046
"x-datadog-parent-id": "5678",
4147
"x-datadog-sampling-priority": "1",
48+
"x-datadog-origin": "synthetics",
4249
}
4350

4451
propagator = HTTPPropagator()
@@ -49,6 +56,7 @@ def test_extract(self):
4956
eq_(span.trace_id, 1234)
5057
eq_(span.parent_id, 5678)
5158
eq_(span.context.sampling_priority, 1)
59+
eq_(span.context._dd_origin, "synthetics")
5260

5361
def test_WSGI_extract(self):
5462
"""Ensure we support the WSGI formatted headers as well."""
@@ -58,6 +66,7 @@ def test_WSGI_extract(self):
5866
"HTTP_X_DATADOG_TRACE_ID": "1234",
5967
"HTTP_X_DATADOG_PARENT_ID": "5678",
6068
"HTTP_X_DATADOG_SAMPLING_PRIORITY": "1",
69+
"HTTP_X_DATADOG_ORIGIN": "synthetics",
6170
}
6271

6372
propagator = HTTPPropagator()
@@ -68,3 +77,4 @@ def test_WSGI_extract(self):
6877
eq_(span.trace_id, 1234)
6978
eq_(span.parent_id, 5678)
7079
eq_(span.context.sampling_priority, 1)
80+
eq_(span.context._dd_origin, "synthetics")

tests/test_context.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ def test_clone(self):
388388
eq_(cloned_ctx._parent_span_id, ctx._parent_span_id)
389389
eq_(cloned_ctx._sampled, ctx._sampled)
390390
eq_(cloned_ctx._sampling_priority, ctx._sampling_priority)
391+
eq_(cloned_ctx._dd_origin, ctx._dd_origin)
391392
eq_(cloned_ctx._current_span, ctx._current_span)
392393
eq_(cloned_ctx._trace, [])
393394
eq_(cloned_ctx._finished_spans, 0)

0 commit comments

Comments
 (0)