Skip to content

Commit 7bc573b

Browse files
committed
Merge branch 'potel-base' into antonpirker/potel/anthropic
2 parents 4292df8 + e7218da commit 7bc573b

36 files changed

+495
-264
lines changed

.github/workflows/codeql-analysis.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ on:
1717
- master
1818
- sentry-sdk-2.0
1919
pull_request:
20-
# The branches below must be a subset of the branches above
21-
branches:
22-
- master
23-
- sentry-sdk-2.0
2420
schedule:
2521
- cron: '18 18 * * 3'
2622

23+
# Cancel in progress workflows on pull_requests.
24+
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
25+
concurrency:
26+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
27+
cancel-in-progress: true
28+
2729
permissions:
2830
contents: read
2931

.github/workflows/enforce-license-compliance.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ on:
88
- release/*
99
- sentry-sdk-2.0
1010
pull_request:
11-
branches:
12-
- master
13-
- main
14-
- sentry-sdk-2.0
11+
12+
# Cancel in progress workflows on pull_requests.
13+
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
14+
concurrency:
15+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
1516

1617
jobs:
1718
enforce-license-compliance:

MIGRATION_GUIDE.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# Sentry SDK Migration Guide
22

3-
43
## Upgrading to 3.0
54

65
Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of what's changed. Looking for a more digestible summary? See the [guide in the docs](https://docs.sentry.io/platforms/python/migration/2.x-to-3.x) with the most common migration patterns.
@@ -19,11 +18,14 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh
1918
- `sentry_sdk.continue_trace` no longer returns a `Transaction` and is now a context manager.
2019
- Redis integration: In Redis pipeline spans there is no `span["data"]["redis.commands"]` that contains a dict `{"count": 3, "first_ten": ["cmd1", "cmd2", ...]}` but instead `span["data"]["redis.commands.count"]` (containing `3`) and `span["data"]["redis.commands.first_ten"]` (containing `["cmd1", "cmd2", ...]`).
2120
- clickhouse-driver integration: The query is now available under the `db.query.text` span attribute (only if `send_default_pii` is `True`).
21+
- `sentry_sdk.init` now returns `None` instead of a context manager.
2222

2323
### Removed
2424

2525
- Spans no longer have a `description`. Use `name` instead.
2626
- Dropped support for Python 3.6.
27+
- The PyMongo integration no longer sets tags. The data is still accessible via span attributes.
28+
- The PyMongo integration doesn't set `operation_ids` anymore. The individual IDs (`operation_id`, `request_id`, `session_id`) are now accessible as separate span attributes.
2729
- `sentry_sdk.metrics` and associated metrics APIs have been removed as Sentry no longer accepts metrics data in this form. See https://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-to-Metrics
2830
- The experimental options `enable_metrics`, `before_emit_metric` and `metric_code_locations` have been removed.
2931
- When setting span status, the HTTP status code is no longer automatically added as a tag.
@@ -38,7 +40,6 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh
3840
- `continue_from_headers`, `continue_from_environ` and `from_traceparent` have been removed, please use top-level API `sentry_sdk.continue_trace` instead.
3941
- `PropagationContext` constructor no longer takes a `dynamic_sampling_context` but takes a `baggage` object instead.
4042

41-
4243
### Deprecated
4344

4445
- `sentry_sdk.start_transaction` is deprecated. Use `sentry_sdk.start_span` instead.

requirements-devenv.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
-r requirements-linting.txt
22
-r requirements-testing.txt
33
mockupdb # required by `pymongo` tests that are enabled by `pymongo` from linter requirements
4-
pytest<7.0.0 # https://github.com/pytest-dev/pytest/issues/9621; see tox.ini
4+
pytest
55
pytest-asyncio

sentry_sdk/_init_implementation.py

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,11 @@
33
import sentry_sdk
44

55
if TYPE_CHECKING:
6-
from typing import Any, ContextManager, Optional
6+
from typing import Any, Optional
77

88
import sentry_sdk.consts
99

1010

11-
class _InitGuard:
12-
def __init__(self, client):
13-
# type: (sentry_sdk.Client) -> None
14-
self._client = client
15-
16-
def __enter__(self):
17-
# type: () -> _InitGuard
18-
return self
19-
20-
def __exit__(self, exc_type, exc_value, tb):
21-
# type: (Any, Any, Any) -> None
22-
c = self._client
23-
if c is not None:
24-
c.close()
25-
26-
2711
def _check_python_deprecations():
2812
# type: () -> None
2913
# Since we're likely to deprecate Python versions in the future, I'm keeping
@@ -33,16 +17,14 @@ def _check_python_deprecations():
3317

3418

3519
def _init(*args, **kwargs):
36-
# type: (*Optional[str], **Any) -> ContextManager[Any]
20+
# type: (*Optional[str], **Any) -> None
3721
"""Initializes the SDK and optionally integrations.
3822
3923
This takes the same arguments as the client constructor.
4024
"""
4125
client = sentry_sdk.Client(*args, **kwargs)
4226
sentry_sdk.get_global_scope().set_client(client)
4327
_check_python_deprecations()
44-
rv = _InitGuard(client)
45-
return rv
4628

4729

4830
if TYPE_CHECKING:
@@ -52,7 +34,7 @@ def _init(*args, **kwargs):
5234
# Use `ClientConstructor` to define the argument types of `init` and
5335
# `ContextManager[Any]` to tell static analyzers about the return type.
5436

55-
class init(sentry_sdk.consts.ClientConstructor, _InitGuard): # noqa: N801
37+
class init(sentry_sdk.consts.ClientConstructor): # noqa: N801
5638
pass
5739

5840
else:

sentry_sdk/integrations/aiohttp.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,17 @@ async def on_request_start(session, trace_config_ctx, params):
229229
% (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE),
230230
origin=AioHttpIntegration.origin,
231231
)
232-
span.set_data(SPANDATA.HTTP_METHOD, method)
232+
233+
data = {
234+
SPANDATA.HTTP_METHOD: method,
235+
}
233236
if parsed_url is not None:
234-
span.set_data("url", parsed_url.url)
235-
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
236-
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
237+
data["url"] = parsed_url.url
238+
data[SPANDATA.HTTP_QUERY] = parsed_url.query
239+
data[SPANDATA.HTTP_FRAGMENT] = parsed_url.fragment
240+
241+
for key, value in data.items():
242+
span.set_data(key, value)
237243

238244
client = sentry_sdk.get_client()
239245

@@ -258,12 +264,23 @@ async def on_request_start(session, trace_config_ctx, params):
258264
params.headers[key] = value
259265

260266
trace_config_ctx.span = span
267+
trace_config_ctx.span_data = data
261268

262269
async def on_request_end(session, trace_config_ctx, params):
263270
# type: (ClientSession, SimpleNamespace, TraceRequestEndParams) -> None
264271
if trace_config_ctx.span is None:
265272
return
266273

274+
span_data = trace_config_ctx.span_data or {}
275+
span_data[SPANDATA.HTTP_STATUS_CODE] = int(params.response.status)
276+
span_data["reason"] = params.response.reason
277+
278+
sentry_sdk.add_breadcrumb(
279+
type="http",
280+
category="httplib",
281+
data=span_data,
282+
)
283+
267284
span = trace_config_ctx.span
268285
span.set_http_status(int(params.response.status))
269286
span.set_data("reason", params.response.reason)

sentry_sdk/integrations/asyncpg.py

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22
import contextlib
3-
from typing import Any, TypeVar, Callable, Awaitable, Iterator
3+
from typing import Any, TypeVar, Callable, Awaitable, Iterator, Optional
44

55
import sentry_sdk
66
from sentry_sdk.consts import OP, SPANDATA
@@ -21,6 +21,7 @@
2121
except ImportError:
2222
raise DidNotEnable("asyncpg not installed.")
2323

24+
2425
# asyncpg.__version__ is a string containing the semantic version in the form of "<major>.<minor>.<patch>"
2526
asyncpg_version = parse_version(asyncpg.__version__)
2627

@@ -123,10 +124,13 @@ def _wrap_connection_method(
123124
async def _inner(*args: Any, **kwargs: Any) -> T:
124125
if sentry_sdk.get_client().get_integration(AsyncPGIntegration) is None:
125126
return await f(*args, **kwargs)
127+
126128
query = args[1]
127129
params_list = args[2] if len(args) > 2 else None
130+
128131
with _record(None, query, params_list, executemany=executemany) as span:
129-
_set_db_data(span, args[0])
132+
data = _get_db_data(conn=args[0])
133+
_set_on_span(span, data)
130134
res = await f(*args, **kwargs)
131135

132136
return res
@@ -146,7 +150,8 @@ def _inner(*args: Any, **kwargs: Any) -> T: # noqa: N807
146150
params_list,
147151
executemany=False,
148152
) as span:
149-
_set_db_data(span, args[0])
153+
data = _get_db_data(conn=args[0])
154+
_set_on_span(span, data)
150155
res = f(*args, **kwargs)
151156
span.set_attribute("db.cursor", _serialize_span_attribute(res))
152157

@@ -160,41 +165,19 @@ async def _inner(*args: Any, **kwargs: Any) -> T:
160165
if sentry_sdk.get_client().get_integration(AsyncPGIntegration) is None:
161166
return await f(*args, **kwargs)
162167

163-
user = kwargs["params"].user
164-
database = kwargs["params"].database
165-
166168
with sentry_sdk.start_span(
167169
op=OP.DB,
168170
name="connect",
169171
origin=AsyncPGIntegration.origin,
170172
) as span:
171-
span.set_attribute(SPANDATA.DB_SYSTEM, "postgresql")
172-
addr = kwargs.get("addr")
173-
if addr:
174-
try:
175-
span.set_attribute(SPANDATA.SERVER_ADDRESS, addr[0])
176-
span.set_attribute(SPANDATA.SERVER_PORT, addr[1])
177-
except IndexError:
178-
pass
179-
180-
span.set_attribute(SPANDATA.DB_NAME, database)
181-
span.set_attribute(SPANDATA.DB_USER, user)
173+
data = _get_db_data(
174+
addr=kwargs.get("addr"),
175+
database=kwargs["params"].database,
176+
user=kwargs["params"].user,
177+
)
178+
_set_on_span(span, data)
182179

183180
with capture_internal_exceptions():
184-
data = {}
185-
for attr in (
186-
"db.cursor",
187-
"db.params",
188-
"db.paramstyle",
189-
SPANDATA.DB_NAME,
190-
SPANDATA.DB_SYSTEM,
191-
SPANDATA.DB_USER,
192-
SPANDATA.SERVER_ADDRESS,
193-
SPANDATA.SERVER_PORT,
194-
):
195-
if span.get_attribute(attr):
196-
data[attr] = span.get_attribute(attr)
197-
198181
sentry_sdk.add_breadcrumb(
199182
message="connect", category="query", data=data
200183
)
@@ -206,21 +189,37 @@ async def _inner(*args: Any, **kwargs: Any) -> T:
206189
return _inner
207190

208191

209-
def _set_db_data(span: Span, conn: Any) -> None:
210-
span.set_attribute(SPANDATA.DB_SYSTEM, "postgresql")
192+
def _get_db_data(
193+
conn: Any = None,
194+
addr: Optional[tuple[str]] = None,
195+
database: Optional[str] = None,
196+
user: Optional[str] = None,
197+
) -> dict[str, str]:
198+
if conn is not None:
199+
addr = conn._addr
200+
database = conn._params.database
201+
user = conn._params.user
202+
203+
data = {
204+
SPANDATA.DB_SYSTEM: "postgresql",
205+
}
211206

212-
addr = conn._addr
213207
if addr:
214208
try:
215-
span.set_attribute(SPANDATA.SERVER_ADDRESS, addr[0])
216-
span.set_attribute(SPANDATA.SERVER_PORT, addr[1])
209+
data[SPANDATA.SERVER_ADDRESS] = addr[0]
210+
data[SPANDATA.SERVER_PORT] = addr[1]
217211
except IndexError:
218212
pass
219213

220-
database = conn._params.database
221214
if database:
222-
span.set_attribute(SPANDATA.DB_NAME, database)
215+
data[SPANDATA.DB_NAME] = database
223216

224-
user = conn._params.user
225217
if user:
226-
span.set_attribute(SPANDATA.DB_USER, user)
218+
data[SPANDATA.DB_USER] = user
219+
220+
return data
221+
222+
223+
def _set_on_span(span: Span, data: dict[str, Any]):
224+
for key, value in data.items():
225+
span.set_attribute(key, value)

sentry_sdk/integrations/boto3.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,20 @@ def _sentry_request_created(service_id, request, operation_name, **kwargs):
7474
origin=Boto3Integration.origin,
7575
)
7676

77+
data = {
78+
SPANDATA.HTTP_METHOD: request.method,
79+
}
7780
with capture_internal_exceptions():
7881
parsed_url = parse_url(request.url, sanitize=False)
79-
span.set_data("aws.request.url", parsed_url.url)
80-
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
81-
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
82+
data["aws.request.url"] = parsed_url.url
83+
data[SPANDATA.HTTP_QUERY] = parsed_url.query
84+
data[SPANDATA.HTTP_FRAGMENT] = parsed_url.fragment
85+
86+
for key, value in data.items():
87+
span.set_data(key, value)
8288

8389
span.set_tag("aws.service_id", service_id)
8490
span.set_tag("aws.operation_name", operation_name)
85-
span.set_data(SPANDATA.HTTP_METHOD, request.method)
8691

8792
# We do it in order for subsequent http calls/retries be
8893
# attached to this span.
@@ -91,6 +96,7 @@ def _sentry_request_created(service_id, request, operation_name, **kwargs):
9196
# request.context is an open-ended data-structure
9297
# where we can add anything useful in request life cycle.
9398
request.context["_sentrysdk_span"] = span
99+
request.context["_sentrysdk_span_data"] = data
94100

95101

96102
def _sentry_after_call(context, parsed, **kwargs):
@@ -100,6 +106,15 @@ def _sentry_after_call(context, parsed, **kwargs):
100106
# Span could be absent if the integration is disabled.
101107
if span is None:
102108
return
109+
110+
span_data = context.pop("_sentrysdk_span_data", {})
111+
112+
sentry_sdk.add_breadcrumb(
113+
type="http",
114+
category="httplib",
115+
data=span_data,
116+
)
117+
103118
span.__exit__(None, None, None)
104119

105120
body = parsed.get("Body")
@@ -143,4 +158,13 @@ def _sentry_after_call_error(context, exception, **kwargs):
143158
# Span could be absent if the integration is disabled.
144159
if span is None:
145160
return
161+
162+
span_data = context.pop("_sentrysdk_span_data", {})
163+
164+
sentry_sdk.add_breadcrumb(
165+
type="http",
166+
category="httplib",
167+
data=span_data,
168+
)
169+
146170
span.__exit__(type(exception), exception, None)

sentry_sdk/integrations/django/asgi.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ def wrap_async_view(callback):
172172
@functools.wraps(callback)
173173
async def sentry_wrapped_callback(request, *args, **kwargs):
174174
# type: (Any, *Any, **Any) -> Any
175+
current_scope = sentry_sdk.get_current_scope()
176+
if current_scope.transaction is not None:
177+
current_scope.transaction.update_active_thread()
178+
175179
sentry_scope = sentry_sdk.get_isolation_scope()
176180
if sentry_scope.profile is not None:
177181
sentry_scope.profile.update_active_thread_id()

sentry_sdk/integrations/django/views.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ def _wrap_sync_view(callback):
7676
@functools.wraps(callback)
7777
def sentry_wrapped_callback(request, *args, **kwargs):
7878
# type: (Any, *Any, **Any) -> Any
79+
current_scope = sentry_sdk.get_current_scope()
80+
if current_scope.transaction is not None:
81+
current_scope.transaction.update_active_thread()
82+
7983
sentry_scope = sentry_sdk.get_isolation_scope()
8084
# set the active thread id to the handler thread for sync views
8185
# this isn't necessary for async views since that runs on main

0 commit comments

Comments
 (0)