1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15- import logging
1615import re
1716
1817from opencensus .trace .span_context import SpanContext
2221
2322_TRACEPARENT_HEADER_NAME = 'traceparent'
2423_TRACESTATE_HEADER_NAME = 'tracestate'
25- _TRACE_CONTEXT_HEADER_FORMAT = \
26- '([0-9a-f]{2})(-([0-9a-f]{32}))(-([0-9a-f]{16}))?(-([0-9a-f]{2}))?'
27- _TRACE_CONTEXT_HEADER_RE = re .compile (_TRACE_CONTEXT_HEADER_FORMAT )
24+ _TRACEPARENT_HEADER_FORMAT = \
25+ '^[ \t ]*([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})' + \
26+ '(-.*)?[ \t ]*$'
27+ _TRACEPARENT_HEADER_FORMAT_RE = re .compile (_TRACEPARENT_HEADER_FORMAT )
2828
2929
3030class TraceContextPropagator (object ):
3131 """Propagator for processing the trace context HTTP header format."""
3232
33- def from_header (self , header ):
34- """Generate a SpanContext object using the trace context header.
35-
36- :type header: str
37- :param header: Trace context header which was extracted from the HTTP
38- request headers.
39-
40- :rtype: :class:`~opencensus.trace.span_context.SpanContext`
41- :returns: SpanContext generated from the trace context header.
42- """
43- if header is None :
44- return SpanContext ()
45-
46- try :
47- match = re .search (_TRACE_CONTEXT_HEADER_RE , header )
48- except TypeError :
49- logging .warning (
50- 'Header should be str, got {}. Cannot parse the header.'
51- .format (header .__class__ .__name__ ))
52- raise
53-
54- if match :
55- version = match .group (1 )
56-
57- if version == '00' :
58- trace_id = match .group (3 )
59- span_id = match .group (5 )
60- trace_options = match .group (7 )
61-
62- if trace_options is None :
63- trace_options = 1
64-
65- # Need to convert span_id from hex string to int
66- span_context = SpanContext (
67- trace_id = trace_id ,
68- span_id = span_id ,
69- trace_options = TraceOptions (trace_options ),
70- from_header = True )
71- return span_context
72- else :
73- logging .warning (
74- 'Header format version {} is not supported, generate a new'
75- 'context instead.' .format (version ))
76- else :
77- logging .warning (
78- 'Cannot parse the header {}, generate a new context instead.'
79- .format (header ))
80-
81- return SpanContext ()
82-
8333 def from_headers (self , headers ):
8434 """Generate a SpanContext object using the W3C Distributed Tracing headers.
8535
@@ -91,42 +41,46 @@ def from_headers(self, headers):
9141 """
9242 if headers is None :
9343 return SpanContext ()
44+
9445 header = headers .get (_TRACEPARENT_HEADER_NAME )
9546 if header is None :
9647 return SpanContext ()
97- header = str (header .encode ('utf-8' ))
98-
99- span_context = self .from_header (header )
10048
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
49+ match = re .search (_TRACEPARENT_HEADER_FORMAT_RE , header )
50+ if not match :
51+ return SpanContext ()
10752
108- def to_header (self , span_context ):
109- """Convert a SpanContext object to header string, using version 0.
53+ version = match .group (1 )
54+ trace_id = match .group (2 )
55+ span_id = match .group (3 )
56+ trace_options = match .group (4 )
11057
111- :type span_context:
112- :class:`~opencensus.trace.span_context.SpanContext`
113- :param span_context: SpanContext object.
58+ if trace_id == '0' * 32 or span_id == '0' * 16 :
59+ return SpanContext ()
11460
115- :rtype: str
116- :returns: A trace context header string in trace context HTTP format.
117- """
118- trace_id = span_context .trace_id
119- span_id = span_context .span_id
120- trace_options = span_context .trace_options .enabled
61+ if version == '00' :
62+ if match .group (5 ):
63+ return SpanContext ()
64+ if version == 'ff' :
65+ return SpanContext ()
12166
122- # Convert the trace options
123- trace_options = '01' if trace_options else '00'
67+ span_context = SpanContext (
68+ trace_id = trace_id ,
69+ span_id = span_id ,
70+ trace_options = TraceOptions (trace_options ),
71+ from_header = True )
12472
125- header = '00-{}-{}-{}' .format (
126- trace_id ,
127- span_id ,
128- trace_options )
129- return header
73+ header = headers .get (_TRACESTATE_HEADER_NAME )
74+ if header is None :
75+ return span_context
76+ try :
77+ tracestate = TracestateStringFormatter ().from_string (header )
78+ if tracestate .is_valid ():
79+ span_context .tracestate = \
80+ TracestateStringFormatter ().from_string (header )
81+ except ValueError :
82+ pass
83+ return span_context
13084
13185 def to_headers (self , span_context ):
13286 """Convert a SpanContext object to W3C Distributed Tracing headers,
@@ -139,8 +93,19 @@ def to_headers(self, span_context):
13993 :rtype: dict
14094 :returns: W3C Distributed Tracing headers.
14195 """
96+ trace_id = span_context .trace_id
97+ span_id = span_context .span_id
98+ trace_options = span_context .trace_options .enabled
99+
100+ # Convert the trace options
101+ trace_options = '01' if trace_options else '00'
102+
142103 headers = {
143- _TRACEPARENT_HEADER_NAME : self .to_header (span_context ),
104+ _TRACEPARENT_HEADER_NAME : '00-{}-{}-{}' .format (
105+ trace_id ,
106+ span_id ,
107+ trace_options
108+ ),
144109 }
145110 tracestate = span_context .tracestate
146111 if tracestate :
0 commit comments