Skip to content

Commit

Permalink
Adds trace_propagation_targets option (#1916)
Browse files Browse the repository at this point in the history
Add an option trace_propagation_targets that defines to what targets the trace headers (sentry-trace and baggage) are added in outgoing HTTP requests.
  • Loading branch information
antonpirker authored Feb 27, 2023
1 parent 5306eab commit 04cfc86
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 23 deletions.
5 changes: 5 additions & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
DEFAULT_QUEUE_SIZE = 100
DEFAULT_MAX_BREADCRUMBS = 100

MATCH_ALL = r".*"


class INSTRUMENTER:
SENTRY = "sentry"
Expand Down Expand Up @@ -123,6 +125,9 @@ def __init__(
before_send_transaction=None, # type: Optional[TransactionProcessor]
project_root=None, # type: Optional[str]
enable_tracing=None, # type: Optional[bool]
trace_propagation_targets=[ # noqa: B006
MATCH_ALL
], # type: Optional[Sequence[str]]
):
# type: (...) -> None
pass
Expand Down
29 changes: 17 additions & 12 deletions sentry_sdk/integrations/httpx.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from sentry_sdk import Hub
from sentry_sdk.consts import OP
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.tracing_utils import should_propagate_trace
from sentry_sdk.utils import logger, parse_url

from sentry_sdk._types import MYPY
Expand Down Expand Up @@ -52,13 +53,15 @@ def send(self, request, **kwargs):
span.set_data("http.query", parsed_url.query)
span.set_data("http.fragment", parsed_url.fragment)

for key, value in hub.iter_trace_propagation_headers():
logger.debug(
"[Tracing] Adding `{key}` header {value} to outgoing request to {url}.".format(
key=key, value=value, url=request.url
if should_propagate_trace(hub, str(request.url)):
for key, value in hub.iter_trace_propagation_headers():
logger.debug(
"[Tracing] Adding `{key}` header {value} to outgoing request to {url}.".format(
key=key, value=value, url=request.url
)
)
)
request.headers[key] = value
request.headers[key] = value

rv = real_send(self, request, **kwargs)

span.set_data("status_code", rv.status_code)
Expand Down Expand Up @@ -91,13 +94,15 @@ async def send(self, request, **kwargs):
span.set_data("http.query", parsed_url.query)
span.set_data("http.fragment", parsed_url.fragment)

for key, value in hub.iter_trace_propagation_headers():
logger.debug(
"[Tracing] Adding `{key}` header {value} to outgoing request to {url}.".format(
key=key, value=value, url=request.url
if should_propagate_trace(hub, str(request.url)):
for key, value in hub.iter_trace_propagation_headers():
logger.debug(
"[Tracing] Adding `{key}` header {value} to outgoing request to {url}.".format(
key=key, value=value, url=request.url
)
)
)
request.headers[key] = value
request.headers[key] = value

rv = await real_send(self, request, **kwargs)

span.set_data("status_code", rv.status_code)
Expand Down
15 changes: 8 additions & 7 deletions sentry_sdk/integrations/stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from sentry_sdk.hub import Hub
from sentry_sdk.integrations import Integration
from sentry_sdk.scope import add_global_event_processor
from sentry_sdk.tracing_utils import EnvironHeaders
from sentry_sdk.tracing_utils import EnvironHeaders, should_propagate_trace
from sentry_sdk.utils import (
capture_internal_exceptions,
logger,
Expand Down Expand Up @@ -98,13 +98,14 @@ def putrequest(self, method, url, *args, **kwargs):

rv = real_putrequest(self, method, url, *args, **kwargs)

for key, value in hub.iter_trace_propagation_headers(span):
logger.debug(
"[Tracing] Adding `{key}` header {value} to outgoing request to {real_url}.".format(
key=key, value=value, real_url=real_url
if should_propagate_trace(hub, real_url):
for key, value in hub.iter_trace_propagation_headers(span):
logger.debug(
"[Tracing] Adding `{key}` header {value} to outgoing request to {real_url}.".format(
key=key, value=value, real_url=real_url
)
)
)
self.putheader(key, value)
self.putheader(key, value)

self._sentrysdk_span = span

Expand Down
23 changes: 21 additions & 2 deletions sentry_sdk/tracing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
if MYPY:
import typing

from typing import Generator
from typing import Optional
from typing import Any
from typing import Dict
from typing import Generator
from typing import Optional
from typing import Union


Expand Down Expand Up @@ -376,6 +376,25 @@ def serialize(self, include_third_party=False):
return ",".join(items)


def should_propagate_trace(hub, url):
# type: (sentry_sdk.Hub, str) -> bool
"""
Returns True if url matches trace_propagation_targets configured in the given hub. Otherwise, returns False.
"""
client = hub.client # type: Any
trace_propagation_targets = client.options["trace_propagation_targets"]

if trace_propagation_targets is None:
return False

for target in trace_propagation_targets:
matched = re.search(target, url)
if matched:
return True

return False


# Circular imports
from sentry_sdk.tracing import LOW_QUALITY_TRANSACTION_SOURCES

Expand Down
144 changes: 144 additions & 0 deletions tests/integrations/httpx/test_httpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import responses

from sentry_sdk import capture_message, start_transaction
from sentry_sdk.consts import MATCH_ALL
from sentry_sdk.integrations.httpx import HttpxIntegration


Expand Down Expand Up @@ -81,3 +82,146 @@ def test_outgoing_trace_headers(sentry_init, httpx_client):
parent_span_id=request_span.span_id,
sampled=1,
)


@pytest.mark.parametrize(
"httpx_client,trace_propagation_targets,url,trace_propagated",
[
[
httpx.Client(),
None,
"https://example.com/",
False,
],
[
httpx.Client(),
[],
"https://example.com/",
False,
],
[
httpx.Client(),
[MATCH_ALL],
"https://example.com/",
True,
],
[
httpx.Client(),
["https://example.com/"],
"https://example.com/",
True,
],
[
httpx.Client(),
["https://example.com/"],
"https://example.com",
False,
],
[
httpx.Client(),
["https://example.com"],
"https://example.com",
True,
],
[
httpx.Client(),
["https://example.com", r"https?:\/\/[\w\-]+(\.[\w\-]+)+\.net"],
"https://example.net",
False,
],
[
httpx.Client(),
["https://example.com", r"https?:\/\/[\w\-]+(\.[\w\-]+)+\.net"],
"https://good.example.net",
True,
],
[
httpx.Client(),
["https://example.com", r"https?:\/\/[\w\-]+(\.[\w\-]+)+\.net"],
"https://good.example.net/some/thing",
True,
],
[
httpx.AsyncClient(),
None,
"https://example.com/",
False,
],
[
httpx.AsyncClient(),
[],
"https://example.com/",
False,
],
[
httpx.AsyncClient(),
[MATCH_ALL],
"https://example.com/",
True,
],
[
httpx.AsyncClient(),
["https://example.com/"],
"https://example.com/",
True,
],
[
httpx.AsyncClient(),
["https://example.com/"],
"https://example.com",
False,
],
[
httpx.AsyncClient(),
["https://example.com"],
"https://example.com",
True,
],
[
httpx.AsyncClient(),
["https://example.com", r"https?:\/\/[\w\-]+(\.[\w\-]+)+\.net"],
"https://example.net",
False,
],
[
httpx.AsyncClient(),
["https://example.com", r"https?:\/\/[\w\-]+(\.[\w\-]+)+\.net"],
"https://good.example.net",
True,
],
[
httpx.AsyncClient(),
["https://example.com", r"https?:\/\/[\w\-]+(\.[\w\-]+)+\.net"],
"https://good.example.net/some/thing",
True,
],
],
)
def test_option_trace_propagation_targets(
sentry_init,
httpx_client,
httpx_mock, # this comes from pytest-httpx
trace_propagation_targets,
url,
trace_propagated,
):
httpx_mock.add_response()

sentry_init(
release="test",
trace_propagation_targets=trace_propagation_targets,
traces_sample_rate=1.0,
integrations=[HttpxIntegration()],
)

if asyncio.iscoroutinefunction(httpx_client.get):
asyncio.get_event_loop().run_until_complete(httpx_client.get(url))
else:
httpx_client.get(url)

request_headers = httpx_mock.get_request().headers

if trace_propagated:
assert "sentry-trace" in request_headers
else:
assert "sentry-trace" not in request_headers
Loading

0 comments on commit 04cfc86

Please sign in to comment.