Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/5870.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`opentelemetry-propagator-b3`: preserve and re-propagate a received debug flag
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing_extensions import deprecated

from opentelemetry import trace
from opentelemetry.context import Context
from opentelemetry.context import Context, create_key, get_value, set_value
from opentelemetry.propagators.textmap import (
CarrierT,
Getter,
Expand All @@ -18,6 +18,8 @@
)
from opentelemetry.trace import format_span_id, format_trace_id

_DEBUG_FLAG_KEY = create_key("b3-debug-flag")


class B3MultiFormat(TextMapPropagator):
"""Propagator for the B3 HTTP multi-header format.
Expand All @@ -32,6 +34,8 @@ class B3MultiFormat(TextMapPropagator):
SAMPLED_KEY = "x-b3-sampled"
FLAGS_KEY = "x-b3-flags"
_SAMPLE_PROPAGATE_VALUES = {"1", "True", "true", "d"}
_DEBUG_SAMPLED_VALUE = "d"
_DEBUG_FLAGS_VALUE = "1"
_trace_id_regex = re_compile(r"[\da-fA-F]{16}|[\da-fA-F]{32}")
_span_id_regex = re_compile(r"[\da-fA-F]{16}")

Expand Down Expand Up @@ -64,18 +68,30 @@ def extract(
trace_id, span_id, sampled = fields
elif len(fields) == 4:
trace_id, span_id, sampled, _ = fields

debug = sampled == self._DEBUG_SAMPLED_VALUE
sampling_state_alone = len(fields) == 1
else:
trace_id = _extract_first_element(getter.get(carrier, self.TRACE_ID_KEY)) or trace_id
span_id = _extract_first_element(getter.get(carrier, self.SPAN_ID_KEY)) or span_id
sampled = _extract_first_element(getter.get(carrier, self.SAMPLED_KEY)) or sampled
flags = _extract_first_element(getter.get(carrier, self.FLAGS_KEY)) or flags
trace_id = _extract_first_element(getter.get(carrier, self.TRACE_ID_KEY))
span_id = _extract_first_element(getter.get(carrier, self.SPAN_ID_KEY))
sampled = _extract_first_element(getter.get(carrier, self.SAMPLED_KEY))
flags = _extract_first_element(getter.get(carrier, self.FLAGS_KEY))

debug = flags == self._DEBUG_FLAGS_VALUE
sampling_state_alone = trace_id is None and span_id is None and bool(sampled or flags)

trace_id = trace.INVALID_TRACE_ID if trace_id is None else trace_id
span_id = trace.INVALID_SPAN_ID if span_id is None else span_id
sampled = "0" if sampled is None else sampled

if (
trace_id == trace.INVALID_TRACE_ID
or span_id == trace.INVALID_SPAN_ID
or self._trace_id_regex.fullmatch(trace_id) is None
or self._span_id_regex.fullmatch(span_id) is None
):
if sampling_state_alone:
context = set_value(_DEBUG_FLAG_KEY, debug, context)
return context

trace_id = int(trace_id, 16)
Expand All @@ -88,6 +104,8 @@ def extract(
if sampled in self._SAMPLE_PROPAGATE_VALUES or flags == "1":
options |= trace.TraceFlags.SAMPLED

context = set_value(_DEBUG_FLAG_KEY, debug, context)

return trace.set_span_in_context(
trace.NonRecordingSpan(
trace.SpanContext(
Expand Down Expand Up @@ -121,14 +139,18 @@ def inject(
format_trace_id(span_context.trace_id),
)
setter.set(carrier, self.SPAN_ID_KEY, format_span_id(span_context.span_id))
setter.set(carrier, self.SAMPLED_KEY, "1" if sampled else "0")
if get_value(_DEBUG_FLAG_KEY, context) is True:
setter.set(carrier, self.FLAGS_KEY, self._DEBUG_FLAGS_VALUE)
else:
setter.set(carrier, self.SAMPLED_KEY, "1" if sampled else "0")

@property
def fields(self) -> set[str]:
return {
self.TRACE_ID_KEY,
self.SPAN_ID_KEY,
self.SAMPLED_KEY,
self.FLAGS_KEY,
}


Expand All @@ -153,10 +175,15 @@ def inject(

sampled = (trace.TraceFlags.SAMPLED & span_context.trace_flags) != 0

if get_value(_DEBUG_FLAG_KEY, context) is True:
sampling_state = self._DEBUG_SAMPLED_VALUE
else:
sampling_state = "1" if sampled else "0"

fields = [
format_trace_id(span_context.trace_id),
format_span_id(span_context.span_id),
"1" if sampled else "0",
sampling_state,
]

setter.set(carrier, self.SINGLE_HEADER_KEY, "-".join(fields))
Expand Down
Loading
Loading