Skip to content
Open
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
9 changes: 9 additions & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,8 @@ def __init__(
error_sampler=None, # type: Optional[Callable[[Event, Hint], Union[float, bool]]]
enable_db_query_source=True, # type: bool
db_query_source_threshold_ms=100, # type: int
enable_http_request_source=True, # type: bool
http_request_source_threshold_ms=100, # type: int
spotlight=None, # type: Optional[Union[bool, str]]
cert_file=None, # type: Optional[str]
key_file=None, # type: Optional[str]
Expand Down Expand Up @@ -1261,6 +1263,13 @@ def __init__(

The query location will be added to the query for queries slower than the specified threshold.

:param enable_http_request_source: When enabled, the source location will be added to outgoing HTTP requests.

:param http_request_source_threshold_ms: The threshold in milliseconds for adding the source location to an
outgoing HTTP request.

The request location will be added to the request for requests slower than the specified threshold.

:param custom_repr: A custom `repr <https://docs.python.org/3/library/functions.html#repr>`_ function to run
while serializing an object.

Expand Down
9 changes: 8 additions & 1 deletion sentry_sdk/integrations/stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
from sentry_sdk.consts import OP, SPANDATA
from sentry_sdk.integrations import Integration
from sentry_sdk.scope import add_global_event_processor
from sentry_sdk.tracing_utils import EnvironHeaders, should_propagate_trace
from sentry_sdk.tracing_utils import (
EnvironHeaders,
should_propagate_trace,
add_http_request_source,
)
from sentry_sdk.utils import (
SENSITIVE_DATA_SUBSTITUTE,
capture_internal_exceptions,
Expand Down Expand Up @@ -135,6 +139,9 @@ def getresponse(self, *args, **kwargs):
finally:
span.finish()

with capture_internal_exceptions():
add_http_request_source(span)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Span Data Overwriting After Completion

The add_http_request_source call is placed after span.finish() in the finally block. Adding source information to an already finished span means this data might not be properly recorded, as finished spans are generally immutable.

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I placed add_http_request_source(span) after the span is finished because you need the end timestamp to determine the delay in receiving a response to the HTTP request.

It's done analogously in asyncpg and sqlalchemy.


return rv

HTTPConnection.putrequest = putrequest # type: ignore[method-assign]
Expand Down
88 changes: 64 additions & 24 deletions sentry_sdk/tracing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,33 +218,11 @@ def _should_be_included(
)


def add_query_source(span):
# type: (sentry_sdk.tracing.Span) -> None
def add_source(span, project_root, in_app_include, in_app_exclude):
# type: (sentry_sdk.tracing.Span, Optional[str], Optional[list[str]], Optional[list[str]]) -> None
"""
Adds OTel compatible source code information to the span
"""
client = sentry_sdk.get_client()
if not client.is_active():
return

if span.timestamp is None or span.start_timestamp is None:
return

should_add_query_source = client.options.get("enable_db_query_source", True)
if not should_add_query_source:
return

duration = span.timestamp - span.start_timestamp
threshold = client.options.get("db_query_source_threshold_ms", 0)
slow_query = duration / timedelta(milliseconds=1) > threshold

if not slow_query:
return

project_root = client.options["project_root"]
in_app_include = client.options.get("in_app_include")
in_app_exclude = client.options.get("in_app_exclude")

# Find the correct frame
frame = sys._getframe() # type: Union[FrameType, None]
while frame is not None:
Expand Down Expand Up @@ -309,6 +287,68 @@ def add_query_source(span):
span.set_data(SPANDATA.CODE_FUNCTION, frame.f_code.co_name)


def add_query_source(span):
# type: (sentry_sdk.tracing.Span) -> None
"""
Adds OTel compatible source code information to a database query span
"""
client = sentry_sdk.get_client()
if not client.is_active():
return

if span.timestamp is None or span.start_timestamp is None:
return

should_add_query_source = client.options.get("enable_db_query_source", True)
if not should_add_query_source:
return

duration = span.timestamp - span.start_timestamp
threshold = client.options.get("db_query_source_threshold_ms", 0)
slow_query = duration / timedelta(milliseconds=1) > threshold

if not slow_query:
return

add_source(
span=span,
project_root=client.options["project_root"],
in_app_include=client.options.get("in_app_include"),
in_app_exclude=client.options.get("in_app_exclude"),
)


def add_http_request_source(span):
# type: (sentry_sdk.tracing.Span) -> None
"""
Adds OTel compatible source code information to a span for an outgoing HTTP request
"""
client = sentry_sdk.get_client()
if not client.is_active():
return

if span.timestamp is None or span.start_timestamp is None:
return

should_add_request_source = client.options.get("enable_http_request_source", True)
if not should_add_request_source:
return

duration = span.timestamp - span.start_timestamp
threshold = client.options.get("http_request_source_threshold_ms", 0)
slow_query = duration / timedelta(milliseconds=1) > threshold

if not slow_query:
return

add_source(
span=span,
project_root=client.options["project_root"],
in_app_include=client.options.get("in_app_include"),
in_app_exclude=client.options.get("in_app_exclude"),
)


def extract_sentrytrace_data(header):
# type: (Optional[str]) -> Optional[Dict[str, Union[str, bool, None]]]
"""
Expand Down
6 changes: 6 additions & 0 deletions tests/integrations/stdlib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import os
import sys

# Load `httplib_helpers` into the module search path to test request source path names relative to module. See
# `test_request_source_with_module_in_search_path`
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))
Empty file.
3 changes: 3 additions & 0 deletions tests/integrations/stdlib/httplib_helpers/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def get_request_with_connection(connection, url):
connection.request("GET", url)
connection.getresponse()
Loading