Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.

Commit 8f4c952

Browse files
reyangSergeyKanzhelev
authored andcommitted
support tracestate HTTP propagation (#261)
1 parent 3c02724 commit 8f4c952

3 files changed

Lines changed: 94 additions & 14 deletions

File tree

opencensus/trace/propagation/trace_context_http_header_format.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717

1818
from opencensus.trace.span_context import SpanContext
1919
from opencensus.trace.trace_options import TraceOptions
20+
from opencensus.trace.propagation.tracestate_string_format \
21+
import TracestateStringFormatter
2022

21-
_TRACE_PARENT_HEADER_NAME = 'traceparent'
23+
_TRACEPARENT_HEADER_NAME = 'traceparent'
24+
_TRACESTATE_HEADER_NAME = 'tracestate'
2225
_TRACE_CONTEXT_HEADER_FORMAT = \
2326
'([0-9a-f]{2})(-([0-9a-f]{32}))(-([0-9a-f]{16}))?(-([0-9a-f]{2}))?'
2427
_TRACE_CONTEXT_HEADER_RE = re.compile(_TRACE_CONTEXT_HEADER_FORMAT)
@@ -88,11 +91,19 @@ def from_headers(self, headers):
8891
"""
8992
if headers is None:
9093
return SpanContext()
91-
header = headers.get(_TRACE_PARENT_HEADER_NAME)
94+
header = headers.get(_TRACEPARENT_HEADER_NAME)
9295
if header is None:
9396
return SpanContext()
9497
header = str(header.encode('utf-8'))
95-
return self.from_header(header)
98+
99+
span_context = self.from_header(header)
100+
101+
header = headers.get(_TRACESTATE_HEADER_NAME)
102+
if header is not None:
103+
span_context.tracestate = \
104+
TracestateStringFormatter().from_string(header)
105+
106+
return span_context
96107

97108
def to_header(self, span_context):
98109
"""Convert a SpanContext object to header string, using version 0.
@@ -128,6 +139,11 @@ def to_headers(self, span_context):
128139
:rtype: dict
129140
:returns: W3C Distributed Tracing headers.
130141
"""
131-
return {
132-
_TRACE_PARENT_HEADER_NAME: self.to_header(span_context),
142+
headers = {
143+
_TRACEPARENT_HEADER_NAME: self.to_header(span_context),
133144
}
145+
tracestate = span_context.tracestate
146+
if tracestate:
147+
headers[_TRACESTATE_HEADER_NAME] = \
148+
TracestateStringFormatter().to_string(tracestate)
149+
return headers

tests/unit/trace/ext/httplib/test_httplib_trace.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ def start_span(self):
177177
span.context_tracer.span_context = mock.Mock()
178178
span.context_tracer.span_context.trace_id = '123'
179179
span.context_tracer.span_context.span_id = '456'
180+
span.context_tracer.span_context.tracestate = None
180181
self.span = span
181182
return span
182183

tests/unit/trace/propagation/test_trace_context_http_header_format.py

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def test_from_header_no_header(self):
2626
TraceContextPropagator()
2727
span_context = propagator.from_header(None)
2828

29-
assert isinstance(span_context, SpanContext)
29+
self.assertTrue(isinstance(span_context, SpanContext))
3030

3131
def test_from_headers_none(self):
3232
from opencensus.trace.span_context import SpanContext
@@ -35,7 +35,7 @@ def test_from_headers_none(self):
3535
TraceContextPropagator()
3636
span_context = propagator.from_headers(None)
3737

38-
assert isinstance(span_context, SpanContext)
38+
self.assertTrue(isinstance(span_context, SpanContext))
3939

4040
def test_from_headers_empty(self):
4141
from opencensus.trace.span_context import SpanContext
@@ -44,7 +44,20 @@ def test_from_headers_empty(self):
4444
TraceContextPropagator()
4545
span_context = propagator.from_headers({})
4646

47-
assert isinstance(span_context, SpanContext)
47+
self.assertTrue(isinstance(span_context, SpanContext))
48+
49+
def test_from_headers_with_tracestate(self):
50+
from opencensus.trace.span_context import SpanContext
51+
52+
propagator = trace_context_http_header_format.\
53+
TraceContextPropagator()
54+
span_context = propagator.from_headers({
55+
'traceparent': '00-a66ee7820d074463aff4c617a63e929f-91e072af6a404137-01',
56+
'tracestate': 'foo=1,bar=2,baz=3',
57+
})
58+
59+
self.assertTrue(isinstance(span_context, SpanContext))
60+
self.assertTrue(span_context.tracestate)
4861

4962
def test_header_type_error(self):
5063
header = 1234
@@ -63,7 +76,7 @@ def test_header_version_not_support(self):
6376
TraceContextPropagator()
6477
span_context = propagator.from_header(header)
6578

66-
assert isinstance(span_context, SpanContext)
79+
self.assertTrue(isinstance(span_context, SpanContext))
6780

6881
def test_header_match(self):
6982
# Trace option is not enabled.
@@ -153,7 +166,7 @@ def test_to_header(self):
153166

154167
self.assertEqual(header, expected_header)
155168

156-
def test_to_headers(self):
169+
def test_to_headers_without_tracestate(self):
157170
from opencensus.trace import span_context
158171
from opencensus.trace import trace_options
159172

@@ -168,8 +181,58 @@ def test_to_headers(self):
168181
TraceContextPropagator()
169182

170183
headers = propagator.to_headers(span_context)
171-
expected_headers = {
172-
'traceparent': '00-{}-{}-01'.format(trace_id, span_id_hex),
173-
}
174184

175-
self.assertEqual(headers, expected_headers)
185+
self.assertTrue('traceparent' in headers)
186+
self.assertEqual(headers['traceparent'],
187+
'00-{}-{}-01'.format(trace_id, span_id_hex))
188+
189+
self.assertFalse('tracestate' in headers)
190+
191+
def test_to_headers_with_empty_tracestate(self):
192+
from opencensus.trace import span_context
193+
from opencensus.trace import trace_options
194+
from opencensus.trace.tracestate import Tracestate
195+
196+
trace_id = '6e0c63257de34c92bf9efcd03927272e'
197+
span_id_hex = '00f067aa0ba902b7'
198+
span_context = span_context.SpanContext(
199+
trace_id=trace_id,
200+
span_id=span_id_hex,
201+
tracestate=Tracestate(),
202+
trace_options=trace_options.TraceOptions('1'))
203+
204+
propagator = trace_context_http_header_format.\
205+
TraceContextPropagator()
206+
207+
headers = propagator.to_headers(span_context)
208+
209+
self.assertTrue('traceparent' in headers)
210+
self.assertEqual(headers['traceparent'],
211+
'00-{}-{}-01'.format(trace_id, span_id_hex))
212+
213+
self.assertFalse('tracestate' in headers)
214+
215+
def test_to_headers_with_tracestate(self):
216+
from opencensus.trace import span_context
217+
from opencensus.trace import trace_options
218+
from opencensus.trace.tracestate import Tracestate
219+
220+
trace_id = '6e0c63257de34c92bf9efcd03927272e'
221+
span_id_hex = '00f067aa0ba902b7'
222+
span_context = span_context.SpanContext(
223+
trace_id=trace_id,
224+
span_id=span_id_hex,
225+
tracestate=Tracestate(foo = "xyz"),
226+
trace_options=trace_options.TraceOptions('1'))
227+
228+
propagator = trace_context_http_header_format.\
229+
TraceContextPropagator()
230+
231+
headers = propagator.to_headers(span_context)
232+
233+
self.assertTrue('traceparent' in headers)
234+
self.assertEqual(headers['traceparent'],
235+
'00-{}-{}-01'.format(trace_id, span_id_hex))
236+
237+
self.assertTrue('tracestate' in headers)
238+
self.assertEqual(headers['tracestate'], 'foo=xyz')

0 commit comments

Comments
 (0)