Skip to content

Commit 6253e28

Browse files
authored
ref: Next iteration of span API (getsentry#466)
* ref: Next iteration of span API * fix: Do not allow user to finish a span twice * fix: Fix tests * fix: Fix aiohttp integration * doc: Add documentation and shortcut method
1 parent a548e4f commit 6253e28

File tree

13 files changed

+146
-116
lines changed

13 files changed

+146
-116
lines changed

mypy.ini

-3
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ warn_redundant_casts = True
2222

2323
; Relaxations:
2424

25-
[mypy-sentry_sdk.tracing]
26-
disallow_untyped_defs = False
27-
2825
[mypy-sentry_sdk._compat]
2926
disallow_untyped_defs = False
3027

sentry_sdk/api.py

+14
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from typing import ContextManager
1616

1717
from sentry_sdk._types import Event, Hint, Breadcrumb, BreadcrumbHint
18+
from sentry_sdk.tracing import Span
1819

1920
T = TypeVar("T")
2021
F = TypeVar("F", bound=Callable[..., Any])
@@ -34,6 +35,7 @@ def overload(x):
3435
"push_scope",
3536
"flush",
3637
"last_event_id",
38+
"start_span",
3739
]
3840

3941

@@ -179,3 +181,15 @@ def last_event_id():
179181
if hub is not None:
180182
return hub.last_event_id()
181183
return None
184+
185+
186+
@hubmethod
187+
def start_span(
188+
span=None, # type: Optional[Span]
189+
**kwargs # type: Any
190+
):
191+
# type: (...) -> Span
192+
193+
# TODO: All other functions in this module check for
194+
# `Hub.current is None`. That actually should never happen?
195+
return Hub.current.start_span(span=span, **kwargs)

sentry_sdk/hub.py

+13-83
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from sentry_sdk._compat import with_metaclass
1111
from sentry_sdk.scope import Scope
1212
from sentry_sdk.client import Client
13-
from sentry_sdk.tracing import Span, maybe_create_breadcrumbs_from_span
13+
from sentry_sdk.tracing import Span
1414
from sentry_sdk.utils import (
1515
exc_info_from_error,
1616
event_from_exception,
@@ -128,17 +128,6 @@ def main(self):
128128
return GLOBAL_HUB
129129

130130

131-
class _HubManager(object):
132-
def __init__(self, hub):
133-
# type: (Hub) -> None
134-
self._old = Hub.current
135-
_local.set(hub)
136-
137-
def __exit__(self, exc_type, exc_value, tb):
138-
# type: (Any, Any, Any) -> None
139-
_local.set(self._old)
140-
141-
142131
class _ScopeManager(object):
143132
def __init__(self, hub):
144133
# type: (Hub) -> None
@@ -429,44 +418,27 @@ def add_breadcrumb(
429418
while len(scope._breadcrumbs) > max_breadcrumbs:
430419
scope._breadcrumbs.popleft()
431420

432-
@contextmanager
433-
def span(
434-
self,
435-
span=None, # type: Optional[Span]
436-
**kwargs # type: Any
437-
):
438-
# type: (...) -> Generator[Span, None, None]
439-
# TODO: Document
440-
span = self.start_span(span=span, **kwargs)
441-
442-
_, scope = self._stack[-1]
443-
old_span = scope.span
444-
scope.span = span
445-
446-
try:
447-
yield span
448-
except Exception:
449-
span.set_failure()
450-
raise
451-
finally:
452-
try:
453-
span.finish()
454-
maybe_create_breadcrumbs_from_span(self, span)
455-
self.finish_span(span)
456-
except Exception:
457-
self._capture_internal_exception(sys.exc_info())
458-
scope.span = old_span
459-
460421
def start_span(
461422
self,
462423
span=None, # type: Optional[Span]
463424
**kwargs # type: Any
464425
):
465426
# type: (...) -> Span
466-
# TODO: Document
427+
"""
428+
Create a new span whose parent span is the currently active
429+
span, if any. The return value is the span object that can
430+
be used as a context manager to start and stop timing.
431+
432+
Note that you will not see any span that is not contained
433+
within a transaction. Create a transaction with
434+
``start_span(transaction="my transaction")`` if an
435+
integration doesn't already do this for you.
436+
"""
467437

468438
client, scope = self._stack[-1]
469439

440+
kwargs.setdefault("hub", self)
441+
470442
if span is None:
471443
if scope.span is not None:
472444
span = scope.span.new_span(**kwargs)
@@ -482,48 +454,6 @@ def start_span(
482454

483455
return span
484456

485-
def finish_span(
486-
self, span # type: Span
487-
):
488-
# type: (...) -> Optional[str]
489-
# TODO: Document
490-
if span.timestamp is None:
491-
# This transaction is not yet finished so we just finish it.
492-
span.finish()
493-
494-
if span.transaction is None:
495-
# If this has no transaction set we assume there's a parent
496-
# transaction for this span that would be flushed out eventually.
497-
return None
498-
499-
if self.client is None:
500-
# We have no client and therefore nowhere to send this transaction
501-
# event.
502-
return None
503-
504-
if not span.sampled:
505-
# At this point a `sampled = None` should have already been
506-
# resolved to a concrete decision. If `sampled` is `None`, it's
507-
# likely that somebody used `with Hub.span(..)` on a
508-
# non-transaction span and later decided to make it a transaction.
509-
assert (
510-
span.sampled is not None
511-
), "Need to set transaction when entering span!"
512-
return None
513-
514-
return self.capture_event(
515-
{
516-
"type": "transaction",
517-
"transaction": span.transaction,
518-
"contexts": {"trace": span.get_trace_context()},
519-
"timestamp": span.timestamp,
520-
"start_timestamp": span.start_timestamp,
521-
"spans": [
522-
s.to_json() for s in (span._finished_spans or ()) if s is not span
523-
],
524-
}
525-
)
526-
527457
@overload # noqa
528458
def push_scope(
529459
self, callback=None # type: Optional[None]

sentry_sdk/integrations/aiohttp.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ async def inner():
6565

6666
# If this transaction name makes it to the UI, AIOHTTP's
6767
# URL resolver did not find a route or died trying.
68-
with hub.span(transaction="generic AIOHTTP request"):
68+
with hub.start_span(transaction="generic AIOHTTP request"):
6969
try:
7070
response = await old_handle(self, request)
7171
except HTTPException:

sentry_sdk/integrations/celery.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def apply_async(*args, **kwargs):
7979
if headers is not None:
8080
kwargs["headers"] = headers
8181

82-
with hub.span(op="celery.submit", description=task.name):
82+
with hub.start_span(op="celery.submit", description=task.name):
8383
return f(*args, **kwargs)
8484
else:
8585
return f(*args, **kwargs)
@@ -114,7 +114,7 @@ def _inner(*args, **kwargs):
114114
# something such as attribute access can fail.
115115
span.transaction = task.name
116116

117-
with hub.span(span):
117+
with hub.start_span(span):
118118
return f(*args, **kwargs)
119119

120120
return _inner

sentry_sdk/integrations/redis.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def sentry_patched_execute_command(self, name, *args, **kwargs):
3232

3333
description = " ".join(description_parts)
3434

35-
with hub.span(op="redis", description=description) as span:
35+
with hub.start_span(op="redis", description=description) as span:
3636
if name and args and name.lower() in ("get", "set", "setex", "setnx"):
3737
span.set_tag("redis.key", args[0])
3838

sentry_sdk/integrations/rq.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def sentry_patched_perform_job(self, job, *args, **kwargs):
5555
with capture_internal_exceptions():
5656
span.transaction = job.func_name
5757

58-
with hub.span(span):
58+
with hub.start_span(span):
5959
rv = old_perform_job(self, job, *args, **kwargs)
6060

6161
if self.is_horse:

sentry_sdk/integrations/stdlib.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def sentry_patched_popen_init(self, *a, **kw):
181181
env = _init_argument(a, kw, "env", 10, lambda x: dict(x or os.environ))
182182
env["SUBPROCESS_" + k.upper().replace("-", "_")] = v
183183

184-
with hub.span(op="subprocess", description=description) as span:
184+
with hub.start_span(op="subprocess", description=description) as span:
185185
span.set_data("subprocess.cwd", cwd)
186186

187187
return old_popen_init(self, *a, **kw)

sentry_sdk/integrations/wsgi.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def __call__(self, environ, start_response):
9696
span.op = "http.server"
9797
span.transaction = "generic WSGI request"
9898

99-
with hub.span(span) as span:
99+
with hub.start_span(span) as span:
100100
try:
101101
rv = self.app(
102102
environ,

0 commit comments

Comments
 (0)