Skip to content

Commit a1386b0

Browse files
feat(stdlib): Add source information for slow outgoing HTTP requests
1 parent 87f8f39 commit a1386b0

File tree

7 files changed

+321
-25
lines changed

7 files changed

+321
-25
lines changed

sentry_sdk/consts.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,8 @@ def __init__(
913913
error_sampler=None, # type: Optional[Callable[[Event, Hint], Union[float, bool]]]
914914
enable_db_query_source=True, # type: bool
915915
db_query_source_threshold_ms=100, # type: int
916+
enable_http_request_source=True, # type: bool
917+
http_request_source_threshold_ms=100, # type: int
916918
spotlight=None, # type: Optional[Union[bool, str]]
917919
cert_file=None, # type: Optional[str]
918920
key_file=None, # type: Optional[str]
@@ -1268,6 +1270,13 @@ def __init__(
12681270
12691271
The query location will be added to the query for queries slower than the specified threshold.
12701272
1273+
:param enable_http_request_source: When enabled, the source location will be added to outgoing HTTP requests.
1274+
1275+
:param http_request_source_threshold_ms: The threshold in milliseconds for adding the source location to an
1276+
outgoing HTTP request.
1277+
1278+
The request location will be added to the request for requests slower than the specified threshold.
1279+
12711280
:param custom_repr: A custom `repr <https://docs.python.org/3/library/functions.html#repr>`_ function to run
12721281
while serializing an object.
12731282

sentry_sdk/integrations/stdlib.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
from sentry_sdk.consts import OP, SPANDATA
99
from sentry_sdk.integrations import Integration
1010
from sentry_sdk.scope import add_global_event_processor
11-
from sentry_sdk.tracing_utils import EnvironHeaders, should_propagate_trace
11+
from sentry_sdk.tracing_utils import (
12+
EnvironHeaders,
13+
should_propagate_trace,
14+
add_http_request_source,
15+
)
1216
from sentry_sdk.utils import (
1317
SENSITIVE_DATA_SUBSTITUTE,
1418
capture_internal_exceptions,
@@ -135,6 +139,9 @@ def getresponse(self, *args, **kwargs):
135139
finally:
136140
span.finish()
137141

142+
with capture_internal_exceptions():
143+
add_http_request_source(span)
144+
138145
return rv
139146

140147
HTTPConnection.putrequest = putrequest # type: ignore[method-assign]

sentry_sdk/tracing_utils.py

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -218,33 +218,10 @@ def _should_be_included(
218218
)
219219

220220

221-
def add_query_source(span):
222-
# type: (sentry_sdk.tracing.Span) -> None
221+
def add_source(span, project_root, in_app_include, in_app_exclude):
223222
"""
224223
Adds OTel compatible source code information to the span
225224
"""
226-
client = sentry_sdk.get_client()
227-
if not client.is_active():
228-
return
229-
230-
if span.timestamp is None or span.start_timestamp is None:
231-
return
232-
233-
should_add_query_source = client.options.get("enable_db_query_source", True)
234-
if not should_add_query_source:
235-
return
236-
237-
duration = span.timestamp - span.start_timestamp
238-
threshold = client.options.get("db_query_source_threshold_ms", 0)
239-
slow_query = duration / timedelta(milliseconds=1) > threshold
240-
241-
if not slow_query:
242-
return
243-
244-
project_root = client.options["project_root"]
245-
in_app_include = client.options.get("in_app_include")
246-
in_app_exclude = client.options.get("in_app_exclude")
247-
248225
# Find the correct frame
249226
frame = sys._getframe() # type: Union[FrameType, None]
250227
while frame is not None:
@@ -309,6 +286,69 @@ def add_query_source(span):
309286
span.set_data(SPANDATA.CODE_FUNCTION, frame.f_code.co_name)
310287

311288

289+
def add_query_source(span):
290+
# type: (sentry_sdk.tracing.Span) -> None
291+
"""
292+
Adds OTel compatible source code information to a database query span
293+
"""
294+
client = sentry_sdk.get_client()
295+
if not client.is_active():
296+
return
297+
298+
if span.timestamp is None or span.start_timestamp is None:
299+
return
300+
301+
should_add_query_source = client.options.get("enable_db_query_source", True)
302+
if not should_add_query_source:
303+
return
304+
305+
duration = span.timestamp - span.start_timestamp
306+
threshold = client.options.get("db_query_source_threshold_ms", 0)
307+
slow_query = duration / timedelta(milliseconds=1) > threshold
308+
309+
if not slow_query:
310+
return
311+
312+
add_source(
313+
span=span,
314+
project_root=client.options["project_root"],
315+
in_app_include=client.options.get("in_app_include"),
316+
in_app_exclude=client.options.get("in_app_exclude"),
317+
)
318+
319+
320+
def add_http_request_source(span):
321+
# type: (sentry_sdk.tracing.Span) -> None
322+
"""
323+
Adds OTel compatible source code information to a span for an outgoing HTTP request
324+
"""
325+
client = sentry_sdk.get_client()
326+
if not client.is_active():
327+
return
328+
329+
if span.timestamp is None or span.start_timestamp is None:
330+
return
331+
332+
should_add_request_source = client.options.get("enable_http_request_source", True)
333+
if not should_add_request_source:
334+
return
335+
336+
duration = span.timestamp - span.start_timestamp
337+
threshold = client.options.get("http_request_source_threshold_ms", 0)
338+
print("division: ", duration / timedelta(milliseconds=1), threshold)
339+
slow_query = duration / timedelta(milliseconds=1) > threshold
340+
341+
if not slow_query:
342+
return
343+
344+
add_source(
345+
span=span,
346+
project_root=client.options["project_root"],
347+
in_app_include=client.options.get("in_app_include"),
348+
in_app_exclude=client.options.get("in_app_exclude"),
349+
)
350+
351+
312352
def extract_sentrytrace_data(header):
313353
# type: (Optional[str]) -> Optional[Dict[str, Union[str, bool, None]]]
314354
"""
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import os
2+
import sys
3+
4+
# Load `asyncpg_helpers` into the module search path to test query source path names relative to module. See
5+
# `test_query_source_with_module_in_search_path`
6+
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))

tests/integrations/stdlib/httplib_helpers/__init__.py

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def get_request_with_connection(connection, url):
2+
connection.request("GET", url)
3+
connection.getresponse()

0 commit comments

Comments
 (0)