Skip to content
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
13 changes: 11 additions & 2 deletions sentry_sdk/tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ class Span(object):
"_span_recorder",
"hub",
"_context_manager_state",
# TODO: rename this "transaction" once we fully and truly deprecate the
# old "transaction" attribute (which was actually the transaction name)?
"_containing_transaction",
)

def __new__(cls, **kwargs):
Expand Down Expand Up @@ -162,6 +165,7 @@ def __init__(
self.timestamp = None # type: Optional[datetime]

self._span_recorder = None # type: Optional[_SpanRecorder]
self._containing_transaction = None # type: Optional[Transaction]

def init_span_recorder(self, maxlen):
# type: (int) -> None
Expand Down Expand Up @@ -208,15 +212,20 @@ def start_child(self, **kwargs):
Start a sub-span from the current span or transaction.

Takes the same arguments as the initializer of :py:class:`Span`. The
trace id, sampling decision, and span recorder are inherited from the
current span/transaction.
trace id, sampling decision, transaction pointer, and span recorder are
inherited from the current span/transaction.
"""
kwargs.setdefault("sampled", self.sampled)

rv = Span(
trace_id=self.trace_id, span_id=None, parent_span_id=self.span_id, **kwargs
)

if isinstance(self, Transaction):
rv._containing_transaction = self
else:
rv._containing_transaction = self._containing_transaction

rv._span_recorder = recorder = self._span_recorder
if recorder:
recorder.add(rv)
Expand Down
83 changes: 81 additions & 2 deletions tests/tracing/test_misc.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest

from sentry_sdk import start_span, start_transaction
from sentry_sdk.tracing import Transaction
from sentry_sdk import Hub, start_span, start_transaction
from sentry_sdk.tracing import Span, Transaction


def test_span_trimming(sentry_init, capture_events):
Expand Down Expand Up @@ -49,3 +49,82 @@ def test_transaction_method_signature(sentry_init, capture_events):
with start_transaction(Transaction(name="c")):
pass
assert len(events) == 4


def test_finds_transaction_on_scope(sentry_init):
sentry_init(traces_sample_rate=1.0)

transaction = start_transaction(name="dogpark")

scope = Hub.current.scope

# See note in Scope class re: getters and setters of the `transaction`
# property. For the moment, assigning to scope.transaction merely sets the
# transaction name, rather than putting the transaction on the scope, so we
# have to assign to _span directly.
scope._span = transaction

# Reading scope.property, however, does what you'd expect, and returns the
# transaction on the scope.
assert scope.transaction is not None
assert isinstance(scope.transaction, Transaction)
assert scope.transaction.name == "dogpark"

# If the transaction is also set as the span on the scope, it can be found
# by accessing _span, too.
assert scope._span is not None
assert isinstance(scope._span, Transaction)
assert scope._span.name == "dogpark"


def test_finds_transaction_when_decedent_span_is_on_scope(
sentry_init,
):
sentry_init(traces_sample_rate=1.0)

transaction = start_transaction(name="dogpark")
child_span = transaction.start_child(op="sniffing")

scope = Hub.current.scope
scope._span = child_span

# this is the same whether it's the transaction itself or one of its
# decedents directly attached to the scope
assert scope.transaction is not None
assert isinstance(scope.transaction, Transaction)
assert scope.transaction.name == "dogpark"

# here we see that it is in fact the span on the scope, rather than the
# transaction itself
assert scope._span is not None
assert isinstance(scope._span, Span)
assert scope._span.op == "sniffing"


def test_finds_orphan_span_on_scope(sentry_init):
# this is deprecated behavior which may be removed at some point (along with
# the start_span function)
sentry_init(traces_sample_rate=1.0)

span = start_span(op="sniffing")

scope = Hub.current.scope
scope._span = span

assert scope._span is not None
assert isinstance(scope._span, Span)
assert scope._span.op == "sniffing"


def test_finds_non_orphan_span_on_scope(sentry_init):
sentry_init(traces_sample_rate=1.0)

transaction = start_transaction(name="dogpark")
child_span = transaction.start_child(op="sniffing")

scope = Hub.current.scope
scope._span = child_span

assert scope._span is not None
assert isinstance(scope._span, Span)
assert scope._span.op == "sniffing"