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.
- sentry_sdk/__init__.py +3 -8
- sentry_sdk/_compat.py +0 -1
- sentry_sdk/_init_implementation.py +6 -44
- sentry_sdk/_types.py +2 -64
- sentry_sdk/ai/monitoring.py +14 -10
- sentry_sdk/ai/utils.py +1 -1
- sentry_sdk/api.py +56 -169
- sentry_sdk/client.py +27 -72
- sentry_sdk/consts.py +60 -23
- sentry_sdk/debug.py +0 -10
- sentry_sdk/envelope.py +1 -3
- sentry_sdk/feature_flags.py +1 -1
- sentry_sdk/integrations/__init__.py +4 -2
- sentry_sdk/integrations/_asgi_common.py +5 -6
- 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 +51 -41
- 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 +7 -2
- 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 +29 -18
- sentry_sdk/integrations/redis/_sync_common.py +28 -19
- 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 -18
- sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +4 -4
- sentry_sdk/integrations/redis/utils.py +64 -24
- 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 +81 -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 +335 -0
- sentry_sdk/opentelemetry/tracing.py +59 -0
- sentry_sdk/opentelemetry/utils.py +484 -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 +108 -361
- sentry_sdk/sessions.py +0 -87
- sentry_sdk/tracing.py +415 -1161
- sentry_sdk/tracing_utils.py +130 -166
- sentry_sdk/transport.py +4 -104
- sentry_sdk/utils.py +169 -152
- {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/METADATA +3 -5
- sentry_sdk-3.0.0a2.dist-info/RECORD +154 -0
- sentry_sdk-3.0.0a2.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.30.0.dist-info/RECORD +0 -152
- sentry_sdk-2.30.0.dist-info/entry_points.txt +0 -2
- {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/WHEEL +0 -0
- {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/top_level.txt +0 -0
|
@@ -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,
|
|
@@ -101,9 +100,9 @@ class CeleryIntegration(Integration):
|
|
|
101
100
|
def _set_status(status):
|
|
102
101
|
# type: (str) -> None
|
|
103
102
|
with capture_internal_exceptions():
|
|
104
|
-
|
|
105
|
-
if
|
|
106
|
-
|
|
103
|
+
span = sentry_sdk.get_current_span()
|
|
104
|
+
if span is not None:
|
|
105
|
+
span.set_status(status)
|
|
107
106
|
|
|
108
107
|
|
|
109
108
|
def _capture_exception(task, exc_info):
|
|
@@ -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
|
|
@@ -3,9 +3,13 @@ from sentry_sdk.consts import OP, SPANDATA
|
|
|
3
3
|
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
|
|
4
4
|
from sentry_sdk.tracing import Span
|
|
5
5
|
from sentry_sdk.scope import should_send_default_pii
|
|
6
|
-
from sentry_sdk.utils import
|
|
6
|
+
from sentry_sdk.utils import (
|
|
7
|
+
_serialize_span_attribute,
|
|
8
|
+
capture_internal_exceptions,
|
|
9
|
+
ensure_integration_enabled,
|
|
10
|
+
)
|
|
7
11
|
|
|
8
|
-
from typing import TYPE_CHECKING, TypeVar
|
|
12
|
+
from typing import TYPE_CHECKING, cast, Any, Dict, TypeVar
|
|
9
13
|
|
|
10
14
|
# Hack to get new Python features working in older versions
|
|
11
15
|
# without introducing a hard dependency on `typing_extensions`
|
|
@@ -84,19 +88,23 @@ def _wrap_start(f: Callable[P, T]) -> Callable[P, T]:
|
|
|
84
88
|
op=OP.DB,
|
|
85
89
|
name=query,
|
|
86
90
|
origin=ClickhouseDriverIntegration.origin,
|
|
91
|
+
only_if_parent=True,
|
|
87
92
|
)
|
|
88
93
|
|
|
89
94
|
connection._sentry_span = span # type: ignore[attr-defined]
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
data = _get_db_data(connection)
|
|
97
|
+
data = cast("dict[str, Any]", data)
|
|
98
|
+
data["db.query.text"] = query
|
|
94
99
|
|
|
95
100
|
if query_id:
|
|
96
|
-
|
|
101
|
+
data["db.query_id"] = query_id
|
|
97
102
|
|
|
98
103
|
if params and should_send_default_pii():
|
|
99
|
-
|
|
104
|
+
data["db.params"] = params
|
|
105
|
+
|
|
106
|
+
connection._sentry_db_data = data # type: ignore[attr-defined]
|
|
107
|
+
_set_on_span(span, data)
|
|
100
108
|
|
|
101
109
|
# run the original code
|
|
102
110
|
ret = f(*args, **kwargs)
|
|
@@ -109,20 +117,32 @@ def _wrap_start(f: Callable[P, T]) -> Callable[P, T]:
|
|
|
109
117
|
def _wrap_end(f: Callable[P, T]) -> Callable[P, T]:
|
|
110
118
|
def _inner_end(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
111
119
|
res = f(*args, **kwargs)
|
|
112
|
-
|
|
113
|
-
|
|
120
|
+
client = cast("clickhouse_driver.client.Client", args[0])
|
|
121
|
+
connection = client.connection
|
|
114
122
|
|
|
123
|
+
span = getattr(connection, "_sentry_span", None)
|
|
115
124
|
if span is not None:
|
|
125
|
+
data = getattr(connection, "_sentry_db_data", {})
|
|
126
|
+
|
|
116
127
|
if res is not None and should_send_default_pii():
|
|
117
|
-
|
|
128
|
+
data["db.result"] = res
|
|
129
|
+
span.set_attribute("db.result", _serialize_span_attribute(res))
|
|
118
130
|
|
|
119
131
|
with capture_internal_exceptions():
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
132
|
+
query = data.pop("db.query.text", None)
|
|
133
|
+
if query:
|
|
134
|
+
sentry_sdk.add_breadcrumb(
|
|
135
|
+
message=query, category="query", data=data
|
|
136
|
+
)
|
|
123
137
|
|
|
124
138
|
span.finish()
|
|
125
139
|
|
|
140
|
+
try:
|
|
141
|
+
del connection._sentry_db_data
|
|
142
|
+
del connection._sentry_span
|
|
143
|
+
except AttributeError:
|
|
144
|
+
pass
|
|
145
|
+
|
|
126
146
|
return res
|
|
127
147
|
|
|
128
148
|
return _inner_end
|
|
@@ -130,28 +150,39 @@ def _wrap_end(f: Callable[P, T]) -> Callable[P, T]:
|
|
|
130
150
|
|
|
131
151
|
def _wrap_send_data(f: Callable[P, T]) -> Callable[P, T]:
|
|
132
152
|
def _inner_send_data(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
153
|
+
client = cast("clickhouse_driver.client.Client", args[0])
|
|
154
|
+
connection = client.connection
|
|
155
|
+
db_params_data = cast("list[Any]", args[2])
|
|
156
|
+
span = getattr(connection, "_sentry_span", None)
|
|
136
157
|
|
|
137
158
|
if span is not None:
|
|
138
|
-
|
|
159
|
+
data = _get_db_data(connection)
|
|
160
|
+
_set_on_span(span, data)
|
|
139
161
|
|
|
140
162
|
if should_send_default_pii():
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
163
|
+
saved_db_data = getattr(
|
|
164
|
+
connection, "_sentry_db_data", {}
|
|
165
|
+
) # type: dict[str, Any]
|
|
166
|
+
db_params = saved_db_data.get("db.params") or [] # type: list[Any]
|
|
167
|
+
db_params.extend(db_params_data)
|
|
168
|
+
saved_db_data["db.params"] = db_params
|
|
169
|
+
span.set_attribute("db.params", _serialize_span_attribute(db_params))
|
|
144
170
|
|
|
145
171
|
return f(*args, **kwargs)
|
|
146
172
|
|
|
147
173
|
return _inner_send_data
|
|
148
174
|
|
|
149
175
|
|
|
150
|
-
def
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
176
|
+
def _get_db_data(connection: clickhouse_driver.connection.Connection) -> Dict[str, str]:
|
|
177
|
+
return {
|
|
178
|
+
SPANDATA.DB_SYSTEM: "clickhouse",
|
|
179
|
+
SPANDATA.SERVER_ADDRESS: connection.host,
|
|
180
|
+
SPANDATA.SERVER_PORT: connection.port,
|
|
181
|
+
SPANDATA.DB_NAME: connection.database,
|
|
182
|
+
SPANDATA.DB_USER: connection.user,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _set_on_span(span: Span, data: Dict[str, Any]) -> None:
|
|
187
|
+
for key, value in data.items():
|
|
188
|
+
span.set_attribute(key, _serialize_span_attribute(value))
|
|
@@ -147,6 +147,7 @@ def _wrap_chat(f, streaming):
|
|
|
147
147
|
op=consts.OP.COHERE_CHAT_COMPLETIONS_CREATE,
|
|
148
148
|
name="cohere.client.Chat",
|
|
149
149
|
origin=CohereIntegration.origin,
|
|
150
|
+
only_if_parent=True,
|
|
150
151
|
)
|
|
151
152
|
span.__enter__()
|
|
152
153
|
try:
|
|
@@ -233,6 +234,7 @@ def _wrap_embed(f):
|
|
|
233
234
|
op=consts.OP.COHERE_EMBEDDINGS_CREATE,
|
|
234
235
|
name="Cohere Embedding Creation",
|
|
235
236
|
origin=CohereIntegration.origin,
|
|
237
|
+
only_if_parent=True,
|
|
236
238
|
) as span:
|
|
237
239
|
if "texts" in kwargs and (
|
|
238
240
|
should_send_default_pii() and integration.include_prompts
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import functools
|
|
1
2
|
import inspect
|
|
2
3
|
import sys
|
|
3
4
|
import threading
|
|
@@ -5,10 +6,9 @@ import weakref
|
|
|
5
6
|
from importlib import import_module
|
|
6
7
|
|
|
7
8
|
import sentry_sdk
|
|
8
|
-
from sentry_sdk.consts import OP, SPANDATA
|
|
9
|
+
from sentry_sdk.consts import OP, SPANDATA, SOURCE_FOR_STYLE, TransactionSource
|
|
9
10
|
from sentry_sdk.scope import add_global_event_processor, should_send_default_pii
|
|
10
11
|
from sentry_sdk.serializer import add_global_repr_processor
|
|
11
|
-
from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource
|
|
12
12
|
from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
|
|
13
13
|
from sentry_sdk.utils import (
|
|
14
14
|
AnnotatedValue,
|
|
@@ -55,6 +55,7 @@ try:
|
|
|
55
55
|
except ImportError:
|
|
56
56
|
raise DidNotEnable("Django not installed")
|
|
57
57
|
|
|
58
|
+
from sentry_sdk.integrations.django.caching import patch_caching
|
|
58
59
|
from sentry_sdk.integrations.django.transactions import LEGACY_RESOLVER
|
|
59
60
|
from sentry_sdk.integrations.django.templates import (
|
|
60
61
|
get_template_frame_from_exception,
|
|
@@ -64,11 +65,6 @@ from sentry_sdk.integrations.django.middleware import patch_django_middlewares
|
|
|
64
65
|
from sentry_sdk.integrations.django.signals_handlers import patch_signals
|
|
65
66
|
from sentry_sdk.integrations.django.views import patch_views
|
|
66
67
|
|
|
67
|
-
if DJANGO_VERSION[:2] > (1, 8):
|
|
68
|
-
from sentry_sdk.integrations.django.caching import patch_caching
|
|
69
|
-
else:
|
|
70
|
-
patch_caching = None # type: ignore
|
|
71
|
-
|
|
72
68
|
from typing import TYPE_CHECKING
|
|
73
69
|
|
|
74
70
|
if TYPE_CHECKING:
|
|
@@ -89,19 +85,6 @@ if TYPE_CHECKING:
|
|
|
89
85
|
from sentry_sdk._types import Event, Hint, EventProcessor, NotImplementedType
|
|
90
86
|
|
|
91
87
|
|
|
92
|
-
if DJANGO_VERSION < (1, 10):
|
|
93
|
-
|
|
94
|
-
def is_authenticated(request_user):
|
|
95
|
-
# type: (Any) -> bool
|
|
96
|
-
return request_user.is_authenticated()
|
|
97
|
-
|
|
98
|
-
else:
|
|
99
|
-
|
|
100
|
-
def is_authenticated(request_user):
|
|
101
|
-
# type: (Any) -> bool
|
|
102
|
-
return request_user.is_authenticated
|
|
103
|
-
|
|
104
|
-
|
|
105
88
|
TRANSACTION_STYLE_VALUES = ("function_name", "url")
|
|
106
89
|
|
|
107
90
|
|
|
@@ -131,7 +114,7 @@ class DjangoIntegration(Integration):
|
|
|
131
114
|
transaction_style="url", # type: str
|
|
132
115
|
middleware_spans=True, # type: bool
|
|
133
116
|
signals_spans=True, # type: bool
|
|
134
|
-
cache_spans=
|
|
117
|
+
cache_spans=True, # type: bool
|
|
135
118
|
signals_denylist=None, # type: Optional[list[signals.Signal]]
|
|
136
119
|
http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: tuple[str, ...]
|
|
137
120
|
):
|
|
@@ -321,6 +304,7 @@ def _patch_drf():
|
|
|
321
304
|
else:
|
|
322
305
|
old_drf_initial = APIView.initial
|
|
323
306
|
|
|
307
|
+
@functools.wraps(old_drf_initial)
|
|
324
308
|
def sentry_patched_drf_initial(self, request, *args, **kwargs):
|
|
325
309
|
# type: (APIView, Any, *Any, **Any) -> Any
|
|
326
310
|
with capture_internal_exceptions():
|
|
@@ -413,11 +397,13 @@ def _set_transaction_name_and_source(scope, transaction_style, request):
|
|
|
413
397
|
if hasattr(urlconf, "handler404"):
|
|
414
398
|
handler = urlconf.handler404
|
|
415
399
|
if isinstance(handler, str):
|
|
416
|
-
scope.
|
|
400
|
+
scope.set_transaction_name(handler)
|
|
417
401
|
else:
|
|
418
|
-
|
|
402
|
+
name = transaction_from_function(
|
|
419
403
|
getattr(handler, "view_class", handler)
|
|
420
404
|
)
|
|
405
|
+
if isinstance(name, str):
|
|
406
|
+
scope.set_transaction_name(name)
|
|
421
407
|
except Exception:
|
|
422
408
|
pass
|
|
423
409
|
|
|
@@ -471,6 +457,7 @@ def _patch_get_response():
|
|
|
471
457
|
|
|
472
458
|
old_get_response = BaseHandler.get_response
|
|
473
459
|
|
|
460
|
+
@functools.wraps(old_get_response)
|
|
474
461
|
def sentry_patched_get_response(self, request):
|
|
475
462
|
# type: (Any, WSGIRequest) -> Union[HttpResponse, BaseException]
|
|
476
463
|
_before_get_response(request)
|
|
@@ -594,7 +581,7 @@ def _set_user_info(request, event):
|
|
|
594
581
|
|
|
595
582
|
user = getattr(request, "user", None)
|
|
596
583
|
|
|
597
|
-
if user is None or not is_authenticated
|
|
584
|
+
if user is None or not user.is_authenticated:
|
|
598
585
|
return
|
|
599
586
|
|
|
600
587
|
try:
|
|
@@ -621,20 +608,11 @@ def install_sql_hook():
|
|
|
621
608
|
except ImportError:
|
|
622
609
|
from django.db.backends.util import CursorWrapper
|
|
623
610
|
|
|
624
|
-
|
|
625
|
-
# django 1.6 and 1.7 compatability
|
|
626
|
-
from django.db.backends import BaseDatabaseWrapper
|
|
627
|
-
except ImportError:
|
|
628
|
-
# django 1.8 or later
|
|
629
|
-
from django.db.backends.base.base import BaseDatabaseWrapper
|
|
611
|
+
from django.db.backends.base.base import BaseDatabaseWrapper
|
|
630
612
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
real_connect = BaseDatabaseWrapper.connect
|
|
635
|
-
except AttributeError:
|
|
636
|
-
# This won't work on Django versions < 1.6
|
|
637
|
-
return
|
|
613
|
+
real_execute = CursorWrapper.execute
|
|
614
|
+
real_executemany = CursorWrapper.executemany
|
|
615
|
+
real_connect = BaseDatabaseWrapper.connect
|
|
638
616
|
|
|
639
617
|
@ensure_integration_enabled(DjangoIntegration, real_execute)
|
|
640
618
|
def execute(self, sql, params=None):
|
|
@@ -650,8 +628,8 @@ def install_sql_hook():
|
|
|
650
628
|
_set_db_data(span, self)
|
|
651
629
|
result = real_execute(self, sql, params)
|
|
652
630
|
|
|
653
|
-
|
|
654
|
-
|
|
631
|
+
with capture_internal_exceptions():
|
|
632
|
+
add_query_source(span)
|
|
655
633
|
|
|
656
634
|
return result
|
|
657
635
|
|
|
@@ -670,8 +648,8 @@ def install_sql_hook():
|
|
|
670
648
|
|
|
671
649
|
result = real_executemany(self, sql, param_list)
|
|
672
650
|
|
|
673
|
-
|
|
674
|
-
|
|
651
|
+
with capture_internal_exceptions():
|
|
652
|
+
add_query_source(span)
|
|
675
653
|
|
|
676
654
|
return result
|
|
677
655
|
|
|
@@ -685,6 +663,7 @@ def install_sql_hook():
|
|
|
685
663
|
op=OP.DB,
|
|
686
664
|
name="connect",
|
|
687
665
|
origin=DjangoIntegration.origin_db,
|
|
666
|
+
only_if_parent=True,
|
|
688
667
|
) as span:
|
|
689
668
|
_set_db_data(span, self)
|
|
690
669
|
return real_connect(self)
|
|
@@ -699,7 +678,7 @@ def _set_db_data(span, cursor_or_db):
|
|
|
699
678
|
# type: (Span, Any) -> None
|
|
700
679
|
db = cursor_or_db.db if hasattr(cursor_or_db, "db") else cursor_or_db
|
|
701
680
|
vendor = db.vendor
|
|
702
|
-
span.
|
|
681
|
+
span.set_attribute(SPANDATA.DB_SYSTEM, vendor)
|
|
703
682
|
|
|
704
683
|
# Some custom backends override `__getattr__`, making it look like `cursor_or_db`
|
|
705
684
|
# actually has a `connection` and the `connection` has a `get_dsn_parameters`
|
|
@@ -732,16 +711,16 @@ def _set_db_data(span, cursor_or_db):
|
|
|
732
711
|
|
|
733
712
|
db_name = connection_params.get("dbname") or connection_params.get("database")
|
|
734
713
|
if db_name is not None:
|
|
735
|
-
span.
|
|
714
|
+
span.set_attribute(SPANDATA.DB_NAME, db_name)
|
|
736
715
|
|
|
737
716
|
server_address = connection_params.get("host")
|
|
738
717
|
if server_address is not None:
|
|
739
|
-
span.
|
|
718
|
+
span.set_attribute(SPANDATA.SERVER_ADDRESS, server_address)
|
|
740
719
|
|
|
741
720
|
server_port = connection_params.get("port")
|
|
742
721
|
if server_port is not None:
|
|
743
|
-
span.
|
|
722
|
+
span.set_attribute(SPANDATA.SERVER_PORT, str(server_port))
|
|
744
723
|
|
|
745
724
|
server_socket_address = connection_params.get("unix_socket")
|
|
746
725
|
if server_socket_address is not None:
|
|
747
|
-
span.
|
|
726
|
+
span.set_attribute(SPANDATA.SERVER_SOCKET_ADDRESS, server_socket_address)
|
|
@@ -88,6 +88,7 @@ def patch_django_asgi_handler_impl(cls):
|
|
|
88
88
|
|
|
89
89
|
old_app = cls.__call__
|
|
90
90
|
|
|
91
|
+
@functools.wraps(old_app)
|
|
91
92
|
async def sentry_patched_asgi_handler(self, scope, receive, send):
|
|
92
93
|
# type: (Any, Any, Any, Any) -> Any
|
|
93
94
|
integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
|
|
@@ -125,6 +126,7 @@ def patch_get_response_async(cls, _before_get_response):
|
|
|
125
126
|
# type: (Any, Any) -> None
|
|
126
127
|
old_get_response_async = cls.get_response_async
|
|
127
128
|
|
|
129
|
+
@functools.wraps(old_get_response_async)
|
|
128
130
|
async def sentry_patched_get_response_async(self, request):
|
|
129
131
|
# type: (Any, Any) -> Union[HttpResponse, BaseException]
|
|
130
132
|
_before_get_response(request)
|
|
@@ -142,6 +144,7 @@ def patch_channels_asgi_handler_impl(cls):
|
|
|
142
144
|
if channels.__version__ < "3.0.0":
|
|
143
145
|
old_app = cls.__call__
|
|
144
146
|
|
|
147
|
+
@functools.wraps(old_app)
|
|
145
148
|
async def sentry_patched_asgi_handler(self, receive, send):
|
|
146
149
|
# type: (Any, Any, Any) -> Any
|
|
147
150
|
integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
|
|
@@ -173,8 +176,8 @@ def wrap_async_view(callback):
|
|
|
173
176
|
async def sentry_wrapped_callback(request, *args, **kwargs):
|
|
174
177
|
# type: (Any, *Any, **Any) -> Any
|
|
175
178
|
current_scope = sentry_sdk.get_current_scope()
|
|
176
|
-
if current_scope.
|
|
177
|
-
current_scope.
|
|
179
|
+
if current_scope.root_span is not None:
|
|
180
|
+
current_scope.root_span.update_active_thread()
|
|
178
181
|
|
|
179
182
|
sentry_scope = sentry_sdk.get_isolation_scope()
|
|
180
183
|
if sentry_scope.profile is not None:
|
|
@@ -184,6 +187,7 @@ def wrap_async_view(callback):
|
|
|
184
187
|
op=OP.VIEW_RENDER,
|
|
185
188
|
name=request.resolver_match.view_name,
|
|
186
189
|
origin=DjangoIntegration.origin,
|
|
190
|
+
only_if_parent=True,
|
|
187
191
|
):
|
|
188
192
|
return await callback(request, *args, **kwargs)
|
|
189
193
|
|
|
@@ -54,27 +54,28 @@ def _patch_cache_method(cache, method_name, address, port):
|
|
|
54
54
|
op=op,
|
|
55
55
|
name=description,
|
|
56
56
|
origin=DjangoIntegration.origin,
|
|
57
|
+
only_if_parent=True,
|
|
57
58
|
) as span:
|
|
58
59
|
value = original_method(*args, **kwargs)
|
|
59
60
|
|
|
60
61
|
with capture_internal_exceptions():
|
|
61
62
|
if address is not None:
|
|
62
|
-
span.
|
|
63
|
+
span.set_attribute(SPANDATA.NETWORK_PEER_ADDRESS, address)
|
|
63
64
|
|
|
64
65
|
if port is not None:
|
|
65
|
-
span.
|
|
66
|
+
span.set_attribute(SPANDATA.NETWORK_PEER_PORT, port)
|
|
66
67
|
|
|
67
68
|
key = _get_safe_key(method_name, args, kwargs)
|
|
68
69
|
if key is not None:
|
|
69
|
-
span.
|
|
70
|
+
span.set_attribute(SPANDATA.CACHE_KEY, key)
|
|
70
71
|
|
|
71
72
|
item_size = None
|
|
72
73
|
if is_get_operation:
|
|
73
74
|
if value:
|
|
74
75
|
item_size = len(str(value))
|
|
75
|
-
span.
|
|
76
|
+
span.set_attribute(SPANDATA.CACHE_HIT, True)
|
|
76
77
|
else:
|
|
77
|
-
span.
|
|
78
|
+
span.set_attribute(SPANDATA.CACHE_HIT, False)
|
|
78
79
|
else: # TODO: We don't handle `get_or_set` which we should
|
|
79
80
|
arg_count = len(args)
|
|
80
81
|
if arg_count >= 2:
|
|
@@ -85,7 +86,7 @@ def _patch_cache_method(cache, method_name, address, port):
|
|
|
85
86
|
item_size = len(str(args[0]))
|
|
86
87
|
|
|
87
88
|
if item_size is not None:
|
|
88
|
-
span.
|
|
89
|
+
span.set_attribute(SPANDATA.CACHE_ITEM_SIZE, item_size)
|
|
89
90
|
|
|
90
91
|
return value
|
|
91
92
|
|
|
@@ -133,22 +134,10 @@ def _get_address_port(settings):
|
|
|
133
134
|
return address, int(port) if port is not None else None
|
|
134
135
|
|
|
135
136
|
|
|
136
|
-
def should_enable_cache_spans():
|
|
137
|
-
# type: () -> bool
|
|
138
|
-
from sentry_sdk.integrations.django import DjangoIntegration
|
|
139
|
-
|
|
140
|
-
client = sentry_sdk.get_client()
|
|
141
|
-
integration = client.get_integration(DjangoIntegration)
|
|
142
|
-
from django.conf import settings
|
|
143
|
-
|
|
144
|
-
return integration is not None and (
|
|
145
|
-
(client.spotlight is not None and settings.DEBUG is True)
|
|
146
|
-
or integration.cache_spans is True
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
|
|
150
137
|
def patch_caching():
|
|
151
138
|
# type: () -> None
|
|
139
|
+
from sentry_sdk.integrations.django import DjangoIntegration
|
|
140
|
+
|
|
152
141
|
if not hasattr(CacheHandler, "_sentry_patched"):
|
|
153
142
|
if DJANGO_VERSION < (3, 2):
|
|
154
143
|
original_get_item = CacheHandler.__getitem__
|
|
@@ -158,7 +147,8 @@ def patch_caching():
|
|
|
158
147
|
# type: (CacheHandler, str) -> Any
|
|
159
148
|
cache = original_get_item(self, alias)
|
|
160
149
|
|
|
161
|
-
|
|
150
|
+
integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
|
|
151
|
+
if integration is not None and integration.cache_spans:
|
|
162
152
|
from django.conf import settings
|
|
163
153
|
|
|
164
154
|
address, port = _get_address_port(
|
|
@@ -180,7 +170,8 @@ def patch_caching():
|
|
|
180
170
|
# type: (CacheHandler, str) -> Any
|
|
181
171
|
cache = original_create_connection(self, alias)
|
|
182
172
|
|
|
183
|
-
|
|
173
|
+
integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
|
|
174
|
+
if integration is not None and integration.cache_spans:
|
|
184
175
|
address, port = _get_address_port(self.settings[alias or "default"])
|
|
185
176
|
|
|
186
177
|
_patch_cache(cache, address, port)
|
|
@@ -89,6 +89,7 @@ def _wrap_middleware(middleware, middleware_name):
|
|
|
89
89
|
op=OP.MIDDLEWARE_DJANGO,
|
|
90
90
|
name=description,
|
|
91
91
|
origin=DjangoIntegration.origin,
|
|
92
|
+
only_if_parent=True,
|
|
92
93
|
)
|
|
93
94
|
middleware_span.set_tag("django.function_name", function_name)
|
|
94
95
|
middleware_span.set_tag("django.middleware_name", middleware_name)
|