Skip to content

Commit 17b42e6

Browse files
Merge branch 'webb/boto3/span-first' of github.com:getsentry/sentry-python into webb/boto3/span-first
2 parents c777a83 + 8121605 commit 17b42e6

16 files changed

Lines changed: 647 additions & 195 deletions

File tree

CHANGELOG.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,88 @@
11
# Changelog
22

3+
## 2.59.0
4+
5+
### New Features ✨
6+
7+
#### Langchain
8+
9+
- Record `run_name` as `gen_ai.function_id` on Invoke Agent Spans by @alexander-alderman-webb in [#5926](https://github.com/getsentry/sentry-python/pull/5926)
10+
- Record `run_name` in `on_tool_start` by @alexander-alderman-webb in [#5925](https://github.com/getsentry/sentry-python/pull/5925)
11+
- Record `run_name` in `on_chat_model_start` by @alexander-alderman-webb in [#5924](https://github.com/getsentry/sentry-python/pull/5924)
12+
13+
#### Other
14+
15+
- (ci) Cancel in-progress PR workflows on new commit push by @joshuarli in [#5994](https://github.com/getsentry/sentry-python/pull/5994)
16+
- (consts) Add updated span convention constants to SPANDATA by @ericapisani in [#6093](https://github.com/getsentry/sentry-python/pull/6093)
17+
- (fastapi) Support span streaming in active thread tracking by @ericapisani in [#6118](https://github.com/getsentry/sentry-python/pull/6118)
18+
- (httpx) Migrate to span first by @ericapisani in [#6084](https://github.com/getsentry/sentry-python/pull/6084)
19+
- (huggingface_hub) Migrate to span first by @ericapisani in [#6124](https://github.com/getsentry/sentry-python/pull/6124)
20+
- (mcp) Migrate to span first by @ericapisani in [#6131](https://github.com/getsentry/sentry-python/pull/6131)
21+
- Add `db.driver.name` spans to database integrations by @ericapisani in [#6082](https://github.com/getsentry/sentry-python/pull/6082)
22+
23+
### Bug Fixes 🐛
24+
25+
We've put additional data that might contain sensitive information, like GraphQL documents, behind the `send_default_pii` option.
26+
27+
#### Httpx
28+
29+
- Consistently early-exit when adding request source by @alexander-alderman-webb in [#6151](https://github.com/getsentry/sentry-python/pull/6151)
30+
- Set `code.namespace` and `code.function` instead of `code.function.name` in span streaming by @alexander-alderman-webb in [#6150](https://github.com/getsentry/sentry-python/pull/6150)
31+
32+
#### Langchain
33+
34+
- Record `run_name` as `gen_ai.function_id` for text completions by @alexander-alderman-webb in [#6073](https://github.com/getsentry/sentry-python/pull/6073)
35+
- Set agent name as `gen_ai.agent.name` for chat and tool spans by @alexander-alderman-webb in [#5877](https://github.com/getsentry/sentry-python/pull/5877)
36+
37+
#### Other
38+
39+
- (asgi) Use `inspect.iscoroutinefunction` on Python 3.14+ by @alexander-alderman-webb in [#6135](https://github.com/getsentry/sentry-python/pull/6135)
40+
- (batcher) Reset lock and flusher in child after fork by @ericapisani in [#6163](https://github.com/getsentry/sentry-python/pull/6163)
41+
- (google_genai) Redact binary data in inline_data and fix multi-part message extraction by @ericapisani in [#5977](https://github.com/getsentry/sentry-python/pull/5977)
42+
- (grpc) Add isolation_scope to async server interceptor by @robinvd in [#5940](https://github.com/getsentry/sentry-python/pull/5940)
43+
- (metrics,logs) Don't attach `span_id` if no active span by @sentrivana in [#6162](https://github.com/getsentry/sentry-python/pull/6162)
44+
- (monitor) Release `Monitor._thread_lock` after fork (#6148) by @vokracko in [#6159](https://github.com/getsentry/sentry-python/pull/6159)
45+
- (openai-agents) Resolve agent from `bindings` for openai-agents >= 0.14 by @ericapisani in [#6102](https://github.com/getsentry/sentry-python/pull/6102)
46+
- (profiler) Stop nulling buffer on teardown by @ericapisani in [#6075](https://github.com/getsentry/sentry-python/pull/6075)
47+
- (quart) Use `inspect.iscoroutinefunction` when Quart does by @alexander-alderman-webb in [#6133](https://github.com/getsentry/sentry-python/pull/6133)
48+
- (security) Prevent GitHub script injection in update-tox workflow by @fix-it-felix-sentry in [#6171](https://github.com/getsentry/sentry-python/pull/6171)
49+
- (starlette/fastapi) Use `inspect.iscoroutinefunction` when Starlette does by @alexander-alderman-webb in [#6134](https://github.com/getsentry/sentry-python/pull/6134)
50+
- (tornado) Make sure context manager doesn't double yield by @sentrivana in [#6152](https://github.com/getsentry/sentry-python/pull/6152)
51+
- Introduce `_get_current_streamed_span()` to keep types backwards compatible by @alexander-alderman-webb in [#6177](https://github.com/getsentry/sentry-python/pull/6177)
52+
53+
### Internal Changes 🔧
54+
55+
#### Stdlib
56+
57+
- Pin timestamps in `add_http_request_source()` patch by @alexander-alderman-webb in [#6160](https://github.com/getsentry/sentry-python/pull/6160)
58+
- Stop mocking `HTTPSConnection.send` in trace header tests by @alexander-alderman-webb in [#6156](https://github.com/getsentry/sentry-python/pull/6156)
59+
60+
#### Other
61+
62+
- (batcher) Only flush the bucket that triggered the flush event by @sentrivana in [#6168](https://github.com/getsentry/sentry-python/pull/6168)
63+
- (celery) Remove unused `NoOpMgr` from utils by @sentrivana in [#6078](https://github.com/getsentry/sentry-python/pull/6078)
64+
- (ci) Update outdated pinned action version comments by @JoshuaMoelans in [#6088](https://github.com/getsentry/sentry-python/pull/6088)
65+
- (fastmcp) Span streaming tests by @alexander-alderman-webb in [#6167](https://github.com/getsentry/sentry-python/pull/6167)
66+
- (graphql) Update document setting by @sentrivana in [#6153](https://github.com/getsentry/sentry-python/pull/6153)
67+
- (grpc) Mirror sync interceptor by @sentrivana in [#6158](https://github.com/getsentry/sentry-python/pull/6158)
68+
- (langchain) Separate test to remove conditional by @alexander-alderman-webb in [#6076](https://github.com/getsentry/sentry-python/pull/6076)
69+
- (pydantic-ai) Remove dead `Model.request` patch by @alexander-alderman-webb in [#5956](https://github.com/getsentry/sentry-python/pull/5956)
70+
- (queues) Update integrations by @sentrivana in [#6157](https://github.com/getsentry/sentry-python/pull/6157)
71+
- (tests) Replace deprecated `enable_tracing`with `traces_sample_rate` by @sentrivana in [#6077](https://github.com/getsentry/sentry-python/pull/6077)
72+
- (transport) Remove redundant checks for dsn by @psh9508 in [#6104](https://github.com/getsentry/sentry-python/pull/6104)
73+
- Rename file by @sentrivana in [#6194](https://github.com/getsentry/sentry-python/pull/6194)
74+
- 🤖 Update test matrix with new releases (05/04) by @github-actions in [#6186](https://github.com/getsentry/sentry-python/pull/6186)
75+
- Assert presence of profile chunks after shutdown by @alexander-alderman-webb in [#6174](https://github.com/getsentry/sentry-python/pull/6174)
76+
- 🤖 Update test matrix with new releases (04/29) by @github-actions in [#6173](https://github.com/getsentry/sentry-python/pull/6173)
77+
- Limit `update-tox` action to master branch by @alexander-alderman-webb in [#6172](https://github.com/getsentry/sentry-python/pull/6172)
78+
- Expand scrubbing by @sentrivana in [#6161](https://github.com/getsentry/sentry-python/pull/6161)
79+
- Remove Python 2 compat from qualname_from_function by @ericapisani in [#6137](https://github.com/getsentry/sentry-python/pull/6137)
80+
- Update test matrix with new releases (04/27) by @alexander-alderman-webb in [#6146](https://github.com/getsentry/sentry-python/pull/6146)
81+
- Raise shutdown timeout in aws lambda tests by @sentrivana in [#6129](https://github.com/getsentry/sentry-python/pull/6129)
82+
- Join thread in continuous profiler test by @alexander-alderman-webb in [#6125](https://github.com/getsentry/sentry-python/pull/6125)
83+
- 🤖 Update test matrix with new releases (04/20) by @github-actions in [#6100](https://github.com/getsentry/sentry-python/pull/6100)
84+
- Set explicit base-branch for codecov action by @ericapisani in [#5992](https://github.com/getsentry/sentry-python/pull/5992)
85+
386
## 2.58.0
487

588
### New Features ✨

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
copyright = "2019-{}, Sentry Team and Contributors".format(datetime.now().year)
3232
author = "Sentry Team and Contributors"
3333

34-
release = "2.58.0"
34+
release = "2.59.0"
3535
version = ".".join(release.split(".")[:2]) # The short X.Y version.
3636

3737

requirements-linting.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ typer
2020
strawberry-graphql
2121
setuptools<82
2222
httpx
23+
botocore-stubs

scripts/build_aws_lambda_layer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def install_python_packages(self) -> None:
4949

5050
sentry_python_sdk = os.path.join(
5151
DIST_PATH,
52-
f"sentry_sdk-{SDK_VERSION}-py2.py3-none-any.whl", # this is generated by "make dist" that is called by "make aws-lambda-layer"
52+
f"sentry_sdk-{SDK_VERSION}-py3-none-any.whl", # this is generated by "make dist" that is called by "make aws-lambda-layer"
5353
)
5454
subprocess.run(
5555
[

sentry_sdk/_types.py

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66

77
from typing import TYPE_CHECKING, TypeVar, Union
88

9-
109
# Re-exported for compat, since code out there in the wild might use this variable.
1110
MYPY = TYPE_CHECKING
1211

1312

1413
SENSITIVE_DATA_SUBSTITUTE = "[Filtered]"
1514
BLOB_DATA_SUBSTITUTE = "[Blob substitute]"
15+
OVER_SIZE_LIMIT_SUBSTITUTE = "[Exceeds maximum size]"
16+
UNPARSABLE_RAW_DATA_SUBSTITUTE = "[Unparsable]"
1617

1718

1819
class AnnotatedValue:
@@ -47,6 +48,8 @@ def __len__(self: "AnnotatedValue") -> int:
4748
@classmethod
4849
def removed_because_raw_data(cls) -> "AnnotatedValue":
4950
"""The value was removed because it could not be parsed. This is done for request body values that are not json nor a form."""
51+
# This is the legacy approach - we want to transition over to `substituted_because_raw_data` after we completely transition
52+
# to span-first
5053
return AnnotatedValue(
5154
value="",
5255
metadata={
@@ -59,12 +62,29 @@ def removed_because_raw_data(cls) -> "AnnotatedValue":
5962
},
6063
)
6164

65+
@classmethod
66+
def substituted_because_raw_data(cls) -> "AnnotatedValue":
67+
"""The value was replaced because it could not be parsed. This is done for request body values that are not json nor a form."""
68+
return AnnotatedValue(
69+
value=UNPARSABLE_RAW_DATA_SUBSTITUTE,
70+
metadata={
71+
"rem": [ # Remark
72+
[
73+
"!raw", # Unparsable raw data
74+
"s", # The fields original value was substituted
75+
]
76+
]
77+
},
78+
)
79+
6280
@classmethod
6381
def removed_because_over_size_limit(cls, value: "Any" = "") -> "AnnotatedValue":
6482
"""
6583
The actual value was removed because the size of the field exceeded the configured maximum size,
6684
for example specified with the max_request_body_size sdk option.
6785
"""
86+
# This is the legacy approach - we want to transition over to `substituted_because_over_size_limit` after we completely transition
87+
# to span-first
6888
return AnnotatedValue(
6989
value=value,
7090
metadata={
@@ -77,6 +97,26 @@ def removed_because_over_size_limit(cls, value: "Any" = "") -> "AnnotatedValue":
7797
},
7898
)
7999

100+
@classmethod
101+
def substituted_because_over_size_limit(
102+
cls, value: "Any" = OVER_SIZE_LIMIT_SUBSTITUTE
103+
) -> "AnnotatedValue":
104+
"""
105+
The actual value was replaced because the size of the field exceeded the configured maximum size,
106+
for example specified with the max_request_body_size sdk option.
107+
"""
108+
return AnnotatedValue(
109+
value=value,
110+
metadata={
111+
"rem": [ # Remark
112+
[
113+
"!config", # Because of configured maximum size
114+
"s", # The fields original value was substituted
115+
]
116+
]
117+
},
118+
)
119+
80120
@classmethod
81121
def substituted_because_contains_sensitive_data(cls) -> "AnnotatedValue":
82122
"""The actual value was removed because it contained sensitive information."""
@@ -99,17 +139,10 @@ def substituted_because_contains_sensitive_data(cls) -> "AnnotatedValue":
99139

100140
if TYPE_CHECKING:
101141
from collections.abc import Container, MutableMapping, Sequence
102-
103142
from datetime import datetime
104-
105143
from types import TracebackType
106-
from typing import Any
107-
from typing import Callable
108-
from typing import Dict
109-
from typing import Mapping
110-
from typing import NotRequired
111-
from typing import Optional
112-
from typing import Type
144+
from typing import Any, Callable, Dict, Mapping, NotRequired, Optional, Type
145+
113146
from typing_extensions import Literal, TypedDict
114147

115148
class SDKInfo(TypedDict):

sentry_sdk/consts.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,12 @@ class SPANDATA:
893893
Example: "5249fbada8d5416482c2f6e47e337372"
894894
"""
895895

896+
RPC_METHOD = "rpc.method"
897+
"""
898+
The fully-qualified logical name of the method from the RPC interface perspective.
899+
Example: "com.example.ExampleService/exampleMethod"
900+
"""
901+
896902
SERVER_ADDRESS = "server.address"
897903
"""
898904
Name of the database host.
@@ -1645,4 +1651,4 @@ def _get_default_options() -> "dict[str, Any]":
16451651
del _get_default_options
16461652

16471653

1648-
VERSION = "2.58.0"
1654+
VERSION = "2.59.0"

sentry_sdk/integrations/_asgi_common.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ def _get_request_data(asgi_scope: "Any") -> "Dict[str, Any]":
9393
if ty in ("http", "websocket"):
9494
request_data["method"] = asgi_scope.get("method")
9595

96-
request_data["headers"] = headers = _filter_headers(_get_headers(asgi_scope))
96+
request_data["headers"] = headers = _filter_headers(
97+
_get_headers(asgi_scope),
98+
)
9799
request_data["query_string"] = _get_query(asgi_scope)
98100

99101
request_data["url"] = _get_url(

sentry_sdk/integrations/_wsgi_common.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,11 @@ def _filter_headers(
218218
if should_send_default_pii():
219219
return headers
220220

221-
substitute: "Union[AnnotatedValue, str]"
222-
if use_annotated_value:
223-
substitute = AnnotatedValue.removed_because_over_size_limit()
224-
else:
225-
substitute = SENSITIVE_DATA_SUBSTITUTE
221+
substitute: "Union[AnnotatedValue, str]" = (
222+
SENSITIVE_DATA_SUBSTITUTE
223+
if not use_annotated_value
224+
else AnnotatedValue.removed_because_over_size_limit()
225+
)
226226

227227
return {
228228
k: (v if k.upper().replace("-", "_") not in SENSITIVE_HEADERS else substitute)

sentry_sdk/integrations/asyncio.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
1-
import sys
21
import functools
2+
import sys
33

44
import sentry_sdk
55
from sentry_sdk.consts import OP
6-
from sentry_sdk.integrations import Integration, DidNotEnable
6+
from sentry_sdk.integrations import DidNotEnable, Integration
77
from sentry_sdk.integrations._wsgi_common import nullcontext
8+
from sentry_sdk.traces import StreamedSpan
9+
from sentry_sdk.tracing import Span
10+
from sentry_sdk.tracing_utils import has_span_streaming_enabled
11+
from sentry_sdk.transport import AsyncHttpTransport
812
from sentry_sdk.utils import (
913
event_from_exception,
14+
is_internal_task,
1015
logger,
1116
reraise,
12-
is_internal_task,
1317
)
14-
from sentry_sdk.transport import AsyncHttpTransport
1518

1619
try:
1720
import asyncio
1821
from asyncio.tasks import Task
1922
except ImportError:
2023
raise DidNotEnable("asyncio not available")
2124

22-
from typing import TYPE_CHECKING
25+
from typing import TYPE_CHECKING, Optional, Union
2326

2427
if TYPE_CHECKING:
25-
from typing import Any, Callable, TypeVar
2628
from collections.abc import Coroutine
29+
from typing import Any, Callable, TypeVar
2730

2831
from sentry_sdk._types import ExcInfo
2932

@@ -142,22 +145,31 @@ def _sentry_task_factory(
142145
@_wrap_coroutine(coro)
143146
async def _task_with_sentry_span_creation() -> "Any":
144147
result = None
145-
146-
integration = sentry_sdk.get_client().get_integration(
147-
AsyncioIntegration
148-
)
148+
client = sentry_sdk.get_client()
149+
integration = client.get_integration(AsyncioIntegration)
149150
task_spans = integration.task_spans if integration else False
150151

152+
span_ctx: "Optional[Union[StreamedSpan, Span]]" = None
153+
is_span_streaming_enabled = has_span_streaming_enabled(client.options)
154+
151155
with sentry_sdk.isolation_scope():
152-
with (
153-
sentry_sdk.start_span(
154-
op=OP.FUNCTION,
155-
name=get_name(coro),
156-
origin=AsyncioIntegration.origin,
157-
)
158-
if task_spans
159-
else nullcontext()
160-
):
156+
if task_spans:
157+
if is_span_streaming_enabled:
158+
span_ctx = sentry_sdk.traces.start_span(
159+
name=get_name(coro),
160+
attributes={
161+
"sentry.op": OP.FUNCTION,
162+
"sentry.origin": AsyncioIntegration.origin,
163+
},
164+
)
165+
else:
166+
span_ctx = sentry_sdk.start_span(
167+
op=OP.FUNCTION,
168+
name=get_name(coro),
169+
origin=AsyncioIntegration.origin,
170+
)
171+
172+
with span_ctx if span_ctx else nullcontext():
161173
try:
162174
result = await coro
163175
except StopAsyncIteration as e:

0 commit comments

Comments
 (0)