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
16 changes: 8 additions & 8 deletions sentry_sdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sentry_sdk._init_implementation import init
from sentry_sdk.consts import INSTRUMENTER
from sentry_sdk.scope import Scope, _ScopeManager, new_scope, isolation_scope
from sentry_sdk.traces import StreamedSpan
from sentry_sdk.traces import StreamedSpan, _get_current_streamed_span
from sentry_sdk.tracing import NoOpSpan, Transaction, trace
from sentry_sdk.crons import monitor

Expand Down Expand Up @@ -422,7 +422,7 @@ def set_measurement(name: str, value: float, unit: "MeasurementUnit" = "") -> No

def get_current_span(
scope: "Optional[Scope]" = None,
) -> "Optional[Union[Span, StreamedSpan]]":
) -> "Optional[Span]":
"""
Returns the currently active span if there is one running, otherwise `None`
"""
Expand Down Expand Up @@ -533,12 +533,7 @@ def update_current_span(
attributes={"user_id": 123, "batch_size": 50}
)
"""
current_span = get_current_span()

if current_span is None:
return

if isinstance(current_span, StreamedSpan):
if isinstance(_get_current_streamed_span(), StreamedSpan):
warnings.warn(
"The `update_current_span` API isn't available in streaming mode. "
"Retrieve the current span with get_current_span() and use its API "
Expand All @@ -548,6 +543,11 @@ def update_current_span(
)
return

current_span = get_current_span()

if current_span is None:
return

if op is not None:
current_span.op = op

Expand Down
19 changes: 11 additions & 8 deletions sentry_sdk/integrations/celery/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from sentry_sdk.integrations.celery.utils import _now_seconds_since_epoch
from sentry_sdk.integrations.logging import ignore_logger
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.traces import StreamedSpan
from sentry_sdk.traces import StreamedSpan, _get_current_streamed_span
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, Span, TransactionSource
from sentry_sdk.tracing_utils import Baggage, has_span_streaming_enabled
from sentry_sdk.utils import (
Expand Down Expand Up @@ -98,13 +98,16 @@ def setup_once() -> None:


def _set_status(status: str) -> None:
client = sentry_sdk.get_client()
span_streaming = has_span_streaming_enabled(client.options)

with capture_internal_exceptions():
scope = sentry_sdk.get_current_scope()
if scope.span is not None:
if isinstance(scope.span, Span):
scope.span.set_status(status)
else:
scope.span.status = "ok" if status == "ok" else "error"

if span_streaming and scope.streamed_span is not None:
scope.streamed_span.status = "ok" if status == "ok" else "error"
elif not span_streaming and scope.span is not None:
scope.span.set_status(status)


def _capture_exception(task: "Any", exc_info: "ExcInfo") -> None:
Expand Down Expand Up @@ -289,7 +292,7 @@ def apply_async(*args: "Any", **kwargs: "Any") -> "Any":

span_mgr: "Union[StreamedSpan, Span, NoOpMgr]" = NoOpMgr()
if span_streaming:
if not task_started_from_beat and sentry_sdk.get_current_span() is not None:
if not task_started_from_beat and _get_current_streamed_span() is not None:
span_mgr = sentry_sdk.traces.start_span(
name=task_name,
attributes={
Expand Down Expand Up @@ -570,7 +573,7 @@ def sentry_publish(self: "Producer", *args: "Any", **kwargs: "Any") -> "Any":

span: "Union[StreamedSpan, Span, None]" = None
if span_streaming:
if sentry_sdk.get_current_span() is not None:
if _get_current_streamed_span() is not None:
span = sentry_sdk.traces.start_span(
name=task_name,
attributes={
Expand Down
16 changes: 10 additions & 6 deletions sentry_sdk/integrations/fastapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,17 @@ def _sentry_get_request_handler(*args: "Any", **kwargs: "Any") -> "Any":
@wraps(old_call)
def _sentry_call(*args: "Any", **kwargs: "Any") -> "Any":
current_scope = sentry_sdk.get_current_scope()
current_span = current_scope.span

if isinstance(current_span, StreamedSpan) and not isinstance(
current_span, NoOpStreamedSpan
):
segment = current_span._segment
segment._update_active_thread()
client = sentry_sdk.get_client()
if has_span_streaming_enabled(client.options):
current_span = current_scope.streamed_span

if isinstance(current_span, StreamedSpan) and not isinstance(
current_span, NoOpStreamedSpan
):
segment = current_span._segment
segment._update_active_thread()

elif current_scope.transaction is not None:
current_scope.transaction.update_active_thread()

Expand Down
23 changes: 13 additions & 10 deletions sentry_sdk/integrations/starlette.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
)
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.traces import NoOpStreamedSpan, StreamedSpan
from sentry_sdk.traces import NoOpStreamedSpan, StreamedSpan, _get_current_streamed_span
from sentry_sdk.tracing import (
SOURCE_FOR_STYLE,
TransactionSource,
Expand Down Expand Up @@ -254,7 +254,7 @@ def _default(value: "Any") -> "Any":
def _set_request_body_data_on_streaming_segment(
info: "Optional[Dict[str, Any]]",
) -> None:
current_span = sentry_sdk.get_current_span()
current_span = _get_current_streamed_span()
if (
info
and "data" in info
Expand Down Expand Up @@ -545,19 +545,22 @@ def event_processor(

@functools.wraps(old_func)
def _sentry_sync_func(*args: "Any", **kwargs: "Any") -> "Any":
integration = sentry_sdk.get_client().get_integration(
StarletteIntegration
)
client = sentry_sdk.get_client()

integration = client.get_integration(StarletteIntegration)
if integration is None:
return old_func(*args, **kwargs)

current_scope = sentry_sdk.get_current_scope()
current_span = current_scope.span

if isinstance(current_span, StreamedSpan) and not isinstance(
current_span, NoOpStreamedSpan
):
current_span._segment._update_active_thread()
span_streaming = has_span_streaming_enabled(client.options)
if span_streaming:
current_span = current_scope.streamed_span

if isinstance(current_span, StreamedSpan) and not isinstance(
current_span, NoOpStreamedSpan
):
current_span._segment._update_active_thread()
elif current_scope.transaction is not None:
current_scope.transaction.update_active_thread()
Comment thread
sentry-warden[bot] marked this conversation as resolved.

Expand Down
45 changes: 34 additions & 11 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,12 +580,18 @@ def get_traceparent(self, *args: "Any", **kwargs: "Any") -> "Optional[str]":
"""
client = self.get_client()

if not has_tracing_enabled(client.options):
return self.get_active_propagation_context().to_traceparent()
Comment thread
sentrivana marked this conversation as resolved.

span_streaming = has_span_streaming_enabled(client.options)
# If we have an active span, return traceparent from there
if (
has_tracing_enabled(client.options)
and self.span is not None
and not isinstance(self.span, NoOpStreamedSpan)
span_streaming
and self.streamed_span is not None
and not isinstance(self.streamed_span, NoOpStreamedSpan)
):
return self.streamed_span._to_traceparent()
elif not span_streaming and self.span is not None:
return self.span._to_traceparent()

# else return traceparent from the propagation context
Expand All @@ -598,12 +604,18 @@ def get_baggage(self, *args: "Any", **kwargs: "Any") -> "Optional[Baggage]":
"""
client = self.get_client()

if not has_tracing_enabled(client.options):
return self.get_active_propagation_context().get_baggage()
Comment thread
sentrivana marked this conversation as resolved.

span_streaming = has_span_streaming_enabled(client.options)
# If we have an active span, return baggage from there
if (
has_tracing_enabled(client.options)
and self.span is not None
and not isinstance(self.span, NoOpStreamedSpan)
span_streaming
and self.streamed_span is not None
and not isinstance(self.streamed_span, NoOpStreamedSpan)
):
return self.streamed_span._to_baggage()
elif not span_streaming and self.span is not None:
return self.span._to_baggage()

# else return baggage from the propagation context
Expand Down Expand Up @@ -680,7 +692,9 @@ def iter_trace_propagation_headers(
return

span = kwargs.pop("span", None)
span = span or self.span
if not span:
span_streaming = has_span_streaming_enabled(client.options)
span = self.streamed_span if span_streaming else self.span

if (
has_tracing_enabled(client.options)
Expand Down Expand Up @@ -877,12 +891,12 @@ def set_user(self, value: "Optional[Dict[str, Any]]") -> None:
session.update(user=value)

@property
def span(self) -> "Optional[Union[Span, StreamedSpan]]":
def span(self) -> "Optional[Span]":
"""Get/set current tracing span or transaction."""
return self._span
return self._span if isinstance(self._span, Span) else None

@span.setter
def span(self, span: "Optional[Union[Span, StreamedSpan]]") -> None:
def span(self, span: "Optional[Span]") -> None:
self._span = span
# XXX: this differs from the implementation in JS, there Scope.setSpan
# does not set Scope._transactionName.
Expand All @@ -893,6 +907,15 @@ def span(self, span: "Optional[Union[Span, StreamedSpan]]") -> None:
if transaction.source:
self._transaction_info["source"] = transaction.source
Comment thread
alexander-alderman-webb marked this conversation as resolved.

@property
def streamed_span(self) -> "Optional[StreamedSpan]":
"""Get/set current tracing span."""
return self._span if isinstance(self._span, StreamedSpan) else None

@streamed_span.setter
def streamed_span(self, span: "Optional[StreamedSpan]") -> None:
self._span = span
Comment thread
alexander-alderman-webb marked this conversation as resolved.

# Also set _transaction and _transaction_info in streaming mode as this
# is used for populating events and linking them to segments
if (
Expand Down Expand Up @@ -1267,7 +1290,7 @@ def start_streamed_span(
if parent_span is _DEFAULT_PARENT_SPAN or isinstance(
parent_span, NoOpStreamedSpan
):
parent_span = self.span # type: ignore
parent_span = self.streamed_span

# If no eligible parent_span was provided and there is no currently
# active span, this is a segment
Expand Down
26 changes: 20 additions & 6 deletions sentry_sdk/traces.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,8 @@ def finish(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> No

def _start(self) -> None:
if self._active:
old_span = self._scope.span
self._scope.span = self
old_span = self._scope.streamed_span
self._scope.streamed_span = self
self._previous_span_on_scope = old_span

def _end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None:
Expand All @@ -360,7 +360,7 @@ def _end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None
with capture_internal_exceptions():
old_span = self._previous_span_on_scope
del self._previous_span_on_scope
self._scope.span = old_span
self._scope.streamed_span = old_span

# Set attributes from the segment. These are set on span end on purpose
# so that we have the best chance to capture the segment's final name
Expand Down Expand Up @@ -586,8 +586,8 @@ def _start(self) -> None:
if self._scope is None:
return

old_span = self._scope.span
self._scope.span = self
old_span = self._scope.streamed_span
self._scope.streamed_span = self
self._previous_span_on_scope = old_span

def _end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None:
Expand All @@ -610,7 +610,7 @@ def _end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None
with capture_internal_exceptions():
old_span = self._previous_span_on_scope
del self._previous_span_on_scope
self._scope.span = old_span
self._scope.streamed_span = old_span

self._finished = True

Expand Down Expand Up @@ -755,3 +755,17 @@ def make_db_query(sql):
return decorator(func)
else:
return decorator


def _get_current_streamed_span(
scope: "Optional[sentry_sdk.Scope]" = None,
) -> "Optional[StreamedSpan]":
"""
Returns the currently active span on the scope if the span is a `StreamedSpan`, otherwise `None`.

This function will only return a non-`None` value when the streaming trace lifecycle is enabled.
To enable the lifecycle, pass `_experiments={"trace_lifecycle": "stream"}` to `sentry.init()`.
"""
scope = scope or sentry_sdk.get_current_scope()
current_span = scope.streamed_span
return current_span
13 changes: 10 additions & 3 deletions sentry_sdk/tracing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1194,7 +1194,7 @@ def sync_wrapper(*args: "Any", **kwargs: "Any") -> "Any":

def get_current_span(
scope: "Optional[sentry_sdk.Scope]" = None,
) -> "Optional[Union[Span, StreamedSpan]]":
) -> "Optional[Span]":
"""
Returns the currently active span if there is one running, otherwise `None`
"""
Expand All @@ -1208,9 +1208,16 @@ def set_span_errored(span: "Optional[Union[Span, StreamedSpan]]" = None) -> None
Set the status of the current or given span to INTERNAL_ERROR.
Also sets the status of the transaction (root span) to INTERNAL_ERROR.
"""
from sentry_sdk.traces import StreamedSpan, SpanStatus
from sentry_sdk.traces import StreamedSpan, SpanStatus, _get_current_streamed_span

span = span or get_current_span()
client = sentry_sdk.get_client()

if not span:
span = (
_get_current_streamed_span()
if has_span_streaming_enabled(client.options)
else sentry_sdk.get_current_span()
)

if span is not None:
if isinstance(span, Span):
Expand Down
7 changes: 6 additions & 1 deletion tests/integrations/celery/test_celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import sentry_sdk
from sentry_sdk import start_transaction, get_current_span
from sentry_sdk.traces import _get_current_streamed_span
from sentry_sdk.integrations.celery import (
CeleryIntegration,
_wrap_task_run,
Expand Down Expand Up @@ -663,7 +664,11 @@ def test_sentry_propagate_traces_override(span_streaming, init_celery):

@celery.task(name="dummy_task", bind=True)
def dummy_task(self, message):
trace_id = get_current_span().trace_id
trace_id = (
_get_current_streamed_span().trace_id
if span_streaming
else get_current_span().trace_id
)
return trace_id

if span_streaming:
Expand Down
4 changes: 3 additions & 1 deletion tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
get_isolation_scope,
)

from sentry_sdk.tracing import Span

from sentry_sdk.client import Client, NonRecordingClient
from tests.conftest import TestTransportWithOptions

Expand All @@ -40,7 +42,7 @@ def test_get_current_span_default_hub(sentry_init):
assert get_current_span() is None

scope = get_current_scope()
fake_span = mock.MagicMock()
fake_span = Span()
scope.span = fake_span

assert get_current_span() == fake_span
Expand Down
Loading