From 625e1b3608862f68295006edee00d0d0916787f2 Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Tue, 27 Jun 2023 11:21:00 +0200 Subject: [PATCH 01/11] Do not overwrite existing baggage on outgoing requests (#2191) --- sentry_sdk/integrations/celery.py | 19 ++++++++++- sentry_sdk/integrations/httpx.py | 17 ++++++++-- tests/integrations/celery/test_celery.py | 42 +++++++++++++++++------- tests/integrations/httpx/test_httpx.py | 40 ++++++++++++++++++++++ 4 files changed, 103 insertions(+), 15 deletions(-) diff --git a/sentry_sdk/integrations/celery.py b/sentry_sdk/integrations/celery.py index 741a2c8bb7..443fcdad45 100644 --- a/sentry_sdk/integrations/celery.py +++ b/sentry_sdk/integrations/celery.py @@ -11,7 +11,7 @@ from sentry_sdk.hub import Hub from sentry_sdk.integrations import Integration, DidNotEnable from sentry_sdk.integrations.logging import ignore_logger -from sentry_sdk.tracing import TRANSACTION_SOURCE_TASK +from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, TRANSACTION_SOURCE_TASK from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.utils import ( capture_internal_exceptions, @@ -158,7 +158,20 @@ def apply_async(*args, **kwargs): # Note: kwargs can contain headers=None, so no setdefault! # Unsure which backend though. kwarg_headers = kwargs.get("headers") or {} + + existing_baggage = kwarg_headers.get(BAGGAGE_HEADER_NAME) + sentry_baggage = headers.get(BAGGAGE_HEADER_NAME) + + combined_baggage = sentry_baggage or existing_baggage + if sentry_baggage and existing_baggage: + combined_baggage = "{},{}".format( + existing_baggage, + sentry_baggage, + ) + kwarg_headers.update(headers) + if combined_baggage: + kwarg_headers[BAGGAGE_HEADER_NAME] = combined_baggage # https://github.com/celery/celery/issues/4875 # @@ -166,6 +179,10 @@ def apply_async(*args, **kwargs): # tracing tools (dd-trace-py) also employ this exact # workaround and we don't want to break them. kwarg_headers.setdefault("headers", {}).update(headers) + if combined_baggage: + kwarg_headers["headers"][ + BAGGAGE_HEADER_NAME + ] = combined_baggage # Add the Sentry options potentially added in `sentry_apply_entry` # to the headers (done when auto-instrumenting Celery Beat tasks) diff --git a/sentry_sdk/integrations/httpx.py b/sentry_sdk/integrations/httpx.py index e84a28d165..04db5047b4 100644 --- a/sentry_sdk/integrations/httpx.py +++ b/sentry_sdk/integrations/httpx.py @@ -1,6 +1,7 @@ from sentry_sdk import Hub 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.utils import ( SENSITIVE_DATA_SUBSTITUTE, @@ -72,7 +73,13 @@ def send(self, request, **kwargs): key=key, value=value, url=request.url ) ) - request.headers[key] = value + 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 + else: + request.headers[key] = value rv = real_send(self, request, **kwargs) @@ -119,7 +126,13 @@ async def send(self, request, **kwargs): key=key, value=value, url=request.url ) ) - request.headers[key] = value + 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 + else: + request.headers[key] = value rv = await real_send(self, request, **kwargs) diff --git a/tests/integrations/celery/test_celery.py b/tests/integrations/celery/test_celery.py index d120d34a12..304f6c2f04 100644 --- a/tests/integrations/celery/test_celery.py +++ b/tests/integrations/celery/test_celery.py @@ -11,7 +11,6 @@ from celery import Celery, VERSION from celery.bin import worker -from celery.signals import task_success try: from unittest import mock # python 3.3 and above @@ -360,7 +359,7 @@ def dummy_task(self): # TODO: This test is hanging when running test with `tox --parallel auto`. Find out why and fix it! @pytest.mark.skip @pytest.mark.forked -def test_redis_backend_trace_propagation(init_celery, capture_events_forksafe, tmpdir): +def test_redis_backend_trace_propagation(init_celery, capture_events_forksafe): celery = init_celery(traces_sample_rate=1.0, backend="redis", debug=True) events = capture_events_forksafe() @@ -493,17 +492,36 @@ def test_task_headers(celery): "sentry-monitor-check-in-id": "123abc", } - @celery.task(name="dummy_task") - def dummy_task(x, y): - return x + y - - def crons_task_success(sender, **kwargs): - headers = _get_headers(sender) - assert headers == sentry_crons_setup - - task_success.connect(crons_task_success) + @celery.task(name="dummy_task", bind=True) + def dummy_task(self, x, y): + return _get_headers(self) # This is how the Celery Beat auto-instrumentation starts a task # in the monkey patched version of `apply_async` # in `sentry_sdk/integrations/celery.py::_wrap_apply_async()` - dummy_task.apply_async(args=(1, 0), headers=sentry_crons_setup) + result = dummy_task.apply_async(args=(1, 0), headers=sentry_crons_setup) + assert result.get() == sentry_crons_setup + + +def test_baggage_propagation(init_celery): + celery = init_celery(traces_sample_rate=1.0, release="abcdef") + + @celery.task(name="dummy_task", bind=True) + def dummy_task(self, x, y): + return _get_headers(self) + + with start_transaction() as transaction: + result = dummy_task.apply_async( + args=(1, 0), + headers={"baggage": "custom=value"}, + ).get() + + assert sorted(result["baggage"].split(",")) == sorted( + [ + "sentry-release=abcdef", + "sentry-trace_id={}".format(transaction.trace_id), + "sentry-environment=production", + "sentry-sample_rate=1.0", + "custom=value", + ] + ) diff --git a/tests/integrations/httpx/test_httpx.py b/tests/integrations/httpx/test_httpx.py index 72188a23e3..9b7842fbb7 100644 --- a/tests/integrations/httpx/test_httpx.py +++ b/tests/integrations/httpx/test_httpx.py @@ -89,6 +89,46 @@ def test_outgoing_trace_headers(sentry_init, httpx_client): ) +@pytest.mark.parametrize( + "httpx_client", + (httpx.Client(), httpx.AsyncClient()), +) +def test_outgoing_trace_headers_append_to_baggage(sentry_init, httpx_client): + sentry_init( + traces_sample_rate=1.0, + integrations=[HttpxIntegration()], + release="d08ebdb9309e1b004c6f52202de58a09c2268e42", + ) + + url = "http://example.com/" + responses.add(responses.GET, url, status=200) + + with start_transaction( + name="/interactions/other-dogs/new-dog", + op="greeting.sniff", + trace_id="01234567890123456789012345678901", + ) as transaction: + if asyncio.iscoroutinefunction(httpx_client.get): + response = asyncio.get_event_loop().run_until_complete( + httpx_client.get(url, headers={"baGGage": "custom=data"}) + ) + else: + response = httpx_client.get(url, headers={"baGGage": "custom=data"}) + + request_span = transaction._span_recorder.spans[-1] + assert response.request.headers[ + "sentry-trace" + ] == "{trace_id}-{parent_span_id}-{sampled}".format( + trace_id=transaction.trace_id, + parent_span_id=request_span.span_id, + sampled=1, + ) + assert ( + response.request.headers["baggage"] + == "custom=data,sentry-trace_id=01234567890123456789012345678901,sentry-environment=production,sentry-release=d08ebdb9309e1b004c6f52202de58a09c2268e42,sentry-transaction=/interactions/other-dogs/new-dog,sentry-sample_rate=1.0" + ) + + @pytest.mark.parametrize( "httpx_client,trace_propagation_targets,url,trace_propagated", [ From 0cc07a72e05051ea5e6c78318d80089601776bfd Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 27 Jun 2023 14:11:51 +0200 Subject: [PATCH 02/11] Make sure there is always a trace context and the trace id is always taken from propagation context. --- sentry_sdk/scope.py | 2 ++ sentry_sdk/tracing.py | 40 +++++++++++++++++++--------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 3ad61d31d5..47d572976c 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -608,6 +608,8 @@ def _drop(cause, ty): if has_tracing_enabled(options): if self._span is not None: contexts["trace"] = self._span.get_trace_context() + else: + contexts["trace"] = self.get_trace_context() else: contexts["trace"] = self.get_trace_context() diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index df59d222f2..7e28b2da03 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -130,7 +130,17 @@ def __init__( start_timestamp=None, # type: Optional[datetime] ): # type: (...) -> None - self.trace_id = trace_id or uuid.uuid4().hex + if trace_id: + self.trace_id = trace_id + + elif hub: + traceparent = hub.get_traceparent() + if traceparent: + self.trace_id = traceparent.split("-")[0] + + if not self.trace_id: + self.trace_id = uuid.uuid4().hex + self.span_id = span_id or uuid.uuid4().hex[16:] self.parent_span_id = parent_span_id self.same_process_as_parent = same_process_as_parent @@ -229,7 +239,7 @@ def start_child(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): trace_id=self.trace_id, parent_span_id=self.span_id, containing_transaction=self.containing_transaction, - **kwargs + **kwargs, ) span_recorder = ( @@ -246,12 +256,8 @@ def new_span(self, **kwargs): return self.start_child(**kwargs) @classmethod - def continue_from_environ( - cls, - environ, # type: typing.Mapping[str, str] - **kwargs # type: Any - ): - # type: (...) -> Transaction + def continue_from_environ(cls, environ, **kwargs): + # type: (Span, typing.Mapping[str, str], Any) -> Transaction """ Create a Transaction with the given params, then add in data pulled from the 'sentry-trace' and 'baggage' headers from the environ (if any) @@ -269,12 +275,8 @@ def continue_from_environ( return Transaction.continue_from_headers(EnvironHeaders(environ), **kwargs) @classmethod - def continue_from_headers( - cls, - headers, # type: typing.Mapping[str, str] - **kwargs # type: Any - ): - # type: (...) -> Transaction + def continue_from_headers(cls, headers, **kwargs): + # type: (Span, typing.Mapping[str, str], Any) -> Transaction """ Create a transaction with the given params (including any data pulled from the 'sentry-trace' and 'baggage' headers). @@ -323,12 +325,8 @@ def iter_headers(self): yield BAGGAGE_HEADER_NAME, baggage @classmethod - def from_traceparent( - cls, - traceparent, # type: Optional[str] - **kwargs # type: Any - ): - # type: (...) -> Optional[Transaction] + def from_traceparent(cls, traceparent, **kwargs): + # type: (Span, Optional[str], Any) -> Optional[Transaction] """ DEPRECATED: Use Transaction.continue_from_headers(headers, **kwargs) @@ -510,7 +508,7 @@ def __init__( parent_sampled=None, # type: Optional[bool] baggage=None, # type: Optional[Baggage] source=TRANSACTION_SOURCE_CUSTOM, # type: str - **kwargs # type: Any + **kwargs, # type: Any ): # type: (...) -> None # TODO: consider removing this in a future release. From 3e87871373d62dd82e6bd1d02db87de6e7a04a08 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 27 Jun 2023 14:16:19 +0200 Subject: [PATCH 03/11] Formatting --- sentry_sdk/tracing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 7e28b2da03..1d3beb8a1d 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -508,7 +508,7 @@ def __init__( parent_sampled=None, # type: Optional[bool] baggage=None, # type: Optional[Baggage] source=TRANSACTION_SOURCE_CUSTOM, # type: str - **kwargs, # type: Any + **kwargs # type: Any ): # type: (...) -> None # TODO: consider removing this in a future release. From 98c74adbbfb2ab947086470d9ceb91f9350cdcc5 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 27 Jun 2023 14:22:37 +0200 Subject: [PATCH 04/11] Trigger CI From 3729fff68aea94c7b68d479f890c3409843bff1f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 27 Jun 2023 14:24:18 +0200 Subject: [PATCH 05/11] Revert "Formatting" This reverts commit 3e87871373d62dd82e6bd1d02db87de6e7a04a08. --- sentry_sdk/tracing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 1d3beb8a1d..7e28b2da03 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -508,7 +508,7 @@ def __init__( parent_sampled=None, # type: Optional[bool] baggage=None, # type: Optional[Baggage] source=TRANSACTION_SOURCE_CUSTOM, # type: str - **kwargs # type: Any + **kwargs, # type: Any ): # type: (...) -> None # TODO: consider removing this in a future release. From 58de7173ac0bd1d3b687330c2c11d339050c8420 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 27 Jun 2023 14:28:50 +0200 Subject: [PATCH 06/11] Formatting --- sentry_sdk/integrations/django/views.py | 48 +++++++++++++++++++++++-- sentry_sdk/tracing.py | 26 ++++++++++---- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/sentry_sdk/integrations/django/views.py b/sentry_sdk/integrations/django/views.py index c1034d0d85..0b19121689 100644 --- a/sentry_sdk/integrations/django/views.py +++ b/sentry_sdk/integrations/django/views.py @@ -18,25 +18,35 @@ except (ImportError, SyntaxError): wrap_async_view = None # type: ignore +_monkey_patch_index = 0 +_not_set = object() + def patch_views(): # type: () -> None from django.core.handlers.base import BaseHandler + from django.http import HttpResponse, HttpResponseBase from django.template.response import SimpleTemplateResponse from sentry_sdk.integrations.django import DjangoIntegration - old_make_view_atomic = BaseHandler.make_view_atomic old_render = SimpleTemplateResponse.render def sentry_patched_render(self): # type: (SimpleTemplateResponse) -> Any + import ipdb + + ipdb.set_trace() hub = Hub.current with hub.start_span( op=OP.VIEW_RESPONSE_RENDER, description="serialize response" ): return old_render(self) + SimpleTemplateResponse.render = sentry_patched_render + + old_make_view_atomic = BaseHandler.make_view_atomic + @_functools.wraps(old_make_view_atomic) def sentry_patched_make_view_atomic(self, *args, **kwargs): # type: (Any, *Any, **Any) -> Any @@ -63,9 +73,43 @@ def sentry_patched_make_view_atomic(self, *args, **kwargs): return sentry_wrapped_callback - SimpleTemplateResponse.render = sentry_patched_render BaseHandler.make_view_atomic = sentry_patched_make_view_atomic + old_http_response = HttpResponse + + def sentry_patched_init(self, *args, **kwargs): + try: + original_content = args[0] + original_content = "xxxx" + original_content + except IndexError: + pass + + return old_http_response.__init__(self, *args, **kwargs) + + HttpResponse.__init__ = sentry_patched_init + + # def monkey_patch(prop): + # global _monkey_patch_index, _not_set + # special_attr = "$_prop_monkey_patch_{}".format(_monkey_patch_index) + # _monkey_patch_index += 1 + + # def getter(self): + # import ipdb + + # ipdb.set_trace() + # value = getattr(self, special_attr, _not_set) + # return prop.fget(self) if value is _not_set else value + + # def setter(self, value): + # import ipdb + + # ipdb.set_trace() + # setattr(self, special_attr, value) + + # return property(getter, setter) + + # HttpResponse.content = monkey_patch(HttpResponse.content) + def _wrap_sync_view(hub, callback): # type: (Hub, Any) -> Any diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 7e28b2da03..633ec505cc 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -239,7 +239,7 @@ def start_child(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): trace_id=self.trace_id, parent_span_id=self.span_id, containing_transaction=self.containing_transaction, - **kwargs, + **kwargs ) span_recorder = ( @@ -256,8 +256,12 @@ def new_span(self, **kwargs): return self.start_child(**kwargs) @classmethod - def continue_from_environ(cls, environ, **kwargs): - # type: (Span, typing.Mapping[str, str], Any) -> Transaction + def continue_from_environ( + cls, + environ, # type: typing.Mapping[str, str] + **kwargs # type: Any + ): + # type: (...) -> Transaction """ Create a Transaction with the given params, then add in data pulled from the 'sentry-trace' and 'baggage' headers from the environ (if any) @@ -275,8 +279,12 @@ def continue_from_environ(cls, environ, **kwargs): return Transaction.continue_from_headers(EnvironHeaders(environ), **kwargs) @classmethod - def continue_from_headers(cls, headers, **kwargs): - # type: (Span, typing.Mapping[str, str], Any) -> Transaction + def continue_from_headers( + cls, + headers, # type: typing.Mapping[str, str] + **kwargs # type: Any + ): + # type: (...) -> Transaction """ Create a transaction with the given params (including any data pulled from the 'sentry-trace' and 'baggage' headers). @@ -325,8 +333,12 @@ def iter_headers(self): yield BAGGAGE_HEADER_NAME, baggage @classmethod - def from_traceparent(cls, traceparent, **kwargs): - # type: (Span, Optional[str], Any) -> Optional[Transaction] + def from_traceparent( + cls, + traceparent, # type: Optional[str] + **kwargs # type: Any + ): + # type: (...) -> Optional[Transaction] """ DEPRECATED: Use Transaction.continue_from_headers(headers, **kwargs) From 0553205a48c5be83fef520acd6798c6a5145bb66 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 27 Jun 2023 14:30:35 +0200 Subject: [PATCH 07/11] Removed stuff not belonging in this pr --- sentry_sdk/integrations/django/views.py | 36 ------------------------- 1 file changed, 36 deletions(-) diff --git a/sentry_sdk/integrations/django/views.py b/sentry_sdk/integrations/django/views.py index 0b19121689..f893cb1b0a 100644 --- a/sentry_sdk/integrations/django/views.py +++ b/sentry_sdk/integrations/django/views.py @@ -26,7 +26,6 @@ def patch_views(): # type: () -> None from django.core.handlers.base import BaseHandler - from django.http import HttpResponse, HttpResponseBase from django.template.response import SimpleTemplateResponse from sentry_sdk.integrations.django import DjangoIntegration @@ -75,41 +74,6 @@ def sentry_patched_make_view_atomic(self, *args, **kwargs): BaseHandler.make_view_atomic = sentry_patched_make_view_atomic - old_http_response = HttpResponse - - def sentry_patched_init(self, *args, **kwargs): - try: - original_content = args[0] - original_content = "xxxx" + original_content - except IndexError: - pass - - return old_http_response.__init__(self, *args, **kwargs) - - HttpResponse.__init__ = sentry_patched_init - - # def monkey_patch(prop): - # global _monkey_patch_index, _not_set - # special_attr = "$_prop_monkey_patch_{}".format(_monkey_patch_index) - # _monkey_patch_index += 1 - - # def getter(self): - # import ipdb - - # ipdb.set_trace() - # value = getattr(self, special_attr, _not_set) - # return prop.fget(self) if value is _not_set else value - - # def setter(self, value): - # import ipdb - - # ipdb.set_trace() - # setattr(self, special_attr, value) - - # return property(getter, setter) - - # HttpResponse.content = monkey_patch(HttpResponse.content) - def _wrap_sync_view(hub, callback): # type: (Hub, Any) -> Any From 1ca179a4c1479557e3d0a97d8b8826723a5c2dd3 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 27 Jun 2023 14:31:03 +0200 Subject: [PATCH 08/11] Removed stuff not belonging in this pr --- sentry_sdk/integrations/django/views.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sentry_sdk/integrations/django/views.py b/sentry_sdk/integrations/django/views.py index f893cb1b0a..0f9b545c74 100644 --- a/sentry_sdk/integrations/django/views.py +++ b/sentry_sdk/integrations/django/views.py @@ -18,9 +18,6 @@ except (ImportError, SyntaxError): wrap_async_view = None # type: ignore -_monkey_patch_index = 0 -_not_set = object() - def patch_views(): # type: () -> None From 544d4e395ab6ebf7744f39ed9fb2a223fc76da95 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 27 Jun 2023 14:31:16 +0200 Subject: [PATCH 09/11] Removed stuff not belonging in this pr --- sentry_sdk/integrations/django/views.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sentry_sdk/integrations/django/views.py b/sentry_sdk/integrations/django/views.py index 0f9b545c74..d9d3ba95b3 100644 --- a/sentry_sdk/integrations/django/views.py +++ b/sentry_sdk/integrations/django/views.py @@ -30,9 +30,6 @@ def patch_views(): def sentry_patched_render(self): # type: (SimpleTemplateResponse) -> Any - import ipdb - - ipdb.set_trace() hub = Hub.current with hub.start_span( op=OP.VIEW_RESPONSE_RENDER, description="serialize response" From 6b78d26408d6b5ca9fe95cbc4e91cbae2241dd90 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 27 Jun 2023 14:32:37 +0200 Subject: [PATCH 10/11] Removed stuff not belonging in this pr --- sentry_sdk/integrations/django/views.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/integrations/django/views.py b/sentry_sdk/integrations/django/views.py index d9d3ba95b3..c1034d0d85 100644 --- a/sentry_sdk/integrations/django/views.py +++ b/sentry_sdk/integrations/django/views.py @@ -26,6 +26,7 @@ def patch_views(): from django.template.response import SimpleTemplateResponse from sentry_sdk.integrations.django import DjangoIntegration + old_make_view_atomic = BaseHandler.make_view_atomic old_render = SimpleTemplateResponse.render def sentry_patched_render(self): @@ -36,10 +37,6 @@ def sentry_patched_render(self): ): return old_render(self) - SimpleTemplateResponse.render = sentry_patched_render - - old_make_view_atomic = BaseHandler.make_view_atomic - @_functools.wraps(old_make_view_atomic) def sentry_patched_make_view_atomic(self, *args, **kwargs): # type: (Any, *Any, **Any) -> Any @@ -66,6 +63,7 @@ def sentry_patched_make_view_atomic(self, *args, **kwargs): return sentry_wrapped_callback + SimpleTemplateResponse.render = sentry_patched_render BaseHandler.make_view_atomic = sentry_patched_make_view_atomic From 8c4e9bbfaa9654552689f5965e6067ab681c026e Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 27 Jun 2023 14:33:28 +0200 Subject: [PATCH 11/11] Removed stuff not belonging in this pr --- sentry_sdk/tracing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 633ec505cc..f81ceee7ba 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -520,7 +520,7 @@ def __init__( parent_sampled=None, # type: Optional[bool] baggage=None, # type: Optional[Baggage] source=TRANSACTION_SOURCE_CUSTOM, # type: str - **kwargs, # type: Any + **kwargs # type: Any ): # type: (...) -> None # TODO: consider removing this in a future release.