sentry-sdk 2.27.0__py2.py3-none-any.whl → 3.0.0a1__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.
- sentry_sdk/__init__.py +4 -8
- sentry_sdk/_compat.py +0 -1
- sentry_sdk/_init_implementation.py +6 -44
- sentry_sdk/_log_batcher.py +47 -28
- sentry_sdk/_types.py +2 -64
- sentry_sdk/ai/monitoring.py +14 -10
- sentry_sdk/ai/utils.py +1 -1
- sentry_sdk/api.py +69 -163
- sentry_sdk/client.py +25 -72
- sentry_sdk/consts.py +42 -23
- sentry_sdk/debug.py +0 -10
- sentry_sdk/envelope.py +2 -10
- sentry_sdk/feature_flags.py +2 -2
- sentry_sdk/integrations/__init__.py +4 -2
- sentry_sdk/integrations/_asgi_common.py +3 -3
- sentry_sdk/integrations/_wsgi_common.py +11 -40
- sentry_sdk/integrations/aiohttp.py +104 -57
- sentry_sdk/integrations/anthropic.py +10 -7
- sentry_sdk/integrations/arq.py +24 -13
- sentry_sdk/integrations/asgi.py +102 -83
- sentry_sdk/integrations/asyncio.py +1 -0
- sentry_sdk/integrations/asyncpg.py +45 -30
- sentry_sdk/integrations/aws_lambda.py +109 -92
- sentry_sdk/integrations/boto3.py +38 -9
- sentry_sdk/integrations/bottle.py +1 -1
- sentry_sdk/integrations/celery/__init__.py +48 -38
- sentry_sdk/integrations/clickhouse_driver.py +59 -28
- sentry_sdk/integrations/cohere.py +2 -0
- sentry_sdk/integrations/django/__init__.py +25 -46
- sentry_sdk/integrations/django/asgi.py +6 -2
- sentry_sdk/integrations/django/caching.py +13 -22
- sentry_sdk/integrations/django/middleware.py +1 -0
- sentry_sdk/integrations/django/signals_handlers.py +3 -1
- sentry_sdk/integrations/django/templates.py +8 -12
- sentry_sdk/integrations/django/transactions.py +1 -6
- sentry_sdk/integrations/django/views.py +5 -2
- sentry_sdk/integrations/falcon.py +7 -25
- sentry_sdk/integrations/fastapi.py +3 -3
- sentry_sdk/integrations/flask.py +1 -1
- sentry_sdk/integrations/gcp.py +63 -38
- sentry_sdk/integrations/graphene.py +6 -13
- sentry_sdk/integrations/grpc/aio/client.py +14 -8
- sentry_sdk/integrations/grpc/aio/server.py +19 -21
- sentry_sdk/integrations/grpc/client.py +8 -6
- sentry_sdk/integrations/grpc/server.py +12 -14
- sentry_sdk/integrations/httpx.py +47 -12
- sentry_sdk/integrations/huey.py +26 -22
- sentry_sdk/integrations/huggingface_hub.py +1 -0
- sentry_sdk/integrations/langchain.py +22 -15
- sentry_sdk/integrations/litestar.py +4 -2
- sentry_sdk/integrations/logging.py +12 -3
- sentry_sdk/integrations/openai.py +2 -0
- sentry_sdk/integrations/pymongo.py +18 -25
- sentry_sdk/integrations/pyramid.py +1 -1
- sentry_sdk/integrations/quart.py +3 -3
- sentry_sdk/integrations/ray.py +23 -17
- sentry_sdk/integrations/redis/_async_common.py +30 -18
- sentry_sdk/integrations/redis/_sync_common.py +28 -18
- sentry_sdk/integrations/redis/modules/caches.py +13 -10
- sentry_sdk/integrations/redis/modules/queries.py +14 -11
- sentry_sdk/integrations/redis/rb.py +4 -4
- sentry_sdk/integrations/redis/redis.py +6 -6
- sentry_sdk/integrations/redis/redis_cluster.py +18 -16
- sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +4 -4
- sentry_sdk/integrations/redis/utils.py +63 -19
- sentry_sdk/integrations/rq.py +68 -23
- sentry_sdk/integrations/rust_tracing.py +28 -43
- sentry_sdk/integrations/sanic.py +23 -13
- sentry_sdk/integrations/socket.py +9 -5
- sentry_sdk/integrations/sqlalchemy.py +8 -8
- sentry_sdk/integrations/starlette.py +11 -31
- sentry_sdk/integrations/starlite.py +4 -2
- sentry_sdk/integrations/stdlib.py +56 -9
- sentry_sdk/integrations/strawberry.py +40 -59
- sentry_sdk/integrations/threading.py +10 -26
- sentry_sdk/integrations/tornado.py +57 -18
- sentry_sdk/integrations/trytond.py +4 -1
- sentry_sdk/integrations/wsgi.py +84 -38
- sentry_sdk/opentelemetry/__init__.py +9 -0
- sentry_sdk/opentelemetry/consts.py +33 -0
- sentry_sdk/opentelemetry/contextvars_context.py +73 -0
- sentry_sdk/{integrations/opentelemetry → opentelemetry}/propagator.py +19 -28
- sentry_sdk/opentelemetry/sampler.py +326 -0
- sentry_sdk/opentelemetry/scope.py +218 -0
- sentry_sdk/opentelemetry/span_processor.py +329 -0
- sentry_sdk/opentelemetry/tracing.py +35 -0
- sentry_sdk/opentelemetry/utils.py +476 -0
- sentry_sdk/profiler/__init__.py +0 -40
- sentry_sdk/profiler/continuous_profiler.py +1 -30
- sentry_sdk/profiler/transaction_profiler.py +5 -56
- sentry_sdk/scope.py +107 -351
- sentry_sdk/sessions.py +0 -87
- sentry_sdk/tracing.py +418 -1144
- sentry_sdk/tracing_utils.py +126 -164
- sentry_sdk/transport.py +4 -104
- sentry_sdk/utils.py +169 -152
- {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/METADATA +3 -5
- sentry_sdk-3.0.0a1.dist-info/RECORD +154 -0
- {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/WHEEL +1 -1
- sentry_sdk-3.0.0a1.dist-info/entry_points.txt +2 -0
- sentry_sdk/hub.py +0 -739
- sentry_sdk/integrations/opentelemetry/__init__.py +0 -7
- sentry_sdk/integrations/opentelemetry/consts.py +0 -5
- sentry_sdk/integrations/opentelemetry/integration.py +0 -58
- sentry_sdk/integrations/opentelemetry/span_processor.py +0 -391
- sentry_sdk/metrics.py +0 -965
- sentry_sdk-2.27.0.dist-info/RECORD +0 -152
- sentry_sdk-2.27.0.dist-info/entry_points.txt +0 -2
- {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
237
|
+
old_handle_event_request = lambda_bootstrap.handle_event_request
|
|
233
238
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
244
|
-
# SDK is initialized inside of the handler
|
|
247
|
+
lambda_bootstrap.handle_event_request = sentry_handle_event_request
|
|
245
248
|
|
|
246
|
-
|
|
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
|
-
|
|
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
|
|
257
|
+
return f(*args, **kwargs)
|
|
252
258
|
|
|
253
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
|
|
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
|
sentry_sdk/integrations/boto3.py
CHANGED
|
@@ -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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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 =
|
|
116
|
+
streaming_span = sentry_sdk.start_span(
|
|
101
117
|
op=OP.HTTP_CLIENT_STREAM,
|
|
102
|
-
name=span.
|
|
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)
|
|
@@ -4,8 +4,7 @@ from functools import wraps
|
|
|
4
4
|
|
|
5
5
|
import sentry_sdk
|
|
6
6
|
from sentry_sdk import isolation_scope
|
|
7
|
-
from sentry_sdk.
|
|
8
|
-
from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA
|
|
7
|
+
from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA, BAGGAGE_HEADER_NAME
|
|
9
8
|
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
|
|
10
9
|
from sentry_sdk.integrations.celery.beat import (
|
|
11
10
|
_patch_beat_apply_entry,
|
|
@@ -14,7 +13,7 @@ from sentry_sdk.integrations.celery.beat import (
|
|
|
14
13
|
)
|
|
15
14
|
from sentry_sdk.integrations.celery.utils import _now_seconds_since_epoch
|
|
16
15
|
from sentry_sdk.integrations.logging import ignore_logger
|
|
17
|
-
from sentry_sdk.tracing import
|
|
16
|
+
from sentry_sdk.tracing import TransactionSource
|
|
18
17
|
from sentry_sdk.tracing_utils import Baggage
|
|
19
18
|
from sentry_sdk.utils import (
|
|
20
19
|
capture_internal_exceptions,
|
|
@@ -113,7 +112,6 @@ def _capture_exception(task, exc_info):
|
|
|
113
112
|
return
|
|
114
113
|
|
|
115
114
|
if isinstance(exc_info[1], CELERY_CONTROL_FLOW_EXCEPTIONS):
|
|
116
|
-
# ??? Doesn't map to anything
|
|
117
115
|
_set_status("aborted")
|
|
118
116
|
return
|
|
119
117
|
|
|
@@ -277,6 +275,7 @@ def _wrap_task_run(f):
|
|
|
277
275
|
op=OP.QUEUE_SUBMIT_CELERY,
|
|
278
276
|
name=task_name,
|
|
279
277
|
origin=CeleryIntegration.origin,
|
|
278
|
+
only_if_parent=True,
|
|
280
279
|
)
|
|
281
280
|
if not task_started_from_beat
|
|
282
281
|
else NoOpMgr()
|
|
@@ -307,40 +306,29 @@ def _wrap_tracer(task, f):
|
|
|
307
306
|
with isolation_scope() as scope:
|
|
308
307
|
scope._name = "celery"
|
|
309
308
|
scope.clear_breadcrumbs()
|
|
309
|
+
scope.set_transaction_name(task.name, source=TransactionSource.TASK)
|
|
310
310
|
scope.add_event_processor(_make_event_processor(task, *args, **kwargs))
|
|
311
311
|
|
|
312
|
-
transaction = None
|
|
313
|
-
|
|
314
312
|
# Celery task objects are not a thing to be trusted. Even
|
|
315
313
|
# something such as attribute access can fail.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
314
|
+
headers = args[3].get("headers") or {}
|
|
315
|
+
|
|
316
|
+
with sentry_sdk.continue_trace(headers):
|
|
317
|
+
with sentry_sdk.start_span(
|
|
320
318
|
op=OP.QUEUE_TASK_CELERY,
|
|
321
|
-
name=
|
|
319
|
+
name=task.name,
|
|
322
320
|
source=TransactionSource.TASK,
|
|
323
321
|
origin=CeleryIntegration.origin,
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
322
|
+
# for some reason, args[1] is a list if non-empty but a
|
|
323
|
+
# tuple if empty
|
|
324
|
+
attributes=_prepopulate_attributes(task, list(args[1]), args[2]),
|
|
325
|
+
) as root_span:
|
|
326
|
+
return_value = f(*args, **kwargs)
|
|
327
327
|
|
|
328
|
-
|
|
329
|
-
|
|
328
|
+
if root_span.status is None:
|
|
329
|
+
root_span.set_status(SPANSTATUS.OK)
|
|
330
330
|
|
|
331
|
-
|
|
332
|
-
transaction,
|
|
333
|
-
custom_sampling_context={
|
|
334
|
-
"celery_job": {
|
|
335
|
-
"task": task.name,
|
|
336
|
-
# for some reason, args[1] is a list if non-empty but a
|
|
337
|
-
# tuple if empty
|
|
338
|
-
"args": list(args[1]),
|
|
339
|
-
"kwargs": args[2],
|
|
340
|
-
}
|
|
341
|
-
},
|
|
342
|
-
):
|
|
343
|
-
return f(*args, **kwargs)
|
|
331
|
+
return return_value
|
|
344
332
|
|
|
345
333
|
return _inner # type: ignore
|
|
346
334
|
|
|
@@ -355,7 +343,7 @@ def _set_messaging_destination_name(task, span):
|
|
|
355
343
|
if delivery_info.get("exchange") == "" and routing_key is not None:
|
|
356
344
|
# Empty exchange indicates the default exchange, meaning the tasks
|
|
357
345
|
# are sent to the queue with the same name as the routing key.
|
|
358
|
-
span.
|
|
346
|
+
span.set_attribute(SPANDATA.MESSAGING_DESTINATION_NAME, routing_key)
|
|
359
347
|
|
|
360
348
|
|
|
361
349
|
def _wrap_task_call(task, f):
|
|
@@ -377,6 +365,7 @@ def _wrap_task_call(task, f):
|
|
|
377
365
|
op=OP.QUEUE_PROCESS,
|
|
378
366
|
name=task.name,
|
|
379
367
|
origin=CeleryIntegration.origin,
|
|
368
|
+
only_if_parent=True,
|
|
380
369
|
) as span:
|
|
381
370
|
_set_messaging_destination_name(task, span)
|
|
382
371
|
|
|
@@ -391,23 +380,26 @@ def _wrap_task_call(task, f):
|
|
|
391
380
|
)
|
|
392
381
|
|
|
393
382
|
if latency is not None:
|
|
394
|
-
span.
|
|
383
|
+
span.set_attribute(
|
|
384
|
+
SPANDATA.MESSAGING_MESSAGE_RECEIVE_LATENCY, latency
|
|
385
|
+
)
|
|
395
386
|
|
|
396
387
|
with capture_internal_exceptions():
|
|
397
|
-
span.
|
|
388
|
+
span.set_attribute(SPANDATA.MESSAGING_MESSAGE_ID, task.request.id)
|
|
398
389
|
|
|
399
390
|
with capture_internal_exceptions():
|
|
400
|
-
span.
|
|
391
|
+
span.set_attribute(
|
|
401
392
|
SPANDATA.MESSAGING_MESSAGE_RETRY_COUNT, task.request.retries
|
|
402
393
|
)
|
|
403
394
|
|
|
404
395
|
with capture_internal_exceptions():
|
|
405
|
-
span.
|
|
396
|
+
span.set_attribute(
|
|
406
397
|
SPANDATA.MESSAGING_SYSTEM,
|
|
407
398
|
task.app.connection().transport.driver_type,
|
|
408
399
|
)
|
|
409
400
|
|
|
410
401
|
return f(*args, **kwargs)
|
|
402
|
+
|
|
411
403
|
except Exception:
|
|
412
404
|
exc_info = sys.exc_info()
|
|
413
405
|
with capture_internal_exceptions():
|
|
@@ -506,23 +498,41 @@ def _patch_producer_publish():
|
|
|
506
498
|
op=OP.QUEUE_PUBLISH,
|
|
507
499
|
name=task_name,
|
|
508
500
|
origin=CeleryIntegration.origin,
|
|
501
|
+
only_if_parent=True,
|
|
509
502
|
) as span:
|
|
510
503
|
if task_id is not None:
|
|
511
|
-
span.
|
|
504
|
+
span.set_attribute(SPANDATA.MESSAGING_MESSAGE_ID, task_id)
|
|
512
505
|
|
|
513
506
|
if exchange == "" and routing_key is not None:
|
|
514
507
|
# Empty exchange indicates the default exchange, meaning messages are
|
|
515
508
|
# routed to the queue with the same name as the routing key.
|
|
516
|
-
span.
|
|
509
|
+
span.set_attribute(SPANDATA.MESSAGING_DESTINATION_NAME, routing_key)
|
|
517
510
|
|
|
518
511
|
if retries is not None:
|
|
519
|
-
span.
|
|
512
|
+
span.set_attribute(SPANDATA.MESSAGING_MESSAGE_RETRY_COUNT, retries)
|
|
520
513
|
|
|
521
514
|
with capture_internal_exceptions():
|
|
522
|
-
span.
|
|
515
|
+
span.set_attribute(
|
|
523
516
|
SPANDATA.MESSAGING_SYSTEM, self.connection.transport.driver_type
|
|
524
517
|
)
|
|
525
518
|
|
|
526
519
|
return original_publish(self, *args, **kwargs)
|
|
527
520
|
|
|
528
521
|
Producer.publish = sentry_publish
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def _prepopulate_attributes(task, args, kwargs):
|
|
525
|
+
# type: (Any, *Any, **Any) -> dict[str, str]
|
|
526
|
+
attributes = {
|
|
527
|
+
"celery.job.task": task.name,
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
for i, arg in enumerate(args):
|
|
531
|
+
with capture_internal_exceptions():
|
|
532
|
+
attributes[f"celery.job.args.{i}"] = str(arg)
|
|
533
|
+
|
|
534
|
+
for kwarg, value in kwargs.items():
|
|
535
|
+
with capture_internal_exceptions():
|
|
536
|
+
attributes[f"celery.job.kwargs.{kwarg}"] = str(value)
|
|
537
|
+
|
|
538
|
+
return attributes
|