Skip to content

Commit 7d0dfa5

Browse files
authored
Merge branch 'master' into dependabot/github_actions/getsentry/codecov-action-afa60378e8e4939ddb77575a51da92885c61ca86
2 parents c2cf93c + 35151a9 commit 7d0dfa5

File tree

23 files changed

+735
-357
lines changed

23 files changed

+735
-357
lines changed

.github/workflows/ai-integration-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
token: ${{ secrets.GITHUB_TOKEN }}
3535

3636
- name: Run Python SDK Tests
37-
uses: getsentry/testing-ai-sdk-integrations@6b1f51ec8af03e19087df452b426aa7e46d2b20a
37+
uses: getsentry/testing-ai-sdk-integrations@1dd9ee2a2d821f41473fc90809a758e8394c689c
3838
env:
3939
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4040
with:

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
# This will also trigger "make dist" that creates the Python packages
5555
make aws-lambda-layer
5656
- name: Upload Python Packages
57-
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
57+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
5858
with:
5959
name: artifact-build_lambda_layer
6060
path: |
@@ -76,7 +76,7 @@ jobs:
7676
make apidocs
7777
cd docs/_build && zip -r gh-pages ./
7878
79-
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
79+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
8080
with:
8181
name: artifact-docs
8282
path: |

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
steps:
2323
- name: Get auth token
2424
id: token
25-
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v2
25+
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
2626
with:
2727
app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }}
2828
private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }}

scripts/populate_tox/package_dependencies.jsonl

Lines changed: 19 additions & 17 deletions
Large diffs are not rendered by default.

scripts/populate_tox/releases.jsonl

Lines changed: 37 additions & 36 deletions
Large diffs are not rendered by default.

sentry_sdk/_span_batcher.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,21 @@ def add(self, span: "StreamedSpan") -> None:
9191
@staticmethod
9292
def _estimate_size(item: "StreamedSpan") -> int:
9393
# Rough estimate of serialized span size that's quick to compute.
94-
# 210 is the rough size of the payload without attributes, and we
95-
# estimate additional 70 bytes on top of that per attribute.
96-
return 210 + 70 * len(item._attributes)
94+
# 210 is the rough size of the payload without attributes, and then we
95+
# estimate the attributes separately.
96+
estimate = 210
97+
for value in item._attributes.values():
98+
estimate += 50
99+
100+
if isinstance(value, str):
101+
estimate += len(value)
102+
else:
103+
estimate += len(str(value))
104+
105+
return estimate
97106

98107
@staticmethod
99108
def _to_transport_format(item: "StreamedSpan") -> "Any":
100-
# TODO[span-first]
101109
res: "dict[str, Any]" = {
102110
"trace_id": item.trace_id,
103111
"span_id": item.span_id,
@@ -126,7 +134,7 @@ def _flush(self) -> None:
126134
return
127135

128136
envelopes = []
129-
for trace_id, spans in self._span_buffer.items():
137+
for spans in self._span_buffer.values():
130138
if spans:
131139
dsc = spans[0]._dynamic_sampling_context()
132140

sentry_sdk/integrations/_asgi_common.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,33 @@ def _get_request_data(asgi_scope: "Any") -> "Dict[str, Any]":
105105
request_data["env"] = {"REMOTE_ADDR": _get_ip(asgi_scope)}
106106

107107
return request_data
108+
109+
110+
def _get_request_attributes(asgi_scope: "Any") -> "dict[str, Any]":
111+
"""
112+
Return attributes related to the HTTP request from the ASGI scope.
113+
"""
114+
attributes: "dict[str, Any]" = {}
115+
116+
ty = asgi_scope["type"]
117+
if ty in ("http", "websocket"):
118+
if asgi_scope.get("method"):
119+
attributes["http.request.method"] = asgi_scope["method"].upper()
120+
121+
headers = _filter_headers(_get_headers(asgi_scope), use_annotated_value=False)
122+
for header, value in headers.items():
123+
attributes[f"http.request.header.{header.lower()}"] = value
124+
125+
query = _get_query(asgi_scope)
126+
if query:
127+
attributes["http.query"] = query
128+
129+
attributes["url.full"] = _get_url(
130+
asgi_scope, "http" if ty == "http" else "ws", headers.get("host")
131+
)
132+
133+
client = asgi_scope.get("client")
134+
if client and should_send_default_pii():
135+
attributes["client.address"] = _get_ip(asgi_scope)
136+
137+
return attributes

sentry_sdk/integrations/_wsgi_common.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from copy import deepcopy
44

55
import sentry_sdk
6+
from sentry_sdk._types import SENSITIVE_DATA_SUBSTITUTE
67
from sentry_sdk.scope import should_send_default_pii
78
from sentry_sdk.utils import AnnotatedValue, logger
89

@@ -211,16 +212,19 @@ def _is_json_content_type(ct: "Optional[str]") -> bool:
211212

212213
def _filter_headers(
213214
headers: "Mapping[str, str]",
215+
use_annotated_value: bool = True,
214216
) -> "Mapping[str, Union[AnnotatedValue, str]]":
215217
if should_send_default_pii():
216218
return headers
217219

220+
substitute: "Union[AnnotatedValue, str]"
221+
if use_annotated_value:
222+
substitute = AnnotatedValue.removed_because_over_size_limit()
223+
else:
224+
substitute = SENSITIVE_DATA_SUBSTITUTE
225+
218226
return {
219-
k: (
220-
v
221-
if k.upper().replace("-", "_") not in SENSITIVE_HEADERS
222-
else AnnotatedValue.removed_because_over_size_limit()
223-
)
227+
k: (v if k.upper().replace("-", "_") not in SENSITIVE_HEADERS else substitute)
224228
for k, v in headers.items()
225229
}
226230

sentry_sdk/integrations/asgi.py

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from sentry_sdk.consts import OP
1616
from sentry_sdk.integrations._asgi_common import (
1717
_get_headers,
18+
_get_request_attributes,
1819
_get_request_data,
1920
_get_url,
2021
)
@@ -23,7 +24,11 @@
2324
nullcontext,
2425
)
2526
from sentry_sdk.sessions import track_session
26-
from sentry_sdk.traces import StreamedSpan
27+
from sentry_sdk.traces import (
28+
StreamedSpan,
29+
SegmentSource,
30+
SOURCE_FOR_STYLE as SEGMENT_SOURCE_FOR_STYLE,
31+
)
2732
from sentry_sdk.tracing import (
2833
SOURCE_FOR_STYLE,
2934
Transaction,
@@ -40,6 +45,7 @@
4045
_get_installed_modules,
4146
reraise,
4247
capture_internal_exceptions,
48+
qualname_from_function,
4349
)
4450

4551
from typing import TYPE_CHECKING
@@ -235,7 +241,7 @@ async def _run_app(
235241
transaction_source, "value", transaction_source
236242
),
237243
"sentry.origin": self.span_origin,
238-
"asgi.type": ty,
244+
"network.protocol.name": ty,
239245
}
240246

241247
if ty in ("http", "websocket"):
@@ -302,6 +308,12 @@ async def _run_app(
302308
)
303309

304310
with span_ctx as span:
311+
if isinstance(span, StreamedSpan):
312+
for attribute, value in _get_request_attributes(
313+
scope
314+
).items():
315+
span.set_attribute(attribute, value)
316+
305317
try:
306318

307319
async def _sentry_wrapped_send(
@@ -336,6 +348,7 @@ async def _sentry_wrapped_send(
336348
return await self.app(
337349
scope, receive, _sentry_wrapped_send
338350
)
351+
339352
except Exception as exc:
340353
suppress_chained_exceptions = (
341354
sentry_sdk.get_client()
@@ -350,6 +363,28 @@ async def _sentry_wrapped_send(
350363
with capture_internal_exceptions():
351364
self._capture_request_exception(exc)
352365
reraise(*exc_info)
366+
367+
finally:
368+
if isinstance(span, StreamedSpan):
369+
already_set = (
370+
span is not None
371+
and span.name != _DEFAULT_TRANSACTION_NAME
372+
and span.get_attributes().get("sentry.span.source")
373+
in [
374+
SegmentSource.COMPONENT.value,
375+
SegmentSource.ROUTE.value,
376+
SegmentSource.CUSTOM.value,
377+
]
378+
)
379+
with capture_internal_exceptions():
380+
if not already_set:
381+
name, source = (
382+
self._get_segment_name_and_source(
383+
self.transaction_style, scope
384+
)
385+
)
386+
span.name = name
387+
span.set_attribute("sentry.span.source", source)
353388
finally:
354389
_asgi_middleware_applied.set(False)
355390

@@ -424,3 +459,40 @@ def _get_transaction_name_and_source(
424459
return name, source
425460

426461
return name, source
462+
463+
def _get_segment_name_and_source(
464+
self: "SentryAsgiMiddleware", segment_style: str, asgi_scope: "Any"
465+
) -> "Tuple[str, str]":
466+
name = None
467+
source = SEGMENT_SOURCE_FOR_STYLE[segment_style].value
468+
ty = asgi_scope.get("type")
469+
470+
if segment_style == "endpoint":
471+
endpoint = asgi_scope.get("endpoint")
472+
# Webframeworks like Starlette mutate the ASGI env once routing is
473+
# done, which is sometime after the request has started. If we have
474+
# an endpoint, overwrite our generic transaction name.
475+
if endpoint:
476+
name = qualname_from_function(endpoint) or ""
477+
else:
478+
name = _get_url(asgi_scope, "http" if ty == "http" else "ws", host=None)
479+
source = SegmentSource.URL.value
480+
481+
elif segment_style == "url":
482+
# FastAPI includes the route object in the scope to let Sentry extract the
483+
# path from it for the transaction name
484+
route = asgi_scope.get("route")
485+
if route:
486+
path = getattr(route, "path", None)
487+
if path is not None:
488+
name = path
489+
else:
490+
name = _get_url(asgi_scope, "http" if ty == "http" else "ws", host=None)
491+
source = SegmentSource.URL.value
492+
493+
if name is None:
494+
name = _DEFAULT_TRANSACTION_NAME
495+
source = SegmentSource.ROUTE.value
496+
return name, source
497+
498+
return name, source

sentry_sdk/integrations/huggingface_hub.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
import sys
21
import inspect
2+
import sys
33
from functools import wraps
4+
from typing import TYPE_CHECKING
45

56
import sentry_sdk
67
from sentry_sdk.ai.monitoring import record_token_usage
78
from sentry_sdk.ai.utils import set_data_normalized
89
from sentry_sdk.consts import OP, SPANDATA
910
from sentry_sdk.integrations import DidNotEnable, Integration
1011
from sentry_sdk.scope import should_send_default_pii
11-
from sentry_sdk.tracing_utils import set_span_errored
1212
from sentry_sdk.utils import (
1313
capture_internal_exceptions,
1414
event_from_exception,
1515
reraise,
1616
)
1717

18-
from typing import TYPE_CHECKING
19-
2018
if TYPE_CHECKING:
2119
from typing import Any, Callable, Iterable
2220

@@ -53,8 +51,6 @@ def setup_once() -> None:
5351

5452

5553
def _capture_exception(exc: "Any") -> None:
56-
set_span_errored()
57-
5854
event, hint = event_from_exception(
5955
exc,
6056
client_options=sentry_sdk.get_client().options,
@@ -131,7 +127,7 @@ def new_huggingface_task(*args: "Any", **kwargs: "Any") -> "Any":
131127
exc_info = sys.exc_info()
132128
with capture_internal_exceptions():
133129
_capture_exception(e)
134-
span.__exit__(None, None, None)
130+
span.__exit__(*exc_info)
135131
reraise(*exc_info)
136132

137133
# Output attributes

0 commit comments

Comments
 (0)