Skip to content

Commit

Permalink
Add indicator if SpanContext was propagated from remote parent (#516)
Browse files Browse the repository at this point in the history
According to the spec a SpanContext should have an indicator if it was propagated from a remote parent.

Introduces an is_remote flag on the SpanContext which gets set when the SpanContext is extracted in a propagaton.
  • Loading branch information
mariojonke committed Mar 26, 2020
1 parent 39654a0 commit 144ab39
Show file tree
Hide file tree
Showing 13 changed files with 116 additions and 31 deletions.
13 changes: 10 additions & 3 deletions ext/opentelemetry-ext-jaeger/tests/test_jaeger_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def setUp(self):
context = trace_api.SpanContext(
trace_id=0x000000000000000000000000DEADBEEF,
span_id=0x00000000DEADBEF0,
is_remote=False,
)

self._test_span = trace.Span("test_span", context=context)
Expand Down Expand Up @@ -133,9 +134,15 @@ def test_translate_to_jaeger(self):
start_times[2] + durations[2],
)

span_context = trace_api.SpanContext(trace_id, span_id)
parent_context = trace_api.SpanContext(trace_id, parent_id)
other_context = trace_api.SpanContext(trace_id, other_id)
span_context = trace_api.SpanContext(
trace_id, span_id, is_remote=False
)
parent_context = trace_api.SpanContext(
trace_id, parent_id, is_remote=False
)
other_context = trace_api.SpanContext(
trace_id, other_id, is_remote=False
)

event_attributes = {
"annotation_bool": True,
Expand Down
15 changes: 11 additions & 4 deletions ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ def test_log_event(self):
def test_span_context(self):
"""Test construction of `SpanContextShim` objects."""

otel_context = trace.SpanContext(1234, 5678)
otel_context = trace.SpanContext(1234, 5678, is_remote=False)
context = opentracingshim.SpanContextShim(otel_context)

self.assertIsInstance(context, opentracing.SpanContext)
Expand All @@ -476,7 +476,9 @@ def test_span_on_error(self):
def test_inject_http_headers(self):
"""Test `inject()` method for Format.HTTP_HEADERS."""

otel_context = trace.SpanContext(trace_id=1220, span_id=7478)
otel_context = trace.SpanContext(
trace_id=1220, span_id=7478, is_remote=False
)
context = opentracingshim.SpanContextShim(otel_context)

headers = {}
Expand All @@ -487,7 +489,9 @@ def test_inject_http_headers(self):
def test_inject_text_map(self):
"""Test `inject()` method for Format.TEXT_MAP."""

otel_context = trace.SpanContext(trace_id=1220, span_id=7478)
otel_context = trace.SpanContext(
trace_id=1220, span_id=7478, is_remote=False
)
context = opentracingshim.SpanContextShim(otel_context)

# Verify Format.TEXT_MAP
Expand All @@ -499,7 +503,9 @@ def test_inject_text_map(self):
def test_inject_binary(self):
"""Test `inject()` method for Format.BINARY."""

otel_context = trace.SpanContext(trace_id=1220, span_id=7478)
otel_context = trace.SpanContext(
trace_id=1220, span_id=7478, is_remote=False
)
context = opentracingshim.SpanContextShim(otel_context)

# Verify exception for non supported binary format.
Expand Down Expand Up @@ -561,6 +567,7 @@ def extract(
trace.SpanContext(
trace_id=int(trace_id_list[0]),
span_id=int(span_id_list[0]),
is_remote=True,
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,16 @@ def test_translate_to_collector(self):
span_context = trace_api.SpanContext(
trace_id,
span_id,
is_remote=False,
trace_flags=TraceFlags(TraceFlags.SAMPLED),
trace_state=trace_api.TraceState({"testKey": "testValue"}),
)
parent_context = trace_api.SpanContext(trace_id, parent_id)
other_context = trace_api.SpanContext(trace_id, span_id)
parent_context = trace_api.SpanContext(
trace_id, parent_id, is_remote=False
)
other_context = trace_api.SpanContext(
trace_id, span_id, is_remote=False
)
event_attributes = {
"annotation_bool": True,
"annotation_string": "annotation_test",
Expand Down Expand Up @@ -279,7 +284,10 @@ def test_export(self):
trace_id = 0x6E0C63257DE34C926F9EFCD03927272E
span_id = 0x34BF92DEEFC58C92
span_context = trace_api.SpanContext(
trace_id, span_id, trace_flags=TraceFlags(TraceFlags.SAMPLED)
trace_id,
span_id,
is_remote=False,
trace_flags=TraceFlags(TraceFlags.SAMPLED),
)
otel_spans = [
trace.Span(
Expand Down
14 changes: 11 additions & 3 deletions ext/opentelemetry-ext-zipkin/tests/test_zipkin_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def setUp(self):
context = trace_api.SpanContext(
trace_id=0x000000000000000000000000DEADBEEF,
span_id=0x00000000DEADBEF0,
is_remote=False,
)

self._test_span = trace.Span("test_span", context=context)
Expand Down Expand Up @@ -114,10 +115,17 @@ def test_export(self):
)

span_context = trace_api.SpanContext(
trace_id, span_id, trace_flags=TraceFlags(TraceFlags.SAMPLED)
trace_id,
span_id,
is_remote=False,
trace_flags=TraceFlags(TraceFlags.SAMPLED),
)
parent_context = trace_api.SpanContext(
trace_id, parent_id, is_remote=False
)
other_context = trace_api.SpanContext(
trace_id, other_id, is_remote=False
)
parent_context = trace_api.SpanContext(trace_id, parent_id)
other_context = trace_api.SpanContext(trace_id, other_id)

event_attributes = {
"annotation_bool": True,
Expand Down
17 changes: 12 additions & 5 deletions opentelemetry-api/src/opentelemetry/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,14 @@ class SpanContext:
span_id: This span's ID.
trace_flags: Trace options to propagate.
trace_state: Tracing-system-specific info to propagate.
is_remote: True if propagated from a remote parent.
"""

def __init__(
self,
trace_id: int,
span_id: int,
is_remote: bool,
trace_flags: "TraceFlags" = DEFAULT_TRACE_OPTIONS,
trace_state: "TraceState" = DEFAULT_TRACE_STATE,
) -> None:
Expand All @@ -336,13 +338,17 @@ def __init__(
self.span_id = span_id
self.trace_flags = trace_flags
self.trace_state = trace_state
self.is_remote = is_remote

def __repr__(self) -> str:
return "{}(trace_id={}, span_id={}, trace_state={!r})".format(
return (
"{}(trace_id={}, span_id={}, trace_state={!r}, is_remote={})"
).format(
type(self).__name__,
format_trace_id(self.trace_id),
format_span_id(self.span_id),
self.trace_state,
self.is_remote,
)

def is_valid(self) -> bool:
Expand Down Expand Up @@ -402,10 +408,11 @@ def set_status(self, status: Status) -> None:
INVALID_SPAN_ID = 0x0000000000000000
INVALID_TRACE_ID = 0x00000000000000000000000000000000
INVALID_SPAN_CONTEXT = SpanContext(
INVALID_TRACE_ID,
INVALID_SPAN_ID,
DEFAULT_TRACE_OPTIONS,
DEFAULT_TRACE_STATE,
trace_id=INVALID_TRACE_ID,
span_id=INVALID_SPAN_ID,
is_remote=False,
trace_flags=DEFAULT_TRACE_OPTIONS,
trace_state=DEFAULT_TRACE_STATE,
)
INVALID_SPAN = DefaultSpan(INVALID_SPAN_CONTEXT)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ def extract(
span_context = trace.SpanContext(
trace_id=int(trace_id, 16),
span_id=int(span_id, 16),
is_remote=True,
trace_flags=trace.TraceFlags(trace_flags),
trace_state=tracestate,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def test_headers_with_tracestate(self):
self.assertEqual(
span_context.trace_state, {"foo": "1", "bar": "2", "baz": "3"}
)
self.assertTrue(span_context.is_remote)
output = {} # type:typing.Dict[str, str]
span = trace.DefaultSpan(span_context)

Expand Down Expand Up @@ -155,7 +156,7 @@ def test_no_send_empty_tracestate(self):
"""
output = {} # type:typing.Dict[str, str]
span = trace.DefaultSpan(
trace.SpanContext(self.TRACE_ID, self.SPAN_ID)
trace.SpanContext(self.TRACE_ID, self.SPAN_ID, is_remote=False)
)
ctx = set_span_in_context(span)
FORMAT.inject(dict.__setitem__, output, ctx)
Expand Down
6 changes: 5 additions & 1 deletion opentelemetry-api/tests/trace/test_defaultspan.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
class TestDefaultSpan(unittest.TestCase):
def test_ctor(self):
context = trace.SpanContext(
1, 1, trace.DEFAULT_TRACE_OPTIONS, trace.DEFAULT_TRACE_STATE
1,
1,
is_remote=False,
trace_flags=trace.DEFAULT_TRACE_OPTIONS,
trace_state=trace.DEFAULT_TRACE_STATE,
)
span = trace.DefaultSpan(context)
self.assertEqual(context, span.get_context())
Expand Down
42 changes: 32 additions & 10 deletions opentelemetry-api/tests/trace/test_sampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
class TestSampler(unittest.TestCase):
def test_always_on(self):
no_record_always_on = sampling.ALWAYS_ON.should_sample(
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_DEFAULT),
trace.SpanContext(
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_DEFAULT
),
0xDEADBEF1,
0xDEADBEF2,
"unsampled parent, sampling on",
Expand All @@ -34,7 +36,9 @@ def test_always_on(self):
self.assertEqual(no_record_always_on.attributes, {})

sampled_always_on = sampling.ALWAYS_ON.should_sample(
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_SAMPLED),
trace.SpanContext(
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_SAMPLED
),
0xDEADBEF1,
0xDEADBEF2,
"sampled parent, sampling on",
Expand All @@ -44,7 +48,9 @@ def test_always_on(self):

def test_always_off(self):
no_record_always_off = sampling.ALWAYS_OFF.should_sample(
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_DEFAULT),
trace.SpanContext(
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_DEFAULT
),
0xDEADBEF1,
0xDEADBEF2,
"unsampled parent, sampling off",
Expand All @@ -53,7 +59,9 @@ def test_always_off(self):
self.assertEqual(no_record_always_off.attributes, {})

sampled_always_on = sampling.ALWAYS_OFF.should_sample(
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_SAMPLED),
trace.SpanContext(
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_SAMPLED
),
0xDEADBEF1,
0xDEADBEF2,
"sampled parent, sampling off",
Expand All @@ -63,7 +71,9 @@ def test_always_off(self):

def test_default_on(self):
no_record_default_on = sampling.DEFAULT_ON.should_sample(
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_DEFAULT),
trace.SpanContext(
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_DEFAULT
),
0xDEADBEF1,
0xDEADBEF2,
"unsampled parent, sampling on",
Expand All @@ -72,7 +82,9 @@ def test_default_on(self):
self.assertEqual(no_record_default_on.attributes, {})

sampled_default_on = sampling.DEFAULT_ON.should_sample(
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_SAMPLED),
trace.SpanContext(
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_SAMPLED
),
0xDEADBEF1,
0xDEADBEF2,
"sampled parent, sampling on",
Expand All @@ -82,7 +94,9 @@ def test_default_on(self):

def test_default_off(self):
no_record_default_off = sampling.DEFAULT_OFF.should_sample(
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_DEFAULT),
trace.SpanContext(
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_DEFAULT
),
0xDEADBEF1,
0xDEADBEF2,
"unsampled parent, sampling off",
Expand All @@ -91,7 +105,9 @@ def test_default_off(self):
self.assertEqual(no_record_default_off.attributes, {})

sampled_default_off = sampling.DEFAULT_OFF.should_sample(
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_SAMPLED),
trace.SpanContext(
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_SAMPLED
),
0xDEADBEF1,
0xDEADBEF2,
"sampled parent, sampling off",
Expand Down Expand Up @@ -120,7 +136,10 @@ def test_probability_sampler(self):
self.assertFalse(
sampler.should_sample(
trace.SpanContext(
0xDEADBEF0, 0xDEADBEF1, trace_flags=TO_DEFAULT
0xDEADBEF0,
0xDEADBEF1,
is_remote=False,
trace_flags=TO_DEFAULT,
),
0x7FFFFFFFFFFFFFFF,
0xDEADBEEF,
Expand All @@ -130,7 +149,10 @@ def test_probability_sampler(self):
self.assertTrue(
sampler.should_sample(
trace.SpanContext(
0xDEADBEF0, 0xDEADBEF1, trace_flags=TO_SAMPLED
0xDEADBEF0,
0xDEADBEF1,
is_remote=False,
trace_flags=TO_SAMPLED,
),
0x8000000000000000,
0xDEADBEEF,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ def extract(
# trace an span ids are encoded in hex, so must be converted
trace_id=int(trace_id, 16),
span_id=int(span_id, 16),
is_remote=True,
trace_flags=trace.TraceFlags(options),
trace_state=trace.TraceState(),
)
Expand Down
6 changes: 5 additions & 1 deletion opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,11 @@ def start_span( # pylint: disable=too-many-locals
trace_state = parent_context.trace_state

context = trace_api.SpanContext(
trace_id, generate_span_id(), trace_flags, trace_state
trace_id,
generate_span_id(),
is_remote=False,
trace_flags=trace_flags,
trace_state=trace_state,
)

# The sampler decides whether to create a real or no-op span at the
Expand Down
4 changes: 4 additions & 0 deletions opentelemetry-sdk/tests/context/propagation/test_b3_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def get_child_parent_new_carrier(old_carrier):
trace_api.SpanContext(
parent_context.trace_id,
trace.generate_span_id(),
is_remote=False,
trace_flags=parent_context.trace_flags,
trace_state=parent_context.trace_state,
),
Expand Down Expand Up @@ -90,6 +91,7 @@ def test_extract_multi_header(self):
new_carrier[FORMAT.PARENT_SPAN_ID_KEY],
b3_format.format_span_id(parent.context.span_id),
)
self.assertTrue(parent.context.is_remote)
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")

def test_extract_single_header(self):
Expand All @@ -111,6 +113,7 @@ def test_extract_single_header(self):
b3_format.format_span_id(child.context.span_id),
)
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")
self.assertTrue(parent.context.is_remote)

child, parent, new_carrier = get_child_parent_new_carrier(
{
Expand All @@ -134,6 +137,7 @@ def test_extract_single_header(self):
new_carrier[FORMAT.PARENT_SPAN_ID_KEY],
b3_format.format_span_id(parent.context.span_id),
)
self.assertTrue(parent.context.is_remote)
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")

def test_extract_header_precedence(self):
Expand Down

0 comments on commit 144ab39

Please sign in to comment.