Skip to content

fix(httpx): Prevent Sentry baggage duplication #3728

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
29 changes: 23 additions & 6 deletions sentry_sdk/integrations/httpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from sentry_sdk.consts import OP, SPANDATA
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME
from sentry_sdk.tracing_utils import should_propagate_trace
from sentry_sdk.tracing_utils import Baggage, should_propagate_trace
from sentry_sdk.utils import (
SENSITIVE_DATA_SUBSTITUTE,
capture_internal_exceptions,
Expand All @@ -14,6 +14,7 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from collections.abc import MutableMapping
from typing import Any


Expand Down Expand Up @@ -76,11 +77,9 @@ def send(self, request, **kwargs):
key=key, value=value, url=request.url
)
)
if key == BAGGAGE_HEADER_NAME and request.headers.get(
BAGGAGE_HEADER_NAME
):
# do not overwrite any existing baggage, just append to it
request.headers[key] += "," + value

if key == BAGGAGE_HEADER_NAME:
_add_sentry_baggage_to_headers(request.headers, value)
else:
request.headers[key] = value

Expand Down Expand Up @@ -148,3 +147,21 @@ async def send(self, request, **kwargs):
return rv

AsyncClient.send = send


def _add_sentry_baggage_to_headers(headers, sentry_baggage):
# type: (MutableMapping[str, str], str) -> None
"""Add the Sentry baggage to the headers.

This function directly mutates the provided headers. The provided sentry_baggage
is appended to the existing baggage. If the baggage already contains Sentry items,
they are stripped out first.
"""
existing_baggage = headers.get(BAGGAGE_HEADER_NAME, "")
stripped_existing_baggage = Baggage.strip_sentry_baggage(existing_baggage)

separator = "," if len(stripped_existing_baggage) > 0 else ""

headers[BAGGAGE_HEADER_NAME] = (
stripped_existing_baggage + separator + sentry_baggage
)
15 changes: 15 additions & 0 deletions sentry_sdk/tracing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,21 @@ def serialize(self, include_third_party=False):

return ",".join(items)

@staticmethod
def strip_sentry_baggage(header):
# type: (str) -> str
"""Remove Sentry baggage from the given header.

Given a Baggage header, return a new Baggage header with all Sentry baggage items removed.
"""
return ",".join(
(
item
for item in header.split(",")
if not Baggage.SENTRY_PREFIX_REGEX.match(item.strip())
)
)


def should_propagate_trace(client, url):
# type: (sentry_sdk.client.BaseClient, str) -> bool
Expand Down
23 changes: 22 additions & 1 deletion tests/test_tracing_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from dataclasses import asdict, dataclass
from typing import Optional, List

from sentry_sdk.tracing_utils import _should_be_included
from sentry_sdk.tracing_utils import _should_be_included, Baggage
import pytest


Expand Down Expand Up @@ -94,3 +94,24 @@ def test_should_be_included(test_case, expected):
kwargs = asdict(test_case)
kwargs.pop("id")
assert _should_be_included(**kwargs) == expected


@pytest.mark.parametrize(
("header", "expected"),
(
("", ""),
("foo=bar", "foo=bar"),
(" foo=bar, baz = qux ", " foo=bar, baz = qux "),
("sentry-trace_id=123", ""),
(" sentry-trace_id = 123 ", ""),
("sentry-trace_id=123,sentry-public_key=456", ""),
("foo=bar,sentry-trace_id=123", "foo=bar"),
("foo=bar,sentry-trace_id=123,baz=qux", "foo=bar,baz=qux"),
(
"foo=bar,sentry-trace_id=123,baz=qux,sentry-public_key=456",
"foo=bar,baz=qux",
),
),
)
def test_strip_sentry_baggage(header, expected):
assert Baggage.strip_sentry_baggage(header) == expected
Loading