Skip to content

Commit 4673548

Browse files
authored
Consolidate tracing_utils (getsentry#2655)
- Remove tracing_utils_py2.py, move the contents of tracing_utils_py3.py to tracing_utils. Some code reorganization was needed to avoid circular imports. - Move the contents of test_decorator_sync.py and test_decorator_async_py3.py to a new file, test_decorator.py, and remove the original files.
1 parent ce549ca commit 4673548

10 files changed

Lines changed: 158 additions & 216 deletions

MIGRATION_GUIDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
- The `BackgroundWorker` thread used to process events was renamed from `raven-sentry.BackgroundWorker` to `sentry-sdk.BackgroundWorker`.
1010
- The `reraise` function was moved from `sentry_sdk._compat` to `sentry_sdk.utils`.
11+
- Moved the contents of `tracing_utils_py3.py` to `tracing_utils.py`. The `start_child_span_decorator` is now in `sentry_sdk.tracing_utils`.
12+
- The actual implementation of `get_current_span` was moved to `sentry_sdk.tracing_utils`. `sentry_sdk.get_current_span` is still accessible as part of the top-level API.
1113

1214
## Removed
1315

@@ -17,5 +19,6 @@
1719
- Removed support for Flask 0.\*.
1820
- `sentry_sdk._functools` was removed.
1921
- A number of compatibility utilities were removed from `sentry_sdk._compat`: the constants `PY2` and `PY33`; the functions `datetime_utcnow`, `utc_from_timestamp`, `implements_str`, `contextmanager`; and the aliases `text_type`, `string_types`, `number_types`, `int_types`, `iteritems`, `binary_sequence_types`.
22+
- Removed `tracing_utils_py2.py`. The `start_child_span_decorator` is now in `sentry_sdk.tracing_utils`.
2023

2124
## Deprecated

sentry_sdk/api.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import inspect
22

3+
from sentry_sdk import tracing_utils
34
from sentry_sdk._types import TYPE_CHECKING
45
from sentry_sdk.hub import Hub
56
from sentry_sdk.scope import Scope
@@ -238,11 +239,7 @@ def get_current_span(hub=None):
238239
"""
239240
Returns the currently active span if there is one running, otherwise `None`
240241
"""
241-
if hub is None:
242-
hub = Hub.current
243-
244-
current_span = hub.scope.span
245-
return current_span
242+
return tracing_utils.get_current_span(hub)
246243

247244

248245
def get_traceparent():

sentry_sdk/tracing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,7 @@ def my_function():
999999
async def my_async_function():
10001000
...
10011001
"""
1002-
from sentry_sdk.tracing_utils_py3 import start_child_span_decorator
1002+
from sentry_sdk.tracing_utils import start_child_span_decorator
10031003

10041004
# This patterns allows usage of both @sentry_traced and @sentry_traced(...)
10051005
# See https://stackoverflow.com/questions/52126071/decorator-with-arguments-avoid-parenthesis-when-no-arguments/52126278

sentry_sdk/tracing_utils.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import contextlib
2+
import inspect
23
import re
34
import sys
45
from collections.abc import Mapping
6+
from functools import wraps
57
from urllib.parse import quote, unquote
68

79
import sentry_sdk
810
from sentry_sdk.consts import OP, SPANDATA
911
from sentry_sdk.utils import (
1012
capture_internal_exceptions,
1113
Dsn,
14+
logger,
1215
match_regex_list,
16+
qualname_from_function,
1317
to_string,
1418
is_sentry_url,
1519
_is_external_source,
@@ -501,5 +505,76 @@ def normalize_incoming_data(incoming_data):
501505
return data
502506

503507

508+
def start_child_span_decorator(func):
509+
# type: (Any) -> Any
510+
"""
511+
Decorator to add child spans for functions.
512+
513+
See also ``sentry_sdk.tracing.trace()``.
514+
"""
515+
# Asynchronous case
516+
if inspect.iscoroutinefunction(func):
517+
518+
@wraps(func)
519+
async def func_with_tracing(*args, **kwargs):
520+
# type: (*Any, **Any) -> Any
521+
522+
span = get_current_span(sentry_sdk.Hub.current)
523+
524+
if span is None:
525+
logger.warning(
526+
"Can not create a child span for %s. "
527+
"Please start a Sentry transaction before calling this function.",
528+
qualname_from_function(func),
529+
)
530+
return await func(*args, **kwargs)
531+
532+
with span.start_child(
533+
op=OP.FUNCTION,
534+
description=qualname_from_function(func),
535+
):
536+
return await func(*args, **kwargs)
537+
538+
# Synchronous case
539+
else:
540+
541+
@wraps(func)
542+
def func_with_tracing(*args, **kwargs):
543+
# type: (*Any, **Any) -> Any
544+
545+
span = get_current_span(sentry_sdk.Hub.current)
546+
547+
if span is None:
548+
logger.warning(
549+
"Can not create a child span for %s. "
550+
"Please start a Sentry transaction before calling this function.",
551+
qualname_from_function(func),
552+
)
553+
return func(*args, **kwargs)
554+
555+
with span.start_child(
556+
op=OP.FUNCTION,
557+
description=qualname_from_function(func),
558+
):
559+
return func(*args, **kwargs)
560+
561+
return func_with_tracing
562+
563+
564+
def get_current_span(hub=None):
565+
# type: (Optional[sentry_sdk.Hub]) -> Optional[Span]
566+
"""
567+
Returns the currently active span if there is one running, otherwise `None`
568+
"""
569+
if hub is None:
570+
hub = sentry_sdk.Hub.current
571+
572+
current_span = hub.scope.span
573+
return current_span
574+
575+
504576
# Circular imports
505577
from sentry_sdk.tracing import LOW_QUALITY_TRANSACTION_SOURCES
578+
579+
if TYPE_CHECKING:
580+
from sentry_sdk.tracing import Span

sentry_sdk/tracing_utils_py2.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

sentry_sdk/tracing_utils_py3.py

Lines changed: 0 additions & 72 deletions
This file was deleted.

tests/conftest.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,6 @@ def patch_start_tracing_child(fake_transaction_is_none=False):
639639
fake_start_child = None
640640

641641
with mock.patch(
642-
"sentry_sdk.tracing_utils_py3.get_current_span",
643-
return_value=fake_transaction,
642+
"sentry_sdk.tracing_utils.get_current_span", return_value=fake_transaction
644643
):
645644
yield fake_start_child

tests/tracing/test_decorator.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from unittest import mock
2+
3+
import pytest
4+
5+
from sentry_sdk.tracing_utils import start_child_span_decorator
6+
from sentry_sdk.utils import logger
7+
from tests.conftest import patch_start_tracing_child
8+
9+
10+
def my_example_function():
11+
return "return_of_sync_function"
12+
13+
14+
async def my_async_example_function():
15+
return "return_of_async_function"
16+
17+
18+
def test_trace_decorator():
19+
with patch_start_tracing_child() as fake_start_child:
20+
result = my_example_function()
21+
fake_start_child.assert_not_called()
22+
assert result == "return_of_sync_function"
23+
24+
result2 = start_child_span_decorator(my_example_function)()
25+
fake_start_child.assert_called_once_with(
26+
op="function", description="test_decorator.my_example_function"
27+
)
28+
assert result2 == "return_of_sync_function"
29+
30+
31+
def test_trace_decorator_no_trx():
32+
with patch_start_tracing_child(fake_transaction_is_none=True):
33+
with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning:
34+
result = my_example_function()
35+
fake_warning.assert_not_called()
36+
assert result == "return_of_sync_function"
37+
38+
result2 = start_child_span_decorator(my_example_function)()
39+
fake_warning.assert_called_once_with(
40+
"Can not create a child span for %s. "
41+
"Please start a Sentry transaction before calling this function.",
42+
"test_decorator.my_example_function",
43+
)
44+
assert result2 == "return_of_sync_function"
45+
46+
47+
@pytest.mark.asyncio
48+
async def test_trace_decorator_async():
49+
with patch_start_tracing_child() as fake_start_child:
50+
result = await my_async_example_function()
51+
fake_start_child.assert_not_called()
52+
assert result == "return_of_async_function"
53+
54+
result2 = await start_child_span_decorator(my_async_example_function)()
55+
fake_start_child.assert_called_once_with(
56+
op="function",
57+
description="test_decorator.my_async_example_function",
58+
)
59+
assert result2 == "return_of_async_function"
60+
61+
62+
@pytest.mark.asyncio
63+
async def test_trace_decorator_async_no_trx():
64+
with patch_start_tracing_child(fake_transaction_is_none=True):
65+
with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning:
66+
result = await my_async_example_function()
67+
fake_warning.assert_not_called()
68+
assert result == "return_of_async_function"
69+
70+
result2 = await start_child_span_decorator(my_async_example_function)()
71+
fake_warning.assert_called_once_with(
72+
"Can not create a child span for %s. "
73+
"Please start a Sentry transaction before calling this function.",
74+
"test_decorator.my_async_example_function",
75+
)
76+
assert result2 == "return_of_async_function"

tests/tracing/test_decorator_async_py3.py

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)