Skip to content

Commit a56dbcb

Browse files
merge master
2 parents d9b9700 + 66ef2e6 commit a56dbcb

File tree

19 files changed

+578
-217
lines changed

19 files changed

+578
-217
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 }}

.github/workflows/update-tox.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
echo "date=$DATE" >> $GITHUB_OUTPUT
5555
5656
- name: Create pull request
57-
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
57+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
5858
with:
5959
script: |
6060
const branchName = '${{ steps.create-branch.outputs.branch_name }}';

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

sentry_sdk/integrations/litellm.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,16 +159,6 @@ def _input_callback(kwargs: "Dict[str, Any]") -> None:
159159
if value is not None:
160160
set_data_normalized(span, attribute, value)
161161

162-
# Record LiteLLM-specific parameters
163-
litellm_params = {
164-
"api_base": kwargs.get("api_base"),
165-
"api_version": kwargs.get("api_version"),
166-
"custom_llm_provider": kwargs.get("custom_llm_provider"),
167-
}
168-
for key, value in litellm_params.items():
169-
if value is not None:
170-
set_data_normalized(span, f"gen_ai.litellm.{key}", value)
171-
172162

173163
def _success_callback(
174164
kwargs: "Dict[str, Any]",

sentry_sdk/integrations/openai_agents/patches/agent_run.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
end_invoke_agent_span,
99
handoff_span,
1010
)
11-
from ..utils import _record_exception_on_span
11+
from sentry_sdk.tracing_utils import set_span_errored
1212

1313
from typing import TYPE_CHECKING
1414

@@ -99,9 +99,9 @@ async def _run_single_turn(
9999

100100
try:
101101
result = await original_run_single_turn(*args, **kwargs)
102-
except Exception as exc:
102+
except Exception:
103103
if span is not None and span.timestamp is None:
104-
_record_exception_on_span(span, exc)
104+
set_span_errored(span)
105105
end_invoke_agent_span(context_wrapper, agent)
106106
reraise(*sys.exc_info())
107107

@@ -153,11 +153,11 @@ async def _run_single_turn_streamed(
153153

154154
try:
155155
result = await original_run_single_turn_streamed(*args, **kwargs)
156-
except Exception as exc:
156+
except Exception:
157157
exc_info = sys.exc_info()
158158
with capture_internal_exceptions():
159159
if span is not None and span.timestamp is None:
160-
_record_exception_on_span(span, exc)
160+
set_span_errored(span)
161161
end_invoke_agent_span(context_wrapper, agent)
162162
_close_streaming_workflow_span(agent)
163163
reraise(*exc_info)

0 commit comments

Comments
 (0)