Skip to content

Commit 78b0f09

Browse files
committed
Merge branch 'master' into py-2391-fix-deadlock-in-batcher-k4wvs
2 parents 4bf5dac + 051cc01 commit 78b0f09

23 files changed

Lines changed: 2537 additions & 904 deletions

.github/workflows/update-tox.yml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,16 @@ jobs:
5555
5656
- name: Create pull request
5757
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
58+
env:
59+
BRANCH_NAME: ${{ steps.create-branch.outputs.branch_name }}
60+
COMMIT_TITLE: ${{ steps.create-branch.outputs.commit_title }}
61+
DATE: ${{ steps.create-branch.outputs.date }}
62+
BASE_BRANCH: ${{ github.ref_name }}
5863
with:
5964
script: |
60-
const branchName = '${{ steps.create-branch.outputs.branch_name }}';
61-
const commitTitle = '${{ steps.create-branch.outputs.commit_title }}';
62-
const date = '${{ steps.create-branch.outputs.date }}';
65+
const branchName = process.env.BRANCH_NAME;
66+
const commitTitle = process.env.COMMIT_TITLE;
67+
const date = process.env.DATE;
6368
const prBody = `Update our test matrix with new releases of integrated frameworks and libraries.
6469
6570
## How it works
@@ -100,7 +105,7 @@ jobs:
100105
repo: context.repo.repo,
101106
title: commitTitle + ' (' + date + ')',
102107
head: branchName,
103-
base: '${{ github.ref_name }}',
108+
base: process.env.BASE_BRANCH,
104109
body: prBody,
105110
});
106111

sentry_sdk/consts.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,12 @@ class SPANDATA:
881881
Example: "tcp", "udp", "unix"
882882
"""
883883

884+
PROCESS_PID = "process.pid"
885+
"""
886+
The process ID of the running process.
887+
Example: 12345
888+
"""
889+
884890
PROFILER_ID = "profiler_id"
885891
"""
886892
Label identifying the profiler id that the span occurred in. This should be a string.
@@ -924,6 +930,24 @@ class SPANDATA:
924930
Example: "MainThread"
925931
"""
926932

933+
URL_FULL = "url.full"
934+
"""
935+
The URL of the resource that was fetched.
936+
Example: "https://example.com/test?foo=bar#buzz"
937+
"""
938+
939+
URL_FRAGMENT = "url.fragment"
940+
"""
941+
The fragments present in the URI. Note that this does not contain the leading # character, while the `http.fragment` attribute does.
942+
Example: "details"
943+
"""
944+
945+
URL_QUERY = "url.query"
946+
"""
947+
The query string present in the URL. Note that this does not contain the leading ? character, while the `http.query` attribute does.
948+
Example: "foo=bar&bar=baz"
949+
"""
950+
927951
MCP_TOOL_NAME = "mcp.tool.name"
928952
"""
929953
The name of the MCP tool being called.

sentry_sdk/integrations/_wsgi_common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"HTTP_SET_COOKIE",
3232
"HTTP_COOKIE",
3333
"HTTP_AUTHORIZATION",
34+
"HTTP_PROXY_AUTHORIZATION",
3435
"HTTP_X_API_KEY",
3536
"HTTP_X_FORWARDED_FOR",
3637
"HTTP_X_REAL_IP",

sentry_sdk/integrations/fastapi.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from sentry_sdk.scope import should_send_default_pii
88
from sentry_sdk.traces import NoOpStreamedSpan, StreamedSpan
99
from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource
10+
from sentry_sdk.tracing_utils import has_span_streaming_enabled
1011
from sentry_sdk.utils import transaction_from_function
1112

1213
from typing import TYPE_CHECKING
@@ -19,6 +20,7 @@
1920
from sentry_sdk.integrations.starlette import (
2021
StarletteIntegration,
2122
StarletteRequestExtractor,
23+
_set_request_body_data_on_streaming_segment,
2224
)
2325
except DidNotEnable:
2426
raise DidNotEnable("Starlette is not installed")
@@ -109,7 +111,8 @@ def _sentry_call(*args: "Any", **kwargs: "Any") -> "Any":
109111
old_app = old_get_request_handler(*args, **kwargs)
110112

111113
async def _sentry_app(*args: "Any", **kwargs: "Any") -> "Any":
112-
integration = sentry_sdk.get_client().get_integration(FastApiIntegration)
114+
client = sentry_sdk.get_client()
115+
integration = client.get_integration(FastApiIntegration)
113116
if integration is None:
114117
return await old_app(*args, **kwargs)
115118

@@ -144,6 +147,9 @@ def event_processor(event: "Event", hint: "Dict[str, Any]") -> "Event":
144147
_make_request_event_processor(request, integration)
145148
)
146149

150+
if has_span_streaming_enabled(client.options):
151+
_set_request_body_data_on_streaming_segment(info)
152+
147153
return await old_app(*args, **kwargs)
148154

149155
return _sentry_app

sentry_sdk/integrations/sqlalchemy.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
from sentry_sdk.consts import SPANSTATUS, SPANDATA
22
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
3-
from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
3+
from sentry_sdk.tracing_utils import (
4+
add_query_source,
5+
record_sql_queries_supporting_streaming,
6+
)
47
from sentry_sdk.utils import (
58
capture_internal_exceptions,
69
ensure_integration_enabled,
710
parse_version,
811
)
12+
from sentry_sdk.traces import StreamedSpan, SpanStatus
13+
from sentry_sdk.tracing import Span
914

1015
try:
1116
from sqlalchemy.engine import Engine # type: ignore
@@ -20,8 +25,7 @@
2025
from typing import Any
2126
from typing import ContextManager
2227
from typing import Optional
23-
24-
from sentry_sdk.tracing import Span
28+
from typing import Union
2529

2630

2731
class SqlalchemyIntegration(Integration):
@@ -48,7 +52,7 @@ def _before_cursor_execute(
4852
executemany: bool,
4953
*args: "Any",
5054
) -> None:
51-
ctx_mgr = record_sql_queries(
55+
ctx_mgr = record_sql_queries_supporting_streaming(
5256
cursor,
5357
statement,
5458
parameters,
@@ -78,12 +82,19 @@ def _after_cursor_execute(
7882
context, "_sentry_sql_span_manager", None
7983
)
8084

85+
# Record query source immediately before span is finished: accurate end timestamp and before the span is flushed.
86+
span: "Optional[Union[Span, StreamedSpan]]" = getattr(
87+
context, "_sentry_sql_span", None
88+
)
89+
if isinstance(span, StreamedSpan):
90+
with capture_internal_exceptions():
91+
add_query_source(span)
92+
8193
if ctx_mgr is not None:
8294
context._sentry_sql_span_manager = None
8395
ctx_mgr.__exit__(None, None, None)
8496

85-
span: "Optional[Span]" = getattr(context, "_sentry_sql_span", None)
86-
if span is not None:
97+
if isinstance(span, Span):
8798
with capture_internal_exceptions():
8899
add_query_source(span)
89100

@@ -96,7 +107,10 @@ def _handle_error(context: "Any", *args: "Any") -> None:
96107
span: "Optional[Span]" = getattr(execution_context, "_sentry_sql_span", None)
97108

98109
if span is not None:
99-
span.set_status(SPANSTATUS.INTERNAL_ERROR)
110+
if isinstance(span, StreamedSpan):
111+
span.status = SpanStatus.ERROR
112+
else:
113+
span.set_status(SPANSTATUS.INTERNAL_ERROR)
100114

101115
# _after_cursor_execute does not get called for crashing SQL stmts. Judging
102116
# from SQLAlchemy codebase it does seem like any error coming into this
@@ -132,29 +146,43 @@ def _get_db_system(name: str) -> "Optional[str]":
132146
return None
133147

134148

135-
def _set_db_data(span: "Span", conn: "Any") -> None:
149+
def _set_db_data(span: "Union[Span, StreamedSpan]", conn: "Any") -> None:
136150
db_system = _get_db_system(conn.engine.name)
137-
if db_system is not None:
138-
span.set_data(SPANDATA.DB_SYSTEM, db_system)
151+
152+
if isinstance(span, StreamedSpan):
153+
if db_system is not None:
154+
span.set_attribute(SPANDATA.DB_SYSTEM_NAME, db_system)
155+
else:
156+
if db_system is not None:
157+
span.set_data(SPANDATA.DB_SYSTEM, db_system)
158+
159+
if isinstance(span, StreamedSpan):
160+
set_on_span = span.set_attribute
161+
else:
162+
set_on_span = span.set_data
139163

140164
try:
141165
driver = conn.dialect.driver
142166
if driver:
143-
span.set_data(SPANDATA.DB_DRIVER_NAME, driver)
167+
set_on_span(SPANDATA.DB_DRIVER_NAME, driver)
144168
except Exception:
145169
pass
146170

147171
if conn.engine.url is None:
148172
return
149173

150174
db_name = conn.engine.url.database
151-
if db_name is not None:
152-
span.set_data(SPANDATA.DB_NAME, db_name)
175+
if isinstance(span, StreamedSpan):
176+
if db_name is not None:
177+
span.set_attribute(SPANDATA.DB_NAMESPACE, db_name)
178+
else:
179+
if db_name is not None:
180+
span.set_data(SPANDATA.DB_NAME, db_name)
153181

154182
server_address = conn.engine.url.host
155183
if server_address is not None:
156-
span.set_data(SPANDATA.SERVER_ADDRESS, server_address)
184+
set_on_span(SPANDATA.SERVER_ADDRESS, server_address)
157185

158186
server_port = conn.engine.url.port
159187
if server_port is not None:
160-
span.set_data(SPANDATA.SERVER_PORT, server_port)
188+
set_on_span(SPANDATA.SERVER_PORT, server_port)

sentry_sdk/integrations/starlette.py

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ async def _sentry_send(*args: "Any", **kwargs: "Any") -> "Any":
241241
return middleware_class
242242

243243

244-
def _serialize_body_data(data: "Any") -> str:
244+
def _serialize_request_body_data(data: "Any") -> str:
245245
# data may be a JSON-serializable value, an AnnotatedValue, or a dict with AnnotatedValue values
246246
def _default(value: "Any") -> "Any":
247247
if isinstance(value, AnnotatedValue):
@@ -251,6 +251,23 @@ def _default(value: "Any") -> "Any":
251251
return json.dumps(data, default=_default)
252252

253253

254+
def _set_request_body_data_on_streaming_segment(
255+
info: "Optional[Dict[str, Any]]",
256+
) -> None:
257+
current_span = sentry_sdk.get_current_span()
258+
if (
259+
info
260+
and "data" in info
261+
and isinstance(current_span, StreamedSpan)
262+
and not isinstance(current_span, NoOpStreamedSpan)
263+
):
264+
with capture_internal_exceptions():
265+
current_span._segment.set_attribute(
266+
"http.request.body.data",
267+
_serialize_request_body_data(info["data"]),
268+
)
269+
270+
254271
@ensure_integration_enabled(StarletteIntegration)
255272
def _capture_exception(exception: BaseException, handled: "Any" = False) -> None:
256273
event, hint = event_from_exception(
@@ -517,23 +534,8 @@ def event_processor(
517534
_make_request_event_processor(request, integration)
518535
)
519536

520-
is_span_streaming_enabled = has_span_streaming_enabled(client.options)
521-
if is_span_streaming_enabled:
522-
current_span = sentry_sdk.get_current_span()
523-
524-
if (
525-
info
526-
and "data" in info
527-
and isinstance(current_span, StreamedSpan)
528-
and not isinstance(current_span, NoOpStreamedSpan)
529-
):
530-
data = info["data"]
531-
532-
with capture_internal_exceptions():
533-
current_span._segment.set_attribute(
534-
"http.request.body.data",
535-
_serialize_body_data(data),
536-
)
537+
if has_span_streaming_enabled(client.options):
538+
_set_request_body_data_on_streaming_segment(info)
537539

538540
return await old_func(*args, **kwargs)
539541

0 commit comments

Comments
 (0)