diff --git a/src/instana/propagators/http_propagator.py b/src/instana/propagators/http_propagator.py index 76ca3114..c6491076 100644 --- a/src/instana/propagators/http_propagator.py +++ b/src/instana/propagators/http_propagator.py @@ -5,6 +5,7 @@ from instana.log import logger from instana.propagators.base_propagator import BasePropagator from instana.util.ids import define_server_timing, hex_id_limited +from instana.span_context import SpanContext from opentelemetry.trace.span import format_span_id @@ -27,7 +28,26 @@ def inject(self, span_context, carrier, disable_w3c_trace_context=False): # Suppression `level` made in the child context or in the parent context # has priority over any non-suppressed `level` setting child_level = int(self.extract_instana_headers(dictionary_carrier)[2] or "1") - span_context.level = min(child_level, span_context.level) + new_level = min(child_level, span_context.level) + + if new_level != span_context.level: + # Create a new span context with the updated level + span_context = SpanContext( + trace_id=span_context.trace_id, + span_id=span_context.span_id, + is_remote=span_context.is_remote, + trace_flags=span_context.trace_flags, + trace_state=span_context.trace_state, + level=new_level, + synthetic=span_context.synthetic, + trace_parent=span_context.trace_parent, + instana_ancestor=span_context.instana_ancestor, + long_trace_id=span_context.long_trace_id, + correlation_type=span_context.correlation_type, + correlation_id=span_context.correlation_id, + traceparent=span_context.traceparent, + tracestate=span_context.tracestate + ) serializable_level = str(span_context.level) diff --git a/tests/propagators/test_http_propagator.py b/tests/propagators/test_http_propagator.py index 25b36635..bac0a173 100644 --- a/tests/propagators/test_http_propagator.py +++ b/tests/propagators/test_http_propagator.py @@ -340,3 +340,45 @@ def test_w3c_off_x_instana_l_0( if "tracestate" in carrier_header.keys(): assert "tracestate" in downstream_carrier assert carrier_header["tracestate"] == downstream_carrier["tracestate"] + + def test_suppression_when_child_level_is_lower( + self, + _trace_id: int, + _span_id: int, + ) -> None: + """ + Test that span_context.level is updated when the child level (extracted from carrier) is lower than the current span_context.level. + """ + # Create a span context with level=1 + original_span_context = SpanContext( + trace_id=_trace_id, + span_id=_span_id, + is_remote=False, + level=1, + ) + + # Create a carrier with level=0 (suppression) + carrier_header = {"x-instana-l": "0"} + + # Inject the span context into the carrier + self.hptc.inject(original_span_context, carrier_header) + + # Extract the span context from the carrier to verify the level was updated + extracted_context = self.hptc.extract(carrier_header) + + # Verify that the level is 0 (suppressed) + assert extracted_context.level == 0 + assert extracted_context.suppression + + # Create a new carrier to test the propagation + downstream_carrier = {} + + # Inject the extracted context into the downstream carrier + self.hptc.inject(extracted_context, downstream_carrier) + + # Verify that the downstream carrier has the correct level + assert downstream_carrier.get("X-INSTANA-L") == "0" + + # Verify that no trace or span IDs are injected when suppressed + assert "X-INSTANA-T" not in downstream_carrier + assert "X-INSTANA-S" not in downstream_carrier