Skip to content

Commit d48cb92

Browse files
merge master
2 parents 5060b5d + 1e758fa commit d48cb92

23 files changed

Lines changed: 1675 additions & 716 deletions

File tree

‎CHANGELOG.md‎

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

3+
## 2.69.1
4+
5+
### New Features ✨
6+
7+
- (django) Add `http.route` attribute by @alexander-alderman-webb in [#7428](https://github.com/getsentry/sentry-python/pull/7428)
8+
9+
### Bug Fixes 🐛
10+
11+
- (aiohttp) Preserve SigV4-signed propagation headers by @Robinbinu in [#7427](https://github.com/getsentry/sentry-python/pull/7427)
12+
13+
### Internal Changes 🔧
14+
15+
#### Mcp
16+
17+
- Parametrize tests on the streaming trace lifecycle by @alexander-alderman-webb in [#7424](https://github.com/getsentry/sentry-python/pull/7424)
18+
- Remove vacuous tests by @alexander-alderman-webb in [#7420](https://github.com/getsentry/sentry-python/pull/7420)
19+
20+
#### Other
21+
22+
- (cohere) Parametrize tests on the streaming trace lifecycle by @alexander-alderman-webb in [#7419](https://github.com/getsentry/sentry-python/pull/7419)
23+
- (openai-agents) Parametrize tests on `stream_gen_ai_spans` and the streaming trace lifecycle by @alexander-alderman-webb in [#7405](https://github.com/getsentry/sentry-python/pull/7405)
24+
325
## 2.69.0
426

527
### 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.69.0"
34+
release = "2.69.1"
3535
version = ".".join(release.split(".")[:2]) # The short X.Y version.
3636

3737

‎sentry_sdk/client.py‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
from sentry_sdk.serializer import serialize
3737
from sentry_sdk.sessions import SessionFlusher
3838
from sentry_sdk.traces import SpanStatus, StreamedSpan
39-
from sentry_sdk.tracing import trace
39+
from sentry_sdk.traces import trace as streaming_trace
40+
from sentry_sdk.tracing import trace as legacy_trace
4041
from sentry_sdk.tracing_utils import has_span_streaming_enabled
4142
from sentry_sdk.transport import (
4243
AsyncHttpTransport,
@@ -509,8 +510,14 @@ def _setup_instrumentation(
509510
self, functions_to_trace: "Sequence[Dict[str, str]]"
510511
) -> None:
511512
"""
512-
Instruments the functions given in the list `functions_to_trace` with the `@sentry_sdk.tracing.trace` decorator.
513+
Instruments the functions given in the list `functions_to_trace` with a trace decorator.
513514
"""
515+
trace = (
516+
streaming_trace
517+
if has_span_streaming_enabled(self.options)
518+
else legacy_trace
519+
)
520+
514521
for function in functions_to_trace:
515522
class_name = None
516523
function_qualname = function["qualified_name"]

‎sentry_sdk/consts.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
DEFAULT_MAX_STACK_FRAMES = 100
88
DEFAULT_ADD_FULL_STACK = False
99

10+
_SENTRY_HEADER_NAMES = frozenset(("baggage", "sentry-trace"))
11+
1012

1113
# Also needs to be at the top to prevent circular import
1214
class EndpointType(Enum):
@@ -1802,4 +1804,4 @@ def _get_default_options() -> "dict[str, Any]":
18021804
del _get_default_options
18031805

18041806

1805-
VERSION = "2.69.0"
1807+
VERSION = "2.69.1"

‎sentry_sdk/integrations/aiohttp.py‎

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from functools import wraps
44

55
import sentry_sdk
6-
from sentry_sdk.consts import OP, SPANDATA
6+
from sentry_sdk.consts import _SENTRY_HEADER_NAMES, OP, SPANDATA
77
from sentry_sdk.data_collection import (
88
_apply_data_collection_filtering_to_query_string,
99
)
@@ -27,6 +27,7 @@
2727
)
2828
from sentry_sdk.tracing import (
2929
BAGGAGE_HEADER_NAME,
30+
SENTRY_TRACE_HEADER_NAME,
3031
SOURCE_FOR_STYLE,
3132
)
3233
from sentry_sdk.tracing_utils import (
@@ -37,6 +38,8 @@
3738
from sentry_sdk.utils import (
3839
SENSITIVE_DATA_SUBSTITUTE,
3940
AnnotatedValue,
41+
_get_aws_sigv4_signed_headers_from_authorization_header,
42+
_get_aws_sigv4_signed_headers_from_url_query_string,
4043
_register_control_flow_exception,
4144
capture_internal_exceptions,
4245
ensure_integration_enabled,
@@ -389,12 +392,50 @@ async def on_request_start(
389392
span = sentry_sdk.traces.start_span(name=span_name, attributes=attributes)
390393

391394
if should_propagate_trace(client, str(params.url)):
395+
# existing `sentry-trace`: skip so it is not duplicated.
396+
headers_to_skip: "set[str]" = set()
397+
if SENTRY_TRACE_HEADER_NAME in params.headers:
398+
headers_to_skip.add(SENTRY_TRACE_HEADER_NAME)
399+
400+
with capture_internal_exceptions():
401+
authorization = params.headers.get("Authorization")
402+
if authorization:
403+
if isinstance(authorization, bytes):
404+
authorization = authorization.decode("latin-1")
405+
# `SignedHeaders` lists fields covered by SigV4.
406+
signed_headers = (
407+
_get_aws_sigv4_signed_headers_from_authorization_header(
408+
authorization
409+
)
410+
)
411+
# skip signed `sentry-trace` and `baggage`.
412+
headers_to_skip.update(
413+
_SENTRY_HEADER_NAMES.intersection(signed_headers)
414+
)
415+
416+
# presigned URLs list signed names in the query string.
417+
query_signed_headers = (
418+
_get_aws_sigv4_signed_headers_from_url_query_string(str(params.url))
419+
)
420+
headers_to_skip.update(
421+
_SENTRY_HEADER_NAMES.intersection(query_signed_headers)
422+
)
423+
392424
for (
393425
key,
394426
value,
395427
) in sentry_sdk.get_current_scope().iter_trace_propagation_headers(
396428
span=span
397429
):
430+
# skip signed headers and an existing `sentry-trace`.
431+
if key.lower() in headers_to_skip:
432+
logger.debug(
433+
"[Tracing] Not adding `{key}` header to outgoing request "
434+
"to {url}: it already exists or is covered by the AWS "
435+
"SigV4 signature.".format(key=key, url=params.url)
436+
)
437+
continue
438+
398439
logger.debug(
399440
"[Tracing] Adding `{key}` header {value} to outgoing request to {url}.".format(
400441
key=key, value=value, url=params.url

‎sentry_sdk/integrations/aws_lambda.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343

4444

4545
def _get_user_from_event(aws_event: "dict[str, Any]") -> "dict[str, Any]":
46+
if not isinstance(aws_event, dict):
47+
return {}
48+
4649
identity = aws_event.get("requestContext", {}).get("identity")
4750
if identity is None:
4851
return {}

‎sentry_sdk/integrations/clickhouse_driver.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def _inner_end(*args: "P.args", **kwargs: "P.kwargs") -> "T":
161161
sentry_sdk.get_isolation_scope().add_breadcrumb(
162162
message=query,
163163
category="query",
164-
data={"db.result": res, **breadcrumb_data},
164+
data=breadcrumb_data,
165165
)
166166

167167
span = getattr(instance.connection, "_sentry_span", None)

‎sentry_sdk/integrations/django/__init__.py‎

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -389,21 +389,19 @@ def _patch_django_asgi_handler() -> None:
389389

390390

391391
def _set_transaction_name_and_source(
392-
scope: "sentry_sdk.Scope", transaction_style: str, request: "WSGIRequest"
392+
scope: "sentry_sdk.Scope",
393+
transaction_style: str,
394+
request: "WSGIRequest",
395+
route_path: "Optional[str]",
393396
) -> None:
394397
try:
395398
transaction_name = None
396399
if transaction_style == "function_name":
397400
fn = resolve(request.path).func
398401
transaction_name = transaction_from_function(getattr(fn, "view_class", fn))
399402

400-
elif transaction_style == "url":
401-
if hasattr(request, "urlconf"):
402-
transaction_name = LEGACY_RESOLVER.resolve(
403-
request.path_info, urlconf=request.urlconf
404-
)
405-
else:
406-
transaction_name = LEGACY_RESOLVER.resolve(request.path_info)
403+
elif transaction_style == "url" and route_path is not None:
404+
transaction_name = route_path
407405

408406
if transaction_name is None:
409407
transaction_name = request.path_info
@@ -415,6 +413,11 @@ def _set_transaction_name_and_source(
415413
transaction_name,
416414
source=source,
417415
)
416+
417+
if transaction_style == "url" and route_path is not None:
418+
server_span = scope._server_segment_span
419+
if server_span is not None:
420+
server_span.set_attribute(SPANDATA.HTTP_ROUTE, route_path)
418421
except Resolver404:
419422
urlconf = import_module(settings.ROOT_URLCONF)
420423
# This exception only gets thrown when transaction_style is `function_name`
@@ -439,8 +442,29 @@ def _before_get_response(request: "WSGIRequest") -> None:
439442
_patch_drf()
440443

441444
scope = sentry_sdk.get_current_scope()
445+
446+
route_path = None
447+
if hasattr(request, "urlconf"):
448+
try:
449+
route_path = LEGACY_RESOLVER.resolve(
450+
request.path_info, urlconf=request.urlconf
451+
)
452+
except Exception:
453+
pass
454+
else:
455+
try:
456+
route_path = LEGACY_RESOLVER.resolve(request.path_info)
457+
except Exception:
458+
pass
459+
460+
server_span = sentry_sdk.get_current_scope()._server_segment_span
461+
if server_span is not None and route_path is not None:
462+
server_span.set_attribute(SPANDATA.HTTP_ROUTE, route_path)
463+
442464
# Rely on WSGI middleware to start a trace
443-
_set_transaction_name_and_source(scope, integration.transaction_style, request)
465+
_set_transaction_name_and_source(
466+
scope, integration.transaction_style, request, route_path=route_path
467+
)
444468

445469
scope.add_event_processor(
446470
_make_wsgi_request_event_processor(weakref.ref(request), integration)
@@ -458,7 +482,27 @@ def _attempt_resolve_again(
458482
if not hasattr(request, "urlconf"):
459483
return
460484

461-
_set_transaction_name_and_source(scope, transaction_style, request)
485+
route_path = None
486+
if hasattr(request, "urlconf"):
487+
try:
488+
route_path = LEGACY_RESOLVER.resolve(
489+
request.path_info, urlconf=request.urlconf
490+
)
491+
except Exception:
492+
pass
493+
else:
494+
try:
495+
route_path = LEGACY_RESOLVER.resolve(request.path_info)
496+
except Exception:
497+
pass
498+
499+
server_span = sentry_sdk.get_current_scope()._server_segment_span
500+
if server_span is not None and route_path is not None:
501+
server_span.set_attribute(SPANDATA.HTTP_ROUTE, route_path)
502+
503+
_set_transaction_name_and_source(
504+
scope, transaction_style, request, route_path=route_path
505+
)
462506

463507

464508
def _get_user_from_request_and_set_on_scope(request: "WSGIRequest") -> None:

‎sentry_sdk/integrations/logging.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,14 +399,14 @@ def _can_record(self, record: "LogRecord") -> bool:
399399

400400
def emit(self, record: "LogRecord") -> "Any":
401401
with capture_internal_exceptions():
402-
self.format(record)
403402
if not self._can_record(record):
404403
return
405404

406405
client = sentry_sdk.get_client()
407406
if not client.is_active():
408407
return
409408

409+
self.format(record)
410410
self._capture_log_from_record(client, record)
411411

412412
def _capture_log_from_record(

‎sentry_sdk/integrations/stdlib.py‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
from typing import TYPE_CHECKING
77

88
import sentry_sdk
9-
from sentry_sdk.consts import OP, SPANDATA
9+
from sentry_sdk.consts import _SENTRY_HEADER_NAMES, OP, SPANDATA
1010
from sentry_sdk.integrations import Integration
1111
from sentry_sdk.scope import add_global_event_processor
1212
from sentry_sdk.traces import StreamedSpan
13-
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, SENTRY_TRACE_HEADER_NAME
13+
from sentry_sdk.tracing import SENTRY_TRACE_HEADER_NAME
1414
from sentry_sdk.tracing_utils import (
1515
EnvironHeaders,
1616
add_http_breadcrumb,
@@ -41,9 +41,6 @@
4141
"version": "%s.%s.%s" % (sys.version_info[:3]),
4242
"build": sys.version,
4343
}
44-
45-
_SENTRY_HEADER_NAMES = frozenset((BAGGAGE_HEADER_NAME, SENTRY_TRACE_HEADER_NAME))
46-
4744
try:
4845
from botocore.awsrequest import AWSHTTPConnection, AWSHTTPSConnection
4946
except ImportError:

0 commit comments

Comments
 (0)