Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
14 changes: 9 additions & 5 deletions ddtrace/internal/utils/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ def _connector_context() -> Generator[HTTPConnection, None, None]:
return _connector_context


_W3C_DD_LIST_MEMBER_MAX_CHARS = 256
Comment thread
julinvictus marked this conversation as resolved.
Outdated
_W3C_DD_LIST_MEMBER_RESERVED_LEN = len("dd=") + len(f"{W3C_TRACESTATE_PARENT_ID_KEY}:{0:016x};")


def w3c_get_dd_list_member(context):
# Context -> str
tags = []
Expand All @@ -195,7 +199,7 @@ def w3c_get_dd_list_member(context):
if usr_id:
tags.append("t.usr.id:{}".format(w3c_encode_tag((_W3C_TRACESTATE_INVALID_CHARS_REGEX_VALUE, "_", usr_id))))

current_tags_len = sum(len(i) for i in tags)
current_tags_len = _W3C_DD_LIST_MEMBER_RESERVED_LEN + len(";".join(tags))
for k, v in _get_metas_to_propagate(context):
if k not in [SAMPLING_DECISION_TRACE_TAG_KEY, _USER_ID_KEY]:
# for key replace ",", "=", and characters outside the ASCII range 0x20 to 0x7E
Expand All @@ -205,11 +209,11 @@ def w3c_get_dd_list_member(context):
w3c_encode_tag((_W3C_TRACESTATE_INVALID_CHARS_REGEX_KEY, "_", k)),
w3c_encode_tag((_W3C_TRACESTATE_INVALID_CHARS_REGEX_VALUE, "_", v)),
)
# we need to keep the total length under 256 char
potential_current_tags_len = current_tags_len + len(next_tag)
if not potential_current_tags_len > 256:
# account for ; before next tag entry
next_tag_len = len(next_tag) + (1 if tags else 0)
Comment thread
julinvictus marked this conversation as resolved.
if current_tags_len + next_tag_len <= _W3C_DD_LIST_MEMBER_MAX_CHARS:
tags.append(next_tag)
current_tags_len += len(next_tag)
current_tags_len += next_tag_len
else:
log.debug("tracestate would exceed 256 char limit with tag: %s. Tag will not be added.", next_tag)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
tracing: Fixes an issue where the ``dd=`` list-member of the W3C ``tracestate`` header could exceed
256 characters, because the length check left out the ``dd=`` prefix, the ``;`` separators, and the
``p:`` parent id field. Proxies that enforce the W3C limit could drop the whole ``tracestate``
header. Propagated tags that do not fit are now dropped whole.
Comment thread
julinvictus marked this conversation as resolved.
Outdated
32 changes: 32 additions & 0 deletions tests/tracer/test_propagation.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,38 @@ def test_inject_deep_child_propagates_trace_level_tags(tracer): # noqa: F811
assert "_dd.p.other=value" in tags


def test_inject_tracestate_dd_member_within_256_chars(tracer): # noqa: F811
"""The injected dd= list-member, including the "dd=" prefix, ";" separators and the
"p:" field added at injection, must not exceed 256 characters.
"""
meta = {
"_dd.p.llmobs_parent_id": "10707708093473052057",
"_dd.p.llmobs_trace_id": "141883241939442010658227520463581072062",
"_dd.p.llmobs_ml_app": "datasciencetiger",
"_dd.p.llmobs_sid": "11a0eec0-ad0a-4f4d-8ae8-7f358ed1b153",
"_dd.p.llmobs_sr": "1",
"_dd.p.llmobs_sd": "1",
"_dd.p.llmobs_pagent_span_id": "14260131734498464268",
"_dd.p.llmobs_pagent_name": "datasciencetiger-orchestrator",
}
ctx = Context(trace_id=1234, sampling_priority=1, dd_origin="rum", meta=meta)
tracer.context_provider.activate(ctx)
with tracer.trace("global_root_span") as span:
headers = {}
HTTPPropagator.inject(span.context, headers)

dd_members = [m for m in headers[_HTTP_HEADER_TRACESTATE].split(",") if m.startswith("dd=")]
assert len(dd_members) == 1
dd_member = dd_members[0]
assert len(dd_member) <= 256, dd_member
assert dd_member.startswith(f"dd=p:{span.span_id:016x};s:1;o:rum;")
# Only whole tags are dropped, never truncated
for tag in dd_member[len("dd=") :].split(";"):
if tag.startswith("t.llmobs_"):
key, _, value = tag.partition(":")
assert meta[key.replace("t.", "_dd.p.", 1)] == value


def test_inject_with_baggage_http_propagation(tracer): # noqa: F811
with override_global_config(dict(_propagation_http_baggage_enabled=True)):
ctx = Context(trace_id=1234, sampling_priority=2, dd_origin="synthetics")
Expand Down
Loading