Skip to content
29 changes: 22 additions & 7 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from sentry_sdk.utils import ContextVar
from sentry_sdk.sessions import SessionFlusher
from sentry_sdk.envelope import Envelope
from sentry_sdk.tracing_utils import has_tracestate_enabled, reinflate_tracestate

from sentry_sdk._types import MYPY

Expand Down Expand Up @@ -329,15 +330,29 @@ def capture_event(
attachments = hint.get("attachments")
is_transaction = event_opt.get("type") == "transaction"

# this is outside of the `if` immediately below because even if we don't
# use the value, we want to make sure we remove it before the event is
# sent
raw_tracestate = (
event_opt.get("contexts", {}).get("trace", {}).pop("tracestate", "")
)

# Transactions or events with attachments should go to the /envelope/
# endpoint.
if is_transaction or attachments:
# Transactions or events with attachments should go to the
# /envelope/ endpoint.
envelope = Envelope(
headers={
"event_id": event_opt["event_id"],
"sent_at": format_timestamp(datetime.utcnow()),
}

headers = {
"event_id": event_opt["event_id"],
"sent_at": format_timestamp(datetime.utcnow()),
}

tracestate_data = raw_tracestate and reinflate_tracestate(
raw_tracestate.replace("sentry=", "")
)
if tracestate_data and has_tracestate_enabled():
headers["trace"] = tracestate_data

envelope = Envelope(headers=headers)

if is_transaction:
envelope.add_transaction(event_opt)
Expand Down
1 change: 1 addition & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"max_spans": Optional[int],
"record_sql_params": Optional[bool],
"smart_transaction_trimming": Optional[bool],
"propagate_tracestate": Optional[bool],
},
total=False,
)
Expand Down
3 changes: 2 additions & 1 deletion sentry_sdk/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,8 @@ def iter_trace_propagation_headers(self, span=None):
if not propagate_traces:
return

yield "sentry-trace", span.to_traceparent()
for header in span.iter_headers():
yield header


GLOBAL_HUB = Hub()
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/django/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from sentry_sdk.hub import Hub, _should_send_default_pii
from sentry_sdk.scope import add_global_event_processor
from sentry_sdk.serializer import add_global_repr_processor
from sentry_sdk.tracing import record_sql_queries
from sentry_sdk.tracing_utils import record_sql_queries
from sentry_sdk.utils import (
HAS_REAL_CONTEXTVARS,
CONTEXTVARS_ERROR_MESSAGE,
Expand Down
11 changes: 11 additions & 0 deletions sentry_sdk/integrations/httpx.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from sentry_sdk import Hub
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.utils import logger

from sentry_sdk._types import MYPY

Expand Down Expand Up @@ -45,6 +46,11 @@ def send(self, request, **kwargs):
span.set_data("method", request.method)
span.set_data("url", 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
rv = real_send(self, request, **kwargs)

Expand Down Expand Up @@ -72,6 +78,11 @@ async def send(self, request, **kwargs):
span.set_data("method", request.method)
span.set_data("url", 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
rv = await real_send(self, request, **kwargs)

Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from sentry_sdk._types import MYPY
from sentry_sdk.hub import Hub
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.tracing import record_sql_queries
from sentry_sdk.tracing_utils import record_sql_queries

try:
from sqlalchemy.engine import Engine # type: ignore
Expand Down
9 changes: 7 additions & 2 deletions sentry_sdk/integrations/stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
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 import EnvironHeaders
from sentry_sdk.utils import capture_internal_exceptions, safe_repr
from sentry_sdk.tracing_utils import EnvironHeaders
from sentry_sdk.utils import capture_internal_exceptions, logger, safe_repr

from sentry_sdk._types import MYPY

Expand Down Expand Up @@ -86,6 +86,11 @@ 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
)
)
self.putheader(key, value)

self._sentrysdk_span = span
Expand Down
20 changes: 7 additions & 13 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,19 +150,13 @@ def transaction(self):
if self._span is None:
return None

# the span on the scope is itself a transaction
if isinstance(self._span, Transaction):
return self._span

# the span on the scope isn't a transaction but belongs to one
if self._span._containing_transaction:
return self._span._containing_transaction
# there is an orphan span on the scope
if self._span.containing_transaction is None:
return None

# there's a span (not a transaction) on the scope, but it was started on
# its own, not as the descendant of a transaction (this is deprecated
# behavior, but as long as the start_span function exists, it can still
# happen)
return None
# there is either a transaction (which is its own containing
# transaction) or a non-orphan span on the scope
return self._span.containing_transaction

@transaction.setter
def transaction(self, value):
Expand All @@ -174,7 +168,7 @@ def transaction(self, value):
# anything set in the scope.
# XXX: note that with the introduction of the Scope.transaction getter,
# there is a semantic and type mismatch between getter and setter. The
# getter returns a transaction, the setter sets a transaction name.
# getter returns a Transaction, the setter sets a transaction name.
# Without breaking version compatibility, we could make the setter set a
# transaction name or transaction (self._span) depending on the type of
# the value argument.
Expand Down
Loading