Skip to content

fix(tracing): fix logic for setting in_app flag #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
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
3 changes: 2 additions & 1 deletion sentry_sdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,8 @@ def start_transaction(

:param transaction: The transaction to start. If omitted, we create and
start a new transaction.
:param instrumenter: This parameter is meant for internal use only.
:param instrumenter: This parameter is meant for internal use only. It
will be removed in the next major version.
:param custom_sampling_context: The transaction's custom sampling context.
:param kwargs: Optional keyword arguments to be passed to the Transaction
constructor. See :py:class:`sentry_sdk.tracing.Transaction` for
Expand Down
8 changes: 7 additions & 1 deletion sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -987,7 +987,8 @@ def start_transaction(

:param transaction: The transaction to start. If omitted, we create and
start a new transaction.
:param instrumenter: This parameter is meant for internal use only.
:param instrumenter: This parameter is meant for internal use only. It
will be removed in the next major version.
:param custom_sampling_context: The transaction's custom sampling context.
:param kwargs: Optional keyword arguments to be passed to the Transaction
constructor. See :py:class:`sentry_sdk.tracing.Transaction` for
Expand Down Expand Up @@ -1054,6 +1055,10 @@ def start_span(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs):
one is not already in progress.

For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`.

The instrumenter parameter is deprecated for user code, and it will
be removed in the next major version. Going forward, it should only
be used by the SDK itself.
"""
with new_scope():
kwargs.setdefault("scope", self)
Expand Down Expand Up @@ -1298,6 +1303,7 @@ def _apply_breadcrumbs_to_event(self, event, hint, options):
event.setdefault("breadcrumbs", {}).setdefault("values", []).extend(
self._breadcrumbs
)
event["breadcrumbs"]["values"].sort(key=lambda crumb: crumb["timestamp"])

def _apply_user_to_event(self, event, hint, options):
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
Expand Down
4 changes: 4 additions & 0 deletions sentry_sdk/tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,10 @@ def start_child(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs):
Takes the same arguments as the initializer of :py:class:`Span`. The
trace id, sampling decision, transaction pointer, and span recorder are
inherited from the current span/transaction.

The instrumenter parameter is deprecated for user code, and it will
be removed in the next major version. Going forward, it should only
be used by the SDK itself.
"""
configuration_instrumenter = sentry_sdk.Scope.get_client().options[
"instrumenter"
Expand Down
39 changes: 18 additions & 21 deletions sentry_sdk/tracing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
is_sentry_url,
_is_external_source,
_module_in_list,
_is_in_project_root,
)
from sentry_sdk._types import TYPE_CHECKING

Expand Down Expand Up @@ -170,6 +171,14 @@ def maybe_create_breadcrumbs_from_span(scope, span):
)


def _get_frame_module_abs_path(frame):
# type: (FrameType) -> str
try:
return frame.f_code.co_filename
except Exception:
return ""


def add_query_source(span):
# type: (sentry_sdk.tracing.Span) -> None
"""
Expand Down Expand Up @@ -200,10 +209,7 @@ def add_query_source(span):
# Find the correct frame
frame = sys._getframe() # type: Union[FrameType, None]
while frame is not None:
try:
abs_path = frame.f_code.co_filename
except Exception:
abs_path = ""
abs_path = _get_frame_module_abs_path(frame)

try:
namespace = frame.f_globals.get("__name__") # type: Optional[str]
Expand All @@ -213,20 +219,14 @@ def add_query_source(span):
is_sentry_sdk_frame = namespace is not None and namespace.startswith(
"sentry_sdk."
)
should_be_included = _module_in_list(namespace, in_app_include)
should_be_excluded = _is_external_source(abs_path) or _module_in_list(
namespace, in_app_exclude
)

should_be_included = not _is_external_source(abs_path)
if namespace is not None:
if in_app_exclude and _module_in_list(namespace, in_app_exclude):
should_be_included = False
if in_app_include and _module_in_list(namespace, in_app_include):
# in_app_include takes precedence over in_app_exclude, so doing it
# at the end
should_be_included = True

if (
abs_path.startswith(project_root)
and should_be_included
and not is_sentry_sdk_frame
if not is_sentry_sdk_frame and (
should_be_included
or (_is_in_project_root(abs_path, project_root) and not should_be_excluded)
):
break

Expand All @@ -250,10 +250,7 @@ def add_query_source(span):
if namespace is not None:
span.set_data(SPANDATA.CODE_NAMESPACE, namespace)

try:
filepath = frame.f_code.co_filename
except Exception:
filepath = None
filepath = _get_frame_module_abs_path(frame)
if filepath is not None:
if namespace is not None:
in_app_path = filename_for_module(namespace, filepath)
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,7 @@ def event_from_exception(


def _module_in_list(name, items):
# type: (str, Optional[List[str]]) -> bool
# type: (Optional[str], Optional[List[str]]) -> bool
if name is None:
return False

Expand Down
43 changes: 39 additions & 4 deletions tests/integrations/django/test_db_query_data.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import contextlib
import os

import pytest
from datetime import datetime
from pathlib import Path
from unittest import mock

from django import VERSION as DJANGO_VERSION
Expand All @@ -15,14 +17,19 @@
from werkzeug.test import Client

from sentry_sdk import start_transaction
from sentry_sdk._types import TYPE_CHECKING
from sentry_sdk.consts import SPANDATA
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.tracing_utils import record_sql_queries
from sentry_sdk.tracing_utils import _get_frame_module_abs_path, record_sql_queries
from sentry_sdk.utils import _module_in_list

from tests.conftest import unpack_werkzeug_response
from tests.integrations.django.utils import pytest_mark_django_db_decorator
from tests.integrations.django.myapp.wsgi import application

if TYPE_CHECKING:
from types import FrameType


@pytest.fixture
def client():
Expand Down Expand Up @@ -283,7 +290,10 @@ def test_query_source_with_in_app_exclude(sentry_init, client, capture_events):

@pytest.mark.forked
@pytest_mark_django_db_decorator(transaction=True)
def test_query_source_with_in_app_include(sentry_init, client, capture_events):
@pytest.mark.parametrize("django_outside_of_project_root", [False, True])
def test_query_source_with_in_app_include(
sentry_init, client, capture_events, django_outside_of_project_root
):
sentry_init(
integrations=[DjangoIntegration()],
send_default_pii=True,
Expand All @@ -301,8 +311,33 @@ def test_query_source_with_in_app_include(sentry_init, client, capture_events):

events = capture_events()

_, status, _ = unpack_werkzeug_response(client.get(reverse("postgres_select_orm")))
assert status == "200 OK"
# Simulate Django installation outside of the project root
original_get_frame_module_abs_path = _get_frame_module_abs_path

def patched_get_frame_module_abs_path_function(frame):
# type: (FrameType) -> str
result = original_get_frame_module_abs_path(frame)
namespace = frame.f_globals.get("__name__")
if _module_in_list(namespace, ["django"]):
result = str(
Path("/outside-of-project-root") / frame.f_code.co_filename[1:]
)
return result

patched_get_frame_module_abs_path = (
mock.patch(
"sentry_sdk.tracing_utils._get_frame_module_abs_path",
patched_get_frame_module_abs_path_function,
)
if django_outside_of_project_root
else contextlib.suppress()
)

with patched_get_frame_module_abs_path:
_, status, _ = unpack_werkzeug_response(
client.get(reverse("postgres_select_orm"))
)
assert status == "200 OK"

(event,) = events
for span in event["spans"]:
Expand Down
32 changes: 32 additions & 0 deletions tests/test_basics.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import logging
import os
import sys
Expand Down Expand Up @@ -391,6 +392,37 @@ def test_breadcrumbs(sentry_init, capture_events):
assert len(event["breadcrumbs"]["values"]) == 0


def test_breadcrumb_ordering(sentry_init, capture_events):
sentry_init()
events = capture_events()

timestamps = [
datetime.datetime.now() - datetime.timedelta(days=10),
datetime.datetime.now() - datetime.timedelta(days=8),
datetime.datetime.now() - datetime.timedelta(days=12),
]

for timestamp in timestamps:
add_breadcrumb(
message="Authenticated at %s" % timestamp,
category="auth",
level="info",
timestamp=timestamp,
)

capture_exception(ValueError())
(event,) = events

assert len(event["breadcrumbs"]["values"]) == len(timestamps)
timestamps_from_event = [
datetime.datetime.strptime(
x["timestamp"].replace("Z", ""), "%Y-%m-%dT%H:%M:%S.%f"
)
for x in event["breadcrumbs"]["values"]
]
assert timestamps_from_event == sorted(timestamps)


def test_attachments(sentry_init, capture_envelopes):
sentry_init()
envelopes = capture_envelopes()
Expand Down
3 changes: 2 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
serialize_frame,
is_sentry_url,
_get_installed_modules,
_generate_installed_modules,
ensure_integration_enabled,
ensure_integration_enabled_async,
)
Expand Down Expand Up @@ -523,7 +524,7 @@ def test_installed_modules():

installed_distributions = {
_normalize_distribution_name(dist): version
for dist, version in _get_installed_modules().items()
for dist, version in _generate_installed_modules()
}

if importlib_available:
Expand Down
Loading