Skip to content
This repository was archived by the owner on Jul 21, 2021. It is now read-only.

Commit b6fbd0e

Browse files
committed
provide meaningful error messages when failed to extract headers from carrier
1 parent 96ebe40 commit b6fbd0e

File tree

3 files changed

+118
-12
lines changed

3 files changed

+118
-12
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ History
66
3.1.1 (unreleased)
77
------------------
88

9-
- Nothing changed yet.
9+
- TextPropagator.extract raises SpanContextCorruptedException with
10+
more meaningful error messages.
1011

1112

1213
3.1.0 (2019-05-12)
@@ -100,4 +101,3 @@ History
100101
----------------
101102

102103
- Initial public API
103-

basictracer/text_propagator.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,34 @@
1212
field_count = 3
1313

1414

15+
def parse_hex_for_field(field_name, value):
16+
"""parses the hexadecimal value of a field into an integer.
17+
Raises SpanContextCorruptedException in case of failure
18+
"""
19+
msg = "{field_name} got an invalid hexadecimal value {value!r}"
20+
msg = msg.format(field_name=field_name, value=value)
21+
try:
22+
return int(value, 16)
23+
except ValueError:
24+
raise SpanContextCorruptedException(msg)
25+
26+
27+
def parse_boolean_for_field(field_name, value):
28+
if value in ('true', '1'):
29+
return True
30+
elif value in ('false', '0'):
31+
return False
32+
33+
msg = (
34+
"{field} got an invalid value {value!r}, "
35+
"should be one of 'true', 'false', '0', '1'"
36+
)
37+
raise SpanContextCorruptedException(msg.format(
38+
value=value,
39+
field=field_name_sampled
40+
))
41+
42+
1543
class TextPropagator(Propagator):
1644
"""A BasicTracer Propagator for Format.TEXT_MAP."""
1745

@@ -31,24 +59,26 @@ def extract(self, carrier): # noqa
3159
v = carrier[k]
3260
k = k.lower()
3361
if k == field_name_span_id:
34-
span_id = int(v, 16)
62+
span_id = parse_hex_for_field(field_name_span_id, v)
3563
count += 1
3664
elif k == field_name_trace_id:
37-
trace_id = int(v, 16)
65+
trace_id = parse_hex_for_field(field_name_trace_id, v)
3866
count += 1
3967
elif k == field_name_sampled:
40-
if v in ('true', '1'):
41-
sampled = True
42-
elif v in ('false', '0'):
43-
sampled = False
44-
else:
45-
raise SpanContextCorruptedException()
68+
sampled = parse_boolean_for_field(field_name_sampled, v)
4669
count += 1
4770
elif k.startswith(prefix_baggage):
4871
baggage[k[len(prefix_baggage):]] = v
4972

5073
if count != field_count:
51-
raise SpanContextCorruptedException()
74+
msg = (
75+
"expected to parse {field_count} fields"
76+
", but parsed {count} instead"
77+
)
78+
raise SpanContextCorruptedException(msg.format(
79+
field_count=field_count,
80+
count=count,
81+
))
5282

5383
return SpanContext(
5484
span_id=span_id,

tests/test_propagation.py

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import pytest
2-
from opentracing import Format, UnsupportedFormatException
2+
from opentracing import (
3+
Format,
4+
UnsupportedFormatException,
5+
SpanContextCorruptedException,
6+
)
37
from basictracer import BasicTracer
48

59

@@ -52,3 +56,75 @@ def test_start_span():
5256
assert child.context.sampled == sp.context.sampled
5357
assert child.context.baggage == sp.context.baggage
5458
assert child.parent_id == sp.context.span_id
59+
60+
61+
def test_span_corrupted_missing_fields():
62+
tracer = BasicTracer()
63+
tracer.register_required_propagators()
64+
65+
# Given an empty carrier
66+
headers = {}
67+
68+
# When .extract is called
69+
with pytest.raises(SpanContextCorruptedException) as exc:
70+
tracer.extract(Format.TEXT_MAP, headers)
71+
72+
# Then it should raise SpanContextCorruptedException
73+
assert str(exc.value) == 'expected to parse 3 fields, but parsed 0 instead'
74+
75+
76+
def test_span_corrupted_invalid_sampled_value():
77+
tracer = BasicTracer()
78+
tracer.register_required_propagators()
79+
80+
# Given a carrier with invalid "ot-tracer-sampled" value
81+
headers = {
82+
"ot-tracer-spanid": 'deadbeef',
83+
"ot-tracer-sampled": 'notbool',
84+
"ot-tracer-traceid": '1c3b00da',
85+
}
86+
87+
# When .extract is called
88+
with pytest.raises(SpanContextCorruptedException) as exc:
89+
tracer.extract(Format.TEXT_MAP, headers)
90+
91+
# Then it should raise SpanContextCorruptedException
92+
assert str(exc.value) == "ot-tracer-sampled got an invalid value 'notbool', should be one of 'true', 'false', '0', '1'"
93+
94+
95+
def test_span_corrupted_invalid_spanid_value():
96+
tracer = BasicTracer()
97+
tracer.register_required_propagators()
98+
99+
# Given a carrier with invalid "ot-tracer-spanid" value
100+
headers = {
101+
"ot-tracer-spanid": 'nothex',
102+
"ot-tracer-sampled": 'false',
103+
"ot-tracer-traceid": '1c3b00da',
104+
}
105+
106+
# When .extract is called
107+
with pytest.raises(SpanContextCorruptedException) as exc:
108+
tracer.extract(Format.TEXT_MAP, headers)
109+
110+
# Then it should raise SpanContextCorruptedException
111+
assert str(exc.value) == "ot-tracer-spanid got an invalid hexadecimal value 'nothex'"
112+
113+
114+
def test_span_corrupted_invalid_traceid_value():
115+
tracer = BasicTracer()
116+
tracer.register_required_propagators()
117+
118+
# Given a carrier with invalid "ot-tracer-traceid" value
119+
headers = {
120+
"ot-tracer-traceid": 'nothex',
121+
"ot-tracer-sampled": 'false',
122+
"ot-tracer-spanid": '1c3b00da',
123+
}
124+
125+
# When .extract is called
126+
with pytest.raises(SpanContextCorruptedException) as exc:
127+
tracer.extract(Format.TEXT_MAP, headers)
128+
129+
# Then it should raise SpanContextCorruptedException
130+
assert str(exc.value) == "ot-tracer-traceid got an invalid hexadecimal value 'nothex'"

0 commit comments

Comments
 (0)