sentry-sdk 2.30.0__py2.py3-none-any.whl → 3.0.0a2__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sentry-sdk might be problematic. Click here for more details.

Files changed (109) hide show
  1. sentry_sdk/__init__.py +3 -8
  2. sentry_sdk/_compat.py +0 -1
  3. sentry_sdk/_init_implementation.py +6 -44
  4. sentry_sdk/_types.py +2 -64
  5. sentry_sdk/ai/monitoring.py +14 -10
  6. sentry_sdk/ai/utils.py +1 -1
  7. sentry_sdk/api.py +56 -169
  8. sentry_sdk/client.py +27 -72
  9. sentry_sdk/consts.py +60 -23
  10. sentry_sdk/debug.py +0 -10
  11. sentry_sdk/envelope.py +1 -3
  12. sentry_sdk/feature_flags.py +1 -1
  13. sentry_sdk/integrations/__init__.py +4 -2
  14. sentry_sdk/integrations/_asgi_common.py +5 -6
  15. sentry_sdk/integrations/_wsgi_common.py +11 -40
  16. sentry_sdk/integrations/aiohttp.py +104 -57
  17. sentry_sdk/integrations/anthropic.py +10 -7
  18. sentry_sdk/integrations/arq.py +24 -13
  19. sentry_sdk/integrations/asgi.py +102 -83
  20. sentry_sdk/integrations/asyncio.py +1 -0
  21. sentry_sdk/integrations/asyncpg.py +45 -30
  22. sentry_sdk/integrations/aws_lambda.py +109 -92
  23. sentry_sdk/integrations/boto3.py +38 -9
  24. sentry_sdk/integrations/bottle.py +1 -1
  25. sentry_sdk/integrations/celery/__init__.py +51 -41
  26. sentry_sdk/integrations/clickhouse_driver.py +59 -28
  27. sentry_sdk/integrations/cohere.py +2 -0
  28. sentry_sdk/integrations/django/__init__.py +25 -46
  29. sentry_sdk/integrations/django/asgi.py +6 -2
  30. sentry_sdk/integrations/django/caching.py +13 -22
  31. sentry_sdk/integrations/django/middleware.py +1 -0
  32. sentry_sdk/integrations/django/signals_handlers.py +3 -1
  33. sentry_sdk/integrations/django/templates.py +8 -12
  34. sentry_sdk/integrations/django/transactions.py +1 -6
  35. sentry_sdk/integrations/django/views.py +5 -2
  36. sentry_sdk/integrations/falcon.py +7 -25
  37. sentry_sdk/integrations/fastapi.py +3 -3
  38. sentry_sdk/integrations/flask.py +1 -1
  39. sentry_sdk/integrations/gcp.py +63 -38
  40. sentry_sdk/integrations/graphene.py +6 -13
  41. sentry_sdk/integrations/grpc/aio/client.py +14 -8
  42. sentry_sdk/integrations/grpc/aio/server.py +19 -21
  43. sentry_sdk/integrations/grpc/client.py +8 -6
  44. sentry_sdk/integrations/grpc/server.py +12 -14
  45. sentry_sdk/integrations/httpx.py +47 -12
  46. sentry_sdk/integrations/huey.py +26 -22
  47. sentry_sdk/integrations/huggingface_hub.py +1 -0
  48. sentry_sdk/integrations/langchain.py +22 -15
  49. sentry_sdk/integrations/litestar.py +4 -2
  50. sentry_sdk/integrations/logging.py +7 -2
  51. sentry_sdk/integrations/openai.py +2 -0
  52. sentry_sdk/integrations/pymongo.py +18 -25
  53. sentry_sdk/integrations/pyramid.py +1 -1
  54. sentry_sdk/integrations/quart.py +3 -3
  55. sentry_sdk/integrations/ray.py +23 -17
  56. sentry_sdk/integrations/redis/_async_common.py +29 -18
  57. sentry_sdk/integrations/redis/_sync_common.py +28 -19
  58. sentry_sdk/integrations/redis/modules/caches.py +13 -10
  59. sentry_sdk/integrations/redis/modules/queries.py +14 -11
  60. sentry_sdk/integrations/redis/rb.py +4 -4
  61. sentry_sdk/integrations/redis/redis.py +6 -6
  62. sentry_sdk/integrations/redis/redis_cluster.py +18 -18
  63. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +4 -4
  64. sentry_sdk/integrations/redis/utils.py +64 -24
  65. sentry_sdk/integrations/rq.py +68 -23
  66. sentry_sdk/integrations/rust_tracing.py +28 -43
  67. sentry_sdk/integrations/sanic.py +23 -13
  68. sentry_sdk/integrations/socket.py +9 -5
  69. sentry_sdk/integrations/sqlalchemy.py +8 -8
  70. sentry_sdk/integrations/starlette.py +11 -31
  71. sentry_sdk/integrations/starlite.py +4 -2
  72. sentry_sdk/integrations/stdlib.py +56 -9
  73. sentry_sdk/integrations/strawberry.py +40 -59
  74. sentry_sdk/integrations/threading.py +10 -26
  75. sentry_sdk/integrations/tornado.py +57 -18
  76. sentry_sdk/integrations/trytond.py +4 -1
  77. sentry_sdk/integrations/wsgi.py +84 -38
  78. sentry_sdk/opentelemetry/__init__.py +9 -0
  79. sentry_sdk/opentelemetry/consts.py +33 -0
  80. sentry_sdk/opentelemetry/contextvars_context.py +81 -0
  81. sentry_sdk/{integrations/opentelemetry → opentelemetry}/propagator.py +19 -28
  82. sentry_sdk/opentelemetry/sampler.py +326 -0
  83. sentry_sdk/opentelemetry/scope.py +218 -0
  84. sentry_sdk/opentelemetry/span_processor.py +335 -0
  85. sentry_sdk/opentelemetry/tracing.py +59 -0
  86. sentry_sdk/opentelemetry/utils.py +484 -0
  87. sentry_sdk/profiler/__init__.py +0 -40
  88. sentry_sdk/profiler/continuous_profiler.py +1 -30
  89. sentry_sdk/profiler/transaction_profiler.py +5 -56
  90. sentry_sdk/scope.py +108 -361
  91. sentry_sdk/sessions.py +0 -87
  92. sentry_sdk/tracing.py +415 -1161
  93. sentry_sdk/tracing_utils.py +130 -166
  94. sentry_sdk/transport.py +4 -104
  95. sentry_sdk/utils.py +169 -152
  96. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/METADATA +3 -5
  97. sentry_sdk-3.0.0a2.dist-info/RECORD +154 -0
  98. sentry_sdk-3.0.0a2.dist-info/entry_points.txt +2 -0
  99. sentry_sdk/hub.py +0 -739
  100. sentry_sdk/integrations/opentelemetry/__init__.py +0 -7
  101. sentry_sdk/integrations/opentelemetry/consts.py +0 -5
  102. sentry_sdk/integrations/opentelemetry/integration.py +0 -58
  103. sentry_sdk/integrations/opentelemetry/span_processor.py +0 -391
  104. sentry_sdk/metrics.py +0 -965
  105. sentry_sdk-2.30.0.dist-info/RECORD +0 -152
  106. sentry_sdk-2.30.0.dist-info/entry_points.txt +0 -2
  107. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/WHEEL +0 -0
  108. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/licenses/LICENSE +0 -0
  109. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
  import contextlib
3
- from typing import Any, TypeVar, Callable, Awaitable, Iterator
3
+ from typing import Any, TypeVar, Callable, Awaitable, Iterator, Optional
4
4
 
5
5
  import sentry_sdk
6
6
  from sentry_sdk.consts import OP, SPANDATA
@@ -8,6 +8,7 @@ from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotE
8
8
  from sentry_sdk.tracing import Span
9
9
  from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
10
10
  from sentry_sdk.utils import (
11
+ _serialize_span_attribute,
11
12
  ensure_integration_enabled,
12
13
  parse_version,
13
14
  capture_internal_exceptions,
@@ -38,7 +39,6 @@ class AsyncPGIntegration(Integration):
38
39
  asyncpg.Connection.execute = _wrap_execute(
39
40
  asyncpg.Connection.execute,
40
41
  )
41
-
42
42
  asyncpg.Connection._execute = _wrap_connection_method(
43
43
  asyncpg.Connection._execute
44
44
  )
@@ -78,8 +78,8 @@ def _wrap_execute(f: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]
78
78
  ) as span:
79
79
  res = await f(*args, **kwargs)
80
80
 
81
- with capture_internal_exceptions():
82
- add_query_source(span)
81
+ with capture_internal_exceptions():
82
+ add_query_source(span)
83
83
 
84
84
  return res
85
85
 
@@ -121,10 +121,13 @@ def _wrap_connection_method(
121
121
  async def _inner(*args: Any, **kwargs: Any) -> T:
122
122
  if sentry_sdk.get_client().get_integration(AsyncPGIntegration) is None:
123
123
  return await f(*args, **kwargs)
124
+
124
125
  query = args[1]
125
126
  params_list = args[2] if len(args) > 2 else None
127
+
126
128
  with _record(None, query, params_list, executemany=executemany) as span:
127
- _set_db_data(span, args[0])
129
+ data = _get_db_data(conn=args[0])
130
+ _set_on_span(span, data)
128
131
  res = await f(*args, **kwargs)
129
132
 
130
133
  return res
@@ -144,9 +147,10 @@ def _wrap_cursor_creation(f: Callable[..., T]) -> Callable[..., T]:
144
147
  params_list,
145
148
  executemany=False,
146
149
  ) as span:
147
- _set_db_data(span, args[0])
150
+ data = _get_db_data(conn=args[0])
151
+ _set_on_span(span, data)
148
152
  res = f(*args, **kwargs)
149
- span.set_data("db.cursor", res)
153
+ span.set_attribute("db.cursor", _serialize_span_attribute(res))
150
154
 
151
155
  return res
152
156
 
@@ -158,29 +162,24 @@ def _wrap_connect_addr(f: Callable[..., Awaitable[T]]) -> Callable[..., Awaitabl
158
162
  if sentry_sdk.get_client().get_integration(AsyncPGIntegration) is None:
159
163
  return await f(*args, **kwargs)
160
164
 
161
- user = kwargs["params"].user
162
- database = kwargs["params"].database
163
-
164
165
  with sentry_sdk.start_span(
165
166
  op=OP.DB,
166
167
  name="connect",
167
168
  origin=AsyncPGIntegration.origin,
169
+ only_if_parent=True,
168
170
  ) as span:
169
- span.set_data(SPANDATA.DB_SYSTEM, "postgresql")
170
- addr = kwargs.get("addr")
171
- if addr:
172
- try:
173
- span.set_data(SPANDATA.SERVER_ADDRESS, addr[0])
174
- span.set_data(SPANDATA.SERVER_PORT, addr[1])
175
- except IndexError:
176
- pass
177
- span.set_data(SPANDATA.DB_NAME, database)
178
- span.set_data(SPANDATA.DB_USER, user)
171
+ data = _get_db_data(
172
+ addr=kwargs.get("addr"),
173
+ database=kwargs["params"].database,
174
+ user=kwargs["params"].user,
175
+ )
176
+ _set_on_span(span, data)
179
177
 
180
178
  with capture_internal_exceptions():
181
179
  sentry_sdk.add_breadcrumb(
182
- message="connect", category="query", data=span._data
180
+ message="connect", category="query", data=data
183
181
  )
182
+
184
183
  res = await f(*args, **kwargs)
185
184
 
186
185
  return res
@@ -188,21 +187,37 @@ def _wrap_connect_addr(f: Callable[..., Awaitable[T]]) -> Callable[..., Awaitabl
188
187
  return _inner
189
188
 
190
189
 
191
- def _set_db_data(span: Span, conn: Any) -> None:
192
- span.set_data(SPANDATA.DB_SYSTEM, "postgresql")
190
+ def _get_db_data(
191
+ conn: Any = None,
192
+ addr: Optional[tuple[str, ...]] = None,
193
+ database: Optional[str] = None,
194
+ user: Optional[str] = None,
195
+ ) -> dict[str, str]:
196
+ if conn is not None:
197
+ addr = conn._addr
198
+ database = conn._params.database
199
+ user = conn._params.user
200
+
201
+ data = {
202
+ SPANDATA.DB_SYSTEM: "postgresql",
203
+ }
193
204
 
194
- addr = conn._addr
195
205
  if addr:
196
206
  try:
197
- span.set_data(SPANDATA.SERVER_ADDRESS, addr[0])
198
- span.set_data(SPANDATA.SERVER_PORT, addr[1])
207
+ data[SPANDATA.SERVER_ADDRESS] = addr[0]
208
+ data[SPANDATA.SERVER_PORT] = addr[1]
199
209
  except IndexError:
200
210
  pass
201
211
 
202
- database = conn._params.database
203
212
  if database:
204
- span.set_data(SPANDATA.DB_NAME, database)
213
+ data[SPANDATA.DB_NAME] = database
205
214
 
206
- user = conn._params.user
207
215
  if user:
208
- span.set_data(SPANDATA.DB_USER, user)
216
+ data[SPANDATA.DB_USER] = user
217
+
218
+ return data
219
+
220
+
221
+ def _set_on_span(span: Span, data: dict[str, Any]) -> None:
222
+ for key, value in data.items():
223
+ span.set_attribute(key, value)
@@ -5,9 +5,9 @@ import sys
5
5
  from copy import deepcopy
6
6
  from datetime import datetime, timedelta, timezone
7
7
  from os import environ
8
+ from urllib.parse import urlencode
8
9
 
9
10
  import sentry_sdk
10
- from sentry_sdk.api import continue_trace
11
11
  from sentry_sdk.consts import OP
12
12
  from sentry_sdk.scope import should_send_default_pii
13
13
  from sentry_sdk.tracing import TransactionSource
@@ -21,7 +21,10 @@ from sentry_sdk.utils import (
21
21
  reraise,
22
22
  )
23
23
  from sentry_sdk.integrations import Integration
24
- from sentry_sdk.integrations._wsgi_common import _filter_headers
24
+ from sentry_sdk.integrations._wsgi_common import (
25
+ _filter_headers,
26
+ _request_headers_to_span_attributes,
27
+ )
25
28
 
26
29
  from typing import TYPE_CHECKING
27
30
 
@@ -40,6 +43,17 @@ TIMEOUT_WARNING_BUFFER = 1500 # Buffer time required to send timeout warning to
40
43
  MILLIS_TO_SECONDS = 1000.0
41
44
 
42
45
 
46
+ EVENT_TO_ATTRIBUTES = {
47
+ "httpMethod": "http.request.method",
48
+ "queryStringParameters": "url.query",
49
+ "path": "url.path",
50
+ }
51
+
52
+ CONTEXT_TO_ATTRIBUTES = {
53
+ "function_name": "faas.name",
54
+ }
55
+
56
+
43
57
  def _wrap_init_error(init_error):
44
58
  # type: (F) -> F
45
59
  @ensure_integration_enabled(AwsLambdaIntegration, init_error)
@@ -110,6 +124,9 @@ def _wrap_handler(handler):
110
124
  configured_time = aws_context.get_remaining_time_in_millis()
111
125
 
112
126
  with sentry_sdk.isolation_scope() as scope:
127
+ scope.set_transaction_name(
128
+ aws_context.function_name, source=TransactionSource.COMPONENT
129
+ )
113
130
  timeout_thread = None
114
131
  with capture_internal_exceptions():
115
132
  scope.clear_breadcrumbs()
@@ -149,34 +166,28 @@ def _wrap_handler(handler):
149
166
  if not isinstance(headers, dict):
150
167
  headers = {}
151
168
 
152
- transaction = continue_trace(
153
- headers,
154
- op=OP.FUNCTION_AWS,
155
- name=aws_context.function_name,
156
- source=TransactionSource.COMPONENT,
157
- origin=AwsLambdaIntegration.origin,
158
- )
159
- with sentry_sdk.start_transaction(
160
- transaction,
161
- custom_sampling_context={
162
- "aws_event": aws_event,
163
- "aws_context": aws_context,
164
- },
165
- ):
166
- try:
167
- return handler(aws_event, aws_context, *args, **kwargs)
168
- except Exception:
169
- exc_info = sys.exc_info()
170
- sentry_event, hint = event_from_exception(
171
- exc_info,
172
- client_options=client.options,
173
- mechanism={"type": "aws_lambda", "handled": False},
174
- )
175
- sentry_sdk.capture_event(sentry_event, hint=hint)
176
- reraise(*exc_info)
177
- finally:
178
- if timeout_thread:
179
- timeout_thread.stop()
169
+ with sentry_sdk.continue_trace(headers):
170
+ with sentry_sdk.start_span(
171
+ op=OP.FUNCTION_AWS,
172
+ name=aws_context.function_name,
173
+ source=TransactionSource.COMPONENT,
174
+ origin=AwsLambdaIntegration.origin,
175
+ attributes=_prepopulate_attributes(request_data, aws_context),
176
+ ):
177
+ try:
178
+ return handler(aws_event, aws_context, *args, **kwargs)
179
+ except Exception:
180
+ exc_info = sys.exc_info()
181
+ sentry_event, hint = event_from_exception(
182
+ exc_info,
183
+ client_options=client.options,
184
+ mechanism={"type": "aws_lambda", "handled": False},
185
+ )
186
+ sentry_sdk.capture_event(sentry_event, hint=hint)
187
+ reraise(*exc_info)
188
+ finally:
189
+ if timeout_thread:
190
+ timeout_thread.stop()
180
191
 
181
192
  return sentry_handler # type: ignore
182
193
 
@@ -219,77 +230,44 @@ class AwsLambdaIntegration(Integration):
219
230
  )
220
231
  return
221
232
 
222
- pre_37 = hasattr(lambda_bootstrap, "handle_http_request") # Python 3.6
223
-
224
- if pre_37:
225
- old_handle_event_request = lambda_bootstrap.handle_event_request
226
-
227
- def sentry_handle_event_request(request_handler, *args, **kwargs):
228
- # type: (Any, *Any, **Any) -> Any
229
- request_handler = _wrap_handler(request_handler)
230
- return old_handle_event_request(request_handler, *args, **kwargs)
233
+ lambda_bootstrap.LambdaRuntimeClient.post_init_error = _wrap_init_error(
234
+ lambda_bootstrap.LambdaRuntimeClient.post_init_error
235
+ )
231
236
 
232
- lambda_bootstrap.handle_event_request = sentry_handle_event_request
237
+ old_handle_event_request = lambda_bootstrap.handle_event_request
233
238
 
234
- old_handle_http_request = lambda_bootstrap.handle_http_request
235
-
236
- def sentry_handle_http_request(request_handler, *args, **kwargs):
237
- # type: (Any, *Any, **Any) -> Any
238
- request_handler = _wrap_handler(request_handler)
239
- return old_handle_http_request(request_handler, *args, **kwargs)
240
-
241
- lambda_bootstrap.handle_http_request = sentry_handle_http_request
239
+ def sentry_handle_event_request( # type: ignore
240
+ lambda_runtime_client, request_handler, *args, **kwargs
241
+ ):
242
+ request_handler = _wrap_handler(request_handler)
243
+ return old_handle_event_request(
244
+ lambda_runtime_client, request_handler, *args, **kwargs
245
+ )
242
246
 
243
- # Patch to_json to drain the queue. This should work even when the
244
- # SDK is initialized inside of the handler
247
+ lambda_bootstrap.handle_event_request = sentry_handle_event_request
245
248
 
246
- old_to_json = lambda_bootstrap.to_json
249
+ # Patch the runtime client to drain the queue. This should work
250
+ # even when the SDK is initialized inside of the handler
247
251
 
248
- def sentry_to_json(*args, **kwargs):
252
+ def _wrap_post_function(f):
253
+ # type: (F) -> F
254
+ def inner(*args, **kwargs):
249
255
  # type: (*Any, **Any) -> Any
250
256
  _drain_queue()
251
- return old_to_json(*args, **kwargs)
257
+ return f(*args, **kwargs)
252
258
 
253
- lambda_bootstrap.to_json = sentry_to_json
254
- else:
255
- lambda_bootstrap.LambdaRuntimeClient.post_init_error = _wrap_init_error(
256
- lambda_bootstrap.LambdaRuntimeClient.post_init_error
257
- )
259
+ return inner # type: ignore
258
260
 
259
- old_handle_event_request = lambda_bootstrap.handle_event_request
260
-
261
- def sentry_handle_event_request( # type: ignore
262
- lambda_runtime_client, request_handler, *args, **kwargs
263
- ):
264
- request_handler = _wrap_handler(request_handler)
265
- return old_handle_event_request(
266
- lambda_runtime_client, request_handler, *args, **kwargs
267
- )
268
-
269
- lambda_bootstrap.handle_event_request = sentry_handle_event_request
270
-
271
- # Patch the runtime client to drain the queue. This should work
272
- # even when the SDK is initialized inside of the handler
273
-
274
- def _wrap_post_function(f):
275
- # type: (F) -> F
276
- def inner(*args, **kwargs):
277
- # type: (*Any, **Any) -> Any
278
- _drain_queue()
279
- return f(*args, **kwargs)
280
-
281
- return inner # type: ignore
282
-
283
- lambda_bootstrap.LambdaRuntimeClient.post_invocation_result = (
284
- _wrap_post_function(
285
- lambda_bootstrap.LambdaRuntimeClient.post_invocation_result
286
- )
261
+ lambda_bootstrap.LambdaRuntimeClient.post_invocation_result = (
262
+ _wrap_post_function(
263
+ lambda_bootstrap.LambdaRuntimeClient.post_invocation_result
287
264
  )
288
- lambda_bootstrap.LambdaRuntimeClient.post_invocation_error = (
289
- _wrap_post_function(
290
- lambda_bootstrap.LambdaRuntimeClient.post_invocation_error
291
- )
265
+ )
266
+ lambda_bootstrap.LambdaRuntimeClient.post_invocation_error = (
267
+ _wrap_post_function(
268
+ lambda_bootstrap.LambdaRuntimeClient.post_invocation_error
292
269
  )
270
+ )
293
271
 
294
272
 
295
273
  def get_lambda_bootstrap():
@@ -362,7 +340,7 @@ def _make_request_event_processor(aws_event, aws_context, configured_timeout):
362
340
  request["url"] = _get_url(aws_event, aws_context)
363
341
 
364
342
  if "queryStringParameters" in aws_event:
365
- request["query_string"] = aws_event["queryStringParameters"]
343
+ request["query_string"] = urlencode(aws_event["queryStringParameters"])
366
344
 
367
345
  if "headers" in aws_event:
368
346
  request["headers"] = _filter_headers(aws_event["headers"])
@@ -402,7 +380,9 @@ def _get_url(aws_event, aws_context):
402
380
  path = aws_event.get("path", None)
403
381
 
404
382
  headers = aws_event.get("headers")
405
- if headers is None:
383
+ # Some AWS Services (ie. EventBridge) set headers as a list
384
+ # or None, so we must ensure it is a dict
385
+ if not isinstance(headers, dict):
406
386
  headers = {}
407
387
 
408
388
  host = headers.get("Host", None)
@@ -497,3 +477,40 @@ def _event_from_error_json(error_json):
497
477
  } # type: Event
498
478
 
499
479
  return event
480
+
481
+
482
+ def _prepopulate_attributes(aws_event, aws_context):
483
+ # type: (Any, Any) -> dict[str, Any]
484
+ attributes = {
485
+ "cloud.provider": "aws",
486
+ }
487
+
488
+ for prop, attr in EVENT_TO_ATTRIBUTES.items():
489
+ if aws_event.get(prop) is not None:
490
+ if prop == "queryStringParameters":
491
+ attributes[attr] = urlencode(aws_event[prop])
492
+ else:
493
+ attributes[attr] = aws_event[prop]
494
+
495
+ for prop, attr in CONTEXT_TO_ATTRIBUTES.items():
496
+ if getattr(aws_context, prop, None) is not None:
497
+ attributes[attr] = getattr(aws_context, prop)
498
+
499
+ url = _get_url(aws_event, aws_context)
500
+ if url:
501
+ if aws_event.get("queryStringParameters"):
502
+ url += f"?{urlencode(aws_event['queryStringParameters'])}"
503
+ attributes["url.full"] = url
504
+
505
+ headers = {}
506
+ if aws_event.get("headers") and isinstance(aws_event["headers"], dict):
507
+ headers = aws_event["headers"]
508
+
509
+ if headers.get("X-Forwarded-Proto"):
510
+ attributes["network.protocol.name"] = headers["X-Forwarded-Proto"]
511
+ if headers.get("Host"):
512
+ attributes["server.address"] = headers["Host"]
513
+
514
+ attributes.update(_request_headers_to_span_attributes(headers))
515
+
516
+ return attributes
@@ -3,7 +3,6 @@ from functools import partial
3
3
  import sentry_sdk
4
4
  from sentry_sdk.consts import OP, SPANDATA
5
5
  from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
6
- from sentry_sdk.tracing import Span
7
6
  from sentry_sdk.utils import (
8
7
  capture_internal_exceptions,
9
8
  ensure_integration_enabled,
@@ -19,6 +18,8 @@ if TYPE_CHECKING:
19
18
  from typing import Optional
20
19
  from typing import Type
21
20
 
21
+ from sentry_sdk.tracing import Span
22
+
22
23
  try:
23
24
  from botocore import __version__ as BOTOCORE_VERSION # type: ignore
24
25
  from botocore.client import BaseClient # type: ignore
@@ -63,17 +64,23 @@ def _sentry_request_created(service_id, request, operation_name, **kwargs):
63
64
  op=OP.HTTP_CLIENT,
64
65
  name=description,
65
66
  origin=Boto3Integration.origin,
67
+ only_if_parent=True,
66
68
  )
67
69
 
70
+ data = {
71
+ SPANDATA.HTTP_METHOD: request.method,
72
+ }
68
73
  with capture_internal_exceptions():
69
74
  parsed_url = parse_url(request.url, sanitize=False)
70
- span.set_data("aws.request.url", parsed_url.url)
71
- span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
72
- span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
75
+ data["aws.request.url"] = parsed_url.url
76
+ data[SPANDATA.HTTP_QUERY] = parsed_url.query
77
+ data[SPANDATA.HTTP_FRAGMENT] = parsed_url.fragment
78
+
79
+ for key, value in data.items():
80
+ span.set_attribute(key, value)
73
81
 
74
82
  span.set_tag("aws.service_id", service_id)
75
83
  span.set_tag("aws.operation_name", operation_name)
76
- span.set_data(SPANDATA.HTTP_METHOD, request.method)
77
84
 
78
85
  # We do it in order for subsequent http calls/retries be
79
86
  # attached to this span.
@@ -82,6 +89,7 @@ def _sentry_request_created(service_id, request, operation_name, **kwargs):
82
89
  # request.context is an open-ended data-structure
83
90
  # where we can add anything useful in request life cycle.
84
91
  request.context["_sentrysdk_span"] = span
92
+ request.context["_sentrysdk_span_data"] = data
85
93
 
86
94
 
87
95
  def _sentry_after_call(context, parsed, **kwargs):
@@ -91,20 +99,28 @@ def _sentry_after_call(context, parsed, **kwargs):
91
99
  # Span could be absent if the integration is disabled.
92
100
  if span is None:
93
101
  return
94
- span.__exit__(None, None, None)
102
+
103
+ span_data = context.pop("_sentrysdk_span_data", {})
104
+
105
+ sentry_sdk.add_breadcrumb(
106
+ type="http",
107
+ category="httplib",
108
+ data=span_data,
109
+ )
95
110
 
96
111
  body = parsed.get("Body")
97
112
  if not isinstance(body, StreamingBody):
113
+ span.__exit__(None, None, None)
98
114
  return
99
115
 
100
- streaming_span = span.start_child(
116
+ streaming_span = sentry_sdk.start_span(
101
117
  op=OP.HTTP_CLIENT_STREAM,
102
- name=span.description,
118
+ name=span.name,
103
119
  origin=Boto3Integration.origin,
120
+ only_if_parent=True,
104
121
  )
105
122
 
106
123
  orig_read = body.read
107
- orig_close = body.close
108
124
 
109
125
  def sentry_streaming_body_read(*args, **kwargs):
110
126
  # type: (*Any, **Any) -> bytes
@@ -119,6 +135,8 @@ def _sentry_after_call(context, parsed, **kwargs):
119
135
 
120
136
  body.read = sentry_streaming_body_read
121
137
 
138
+ orig_close = body.close
139
+
122
140
  def sentry_streaming_body_close(*args, **kwargs):
123
141
  # type: (*Any, **Any) -> None
124
142
  streaming_span.finish()
@@ -126,6 +144,8 @@ def _sentry_after_call(context, parsed, **kwargs):
126
144
 
127
145
  body.close = sentry_streaming_body_close
128
146
 
147
+ span.__exit__(None, None, None)
148
+
129
149
 
130
150
  def _sentry_after_call_error(context, exception, **kwargs):
131
151
  # type: (Dict[str, Any], Type[BaseException], **Any) -> None
@@ -134,4 +154,13 @@ def _sentry_after_call_error(context, exception, **kwargs):
134
154
  # Span could be absent if the integration is disabled.
135
155
  if span is None:
136
156
  return
157
+
158
+ span_data = context.pop("_sentrysdk_span_data", {})
159
+
160
+ sentry_sdk.add_breadcrumb(
161
+ type="http",
162
+ category="httplib",
163
+ data=span_data,
164
+ )
165
+
137
166
  span.__exit__(type(exception), exception, None)
@@ -1,7 +1,7 @@
1
1
  import functools
2
2
 
3
3
  import sentry_sdk
4
- from sentry_sdk.tracing import SOURCE_FOR_STYLE
4
+ from sentry_sdk.consts import SOURCE_FOR_STYLE
5
5
  from sentry_sdk.utils import (
6
6
  capture_internal_exceptions,
7
7
  ensure_integration_enabled,