diff --git a/ext/opentelemetry-ext-jaeger/tests/test_jaeger_exporter.py b/ext/opentelemetry-ext-jaeger/tests/test_jaeger_exporter.py index e8ad734e0e0..eac90455ade 100644 --- a/ext/opentelemetry-ext-jaeger/tests/test_jaeger_exporter.py +++ b/ext/opentelemetry-ext-jaeger/tests/test_jaeger_exporter.py @@ -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) @@ -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, diff --git a/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py b/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py index 12c792d4e65..9f62b90ddf1 100644 --- a/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py +++ b/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py @@ -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) @@ -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 = {} @@ -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 @@ -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. @@ -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, ) ) ) diff --git a/ext/opentelemetry-ext-otcollector/tests/test_otcollector_trace_exporter.py b/ext/opentelemetry-ext-otcollector/tests/test_otcollector_trace_exporter.py index a731626b08a..636639bb1a3 100644 --- a/ext/opentelemetry-ext-otcollector/tests/test_otcollector_trace_exporter.py +++ b/ext/opentelemetry-ext-otcollector/tests/test_otcollector_trace_exporter.py @@ -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", @@ -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( diff --git a/ext/opentelemetry-ext-zipkin/tests/test_zipkin_exporter.py b/ext/opentelemetry-ext-zipkin/tests/test_zipkin_exporter.py index 34183e923b4..22c64b80152 100644 --- a/ext/opentelemetry-ext-zipkin/tests/test_zipkin_exporter.py +++ b/ext/opentelemetry-ext-zipkin/tests/test_zipkin_exporter.py @@ -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) @@ -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, diff --git a/opentelemetry-api/src/opentelemetry/trace/__init__.py b/opentelemetry-api/src/opentelemetry/trace/__init__.py index 4ea2ebd745f..69cfb157d67 100644 --- a/opentelemetry-api/src/opentelemetry/trace/__init__.py +++ b/opentelemetry-api/src/opentelemetry/trace/__init__.py @@ -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: @@ -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: @@ -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) diff --git a/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontexthttptextformat.py b/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontexthttptextformat.py index 46765011318..732ce96c665 100644 --- a/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontexthttptextformat.py +++ b/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontexthttptextformat.py @@ -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, ) diff --git a/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py b/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py index f3c97aae015..11a8ecd56e9 100644 --- a/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py +++ b/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py @@ -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) @@ -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) diff --git a/opentelemetry-api/tests/trace/test_defaultspan.py b/opentelemetry-api/tests/trace/test_defaultspan.py index b6595579f9b..d27f2b1bbcd 100644 --- a/opentelemetry-api/tests/trace/test_defaultspan.py +++ b/opentelemetry-api/tests/trace/test_defaultspan.py @@ -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()) diff --git a/opentelemetry-api/tests/trace/test_sampling.py b/opentelemetry-api/tests/trace/test_sampling.py index 3b5f1dc6fc1..0be222f3dc0 100644 --- a/opentelemetry-api/tests/trace/test_sampling.py +++ b/opentelemetry-api/tests/trace/test_sampling.py @@ -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", @@ -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", @@ -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", @@ -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", @@ -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", @@ -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", @@ -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", @@ -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", @@ -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, @@ -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, diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py b/opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py index 6b8d16bf563..e082ed03e4e 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py @@ -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(), ) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index 94efa160116..6211d1878ff 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -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 diff --git a/opentelemetry-sdk/tests/context/propagation/test_b3_format.py b/opentelemetry-sdk/tests/context/propagation/test_b3_format.py index 75b3f562b53..8f06912b9ec 100644 --- a/opentelemetry-sdk/tests/context/propagation/test_b3_format.py +++ b/opentelemetry-sdk/tests/context/propagation/test_b3_format.py @@ -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, ), @@ -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): @@ -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( { @@ -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): diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py index 3c5d1f1a581..acf7e66b44f 100644 --- a/opentelemetry-sdk/tests/trace/test_trace.py +++ b/opentelemetry-sdk/tests/trace/test_trace.py @@ -268,6 +268,7 @@ def test_start_span_explicit(self): other_parent = trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x00000000DEADBEF0, + is_remote=False, trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ) @@ -337,6 +338,7 @@ def test_start_as_current_span_explicit(self): other_parent = trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x00000000DEADBEF0, + is_remote=False, ) self.assertIsNone(tracer.get_current_span()) @@ -378,6 +380,12 @@ def test_default_span_resource(self): # pylint: disable=protected-access self.assertIs(span.resource, resources._EMPTY_RESOURCE) + def test_span_context_remote_flag(self): + tracer = new_tracer() + + span = tracer.start_span("foo") + self.assertFalse(span.context.is_remote) + class TestSpan(unittest.TestCase): def setUp(self): @@ -564,14 +572,17 @@ def test_links(self): other_context1 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), + is_remote=False, ) other_context2 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), + is_remote=False, ) other_context3 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), + is_remote=False, ) links = [ trace_api.Link(other_context1),