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
|
@@ -3,14 +3,15 @@ from sentry_sdk.consts import OP
|
|
|
3
3
|
from sentry_sdk.integrations.redis.consts import SPAN_ORIGIN
|
|
4
4
|
from sentry_sdk.integrations.redis.modules.caches import (
|
|
5
5
|
_compile_cache_span_properties,
|
|
6
|
-
|
|
6
|
+
_get_cache_data,
|
|
7
7
|
)
|
|
8
8
|
from sentry_sdk.integrations.redis.modules.queries import _compile_db_span_properties
|
|
9
9
|
from sentry_sdk.integrations.redis.utils import (
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
_create_breadcrumb,
|
|
11
|
+
_get_client_data,
|
|
12
|
+
_get_pipeline_data,
|
|
13
|
+
_update_span,
|
|
12
14
|
)
|
|
13
|
-
from sentry_sdk.tracing import Span
|
|
14
15
|
from sentry_sdk.utils import capture_internal_exceptions
|
|
15
16
|
|
|
16
17
|
from typing import TYPE_CHECKING
|
|
@@ -24,9 +25,9 @@ def patch_redis_pipeline(
|
|
|
24
25
|
pipeline_cls,
|
|
25
26
|
is_cluster,
|
|
26
27
|
get_command_args_fn,
|
|
27
|
-
|
|
28
|
+
get_db_data_fn,
|
|
28
29
|
):
|
|
29
|
-
# type: (Any, bool, Any, Callable[[
|
|
30
|
+
# type: (Any, bool, Any, Callable[[Any], dict[str, Any]]) -> None
|
|
30
31
|
old_execute = pipeline_cls.execute
|
|
31
32
|
|
|
32
33
|
from sentry_sdk.integrations.redis import RedisIntegration
|
|
@@ -40,30 +41,31 @@ def patch_redis_pipeline(
|
|
|
40
41
|
op=OP.DB_REDIS,
|
|
41
42
|
name="redis.pipeline.execute",
|
|
42
43
|
origin=SPAN_ORIGIN,
|
|
44
|
+
only_if_parent=True,
|
|
43
45
|
) as span:
|
|
44
46
|
with capture_internal_exceptions():
|
|
45
|
-
command_seq = None
|
|
46
47
|
try:
|
|
47
48
|
command_seq = self._execution_strategy.command_queue
|
|
48
49
|
except AttributeError:
|
|
49
50
|
command_seq = self.command_stack
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
command_seq,
|
|
52
|
+
span_data = get_db_data_fn(self)
|
|
53
|
+
pipeline_data = _get_pipeline_data(
|
|
54
|
+
is_cluster=is_cluster,
|
|
55
|
+
get_command_args_fn=get_command_args_fn,
|
|
56
|
+
is_transaction=False if is_cluster else self.transaction,
|
|
57
|
+
command_seq=command_seq,
|
|
58
58
|
)
|
|
59
|
+
_update_span(span, span_data, pipeline_data)
|
|
60
|
+
_create_breadcrumb("redis.pipeline.execute", span_data, pipeline_data)
|
|
59
61
|
|
|
60
62
|
return old_execute(self, *args, **kwargs)
|
|
61
63
|
|
|
62
64
|
pipeline_cls.execute = sentry_patched_execute
|
|
63
65
|
|
|
64
66
|
|
|
65
|
-
def patch_redis_client(cls, is_cluster,
|
|
66
|
-
# type: (Any, bool, Callable[[
|
|
67
|
+
def patch_redis_client(cls, is_cluster, get_db_data_fn):
|
|
68
|
+
# type: (Any, bool, Callable[[Any], dict[str, Any]]) -> None
|
|
67
69
|
"""
|
|
68
70
|
This function can be used to instrument custom redis client classes or
|
|
69
71
|
subclasses.
|
|
@@ -91,6 +93,7 @@ def patch_redis_client(cls, is_cluster, set_db_data_fn):
|
|
|
91
93
|
op=cache_properties["op"],
|
|
92
94
|
name=cache_properties["description"],
|
|
93
95
|
origin=SPAN_ORIGIN,
|
|
96
|
+
only_if_parent=True,
|
|
94
97
|
)
|
|
95
98
|
cache_span.__enter__()
|
|
96
99
|
|
|
@@ -100,18 +103,24 @@ def patch_redis_client(cls, is_cluster, set_db_data_fn):
|
|
|
100
103
|
op=db_properties["op"],
|
|
101
104
|
name=db_properties["description"],
|
|
102
105
|
origin=SPAN_ORIGIN,
|
|
106
|
+
only_if_parent=True,
|
|
103
107
|
)
|
|
104
108
|
db_span.__enter__()
|
|
105
109
|
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
db_span_data = get_db_data_fn(self)
|
|
111
|
+
db_client_span_data = _get_client_data(is_cluster, name, *args)
|
|
112
|
+
_update_span(db_span, db_span_data, db_client_span_data)
|
|
113
|
+
_create_breadcrumb(
|
|
114
|
+
db_properties["description"], db_span_data, db_client_span_data
|
|
115
|
+
)
|
|
108
116
|
|
|
109
117
|
value = old_execute_command(self, name, *args, **kwargs)
|
|
110
118
|
|
|
111
119
|
db_span.__exit__(None, None, None)
|
|
112
120
|
|
|
113
121
|
if cache_span:
|
|
114
|
-
|
|
122
|
+
cache_span_data = _get_cache_data(self, cache_properties, value)
|
|
123
|
+
_update_span(cache_span, cache_span_data)
|
|
115
124
|
cache_span.__exit__(None, None, None)
|
|
116
125
|
|
|
117
126
|
return value
|
|
@@ -13,7 +13,6 @@ from typing import TYPE_CHECKING
|
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
15
|
from sentry_sdk.integrations.redis import RedisIntegration
|
|
16
|
-
from sentry_sdk.tracing import Span
|
|
17
16
|
from typing import Any, Optional
|
|
18
17
|
|
|
19
18
|
|
|
@@ -75,22 +74,24 @@ def _get_cache_span_description(redis_command, args, kwargs, integration):
|
|
|
75
74
|
return description
|
|
76
75
|
|
|
77
76
|
|
|
78
|
-
def
|
|
79
|
-
# type: (
|
|
77
|
+
def _get_cache_data(redis_client, properties, return_value):
|
|
78
|
+
# type: (Any, dict[str, Any], Optional[Any]) -> dict[str, Any]
|
|
79
|
+
data = {}
|
|
80
|
+
|
|
80
81
|
with capture_internal_exceptions():
|
|
81
|
-
|
|
82
|
+
data[SPANDATA.CACHE_KEY] = properties["key"]
|
|
82
83
|
|
|
83
84
|
if properties["redis_command"] in GET_COMMANDS:
|
|
84
85
|
if return_value is not None:
|
|
85
|
-
|
|
86
|
+
data[SPANDATA.CACHE_HIT] = True
|
|
86
87
|
size = (
|
|
87
88
|
len(str(return_value).encode("utf-8"))
|
|
88
89
|
if not isinstance(return_value, bytes)
|
|
89
90
|
else len(return_value)
|
|
90
91
|
)
|
|
91
|
-
|
|
92
|
+
data[SPANDATA.CACHE_ITEM_SIZE] = size
|
|
92
93
|
else:
|
|
93
|
-
|
|
94
|
+
data[SPANDATA.CACHE_HIT] = False
|
|
94
95
|
|
|
95
96
|
elif properties["redis_command"] in SET_COMMANDS:
|
|
96
97
|
if properties["value"] is not None:
|
|
@@ -99,7 +100,7 @@ def _set_cache_data(span, redis_client, properties, return_value):
|
|
|
99
100
|
if not isinstance(properties["value"], bytes)
|
|
100
101
|
else len(properties["value"])
|
|
101
102
|
)
|
|
102
|
-
|
|
103
|
+
data[SPANDATA.CACHE_ITEM_SIZE] = size
|
|
103
104
|
|
|
104
105
|
try:
|
|
105
106
|
connection_params = redis_client.connection_pool.connection_kwargs
|
|
@@ -114,8 +115,10 @@ def _set_cache_data(span, redis_client, properties, return_value):
|
|
|
114
115
|
|
|
115
116
|
host = connection_params.get("host")
|
|
116
117
|
if host is not None:
|
|
117
|
-
|
|
118
|
+
data[SPANDATA.NETWORK_PEER_ADDRESS] = host
|
|
118
119
|
|
|
119
120
|
port = connection_params.get("port")
|
|
120
121
|
if port is not None:
|
|
121
|
-
|
|
122
|
+
data[SPANDATA.NETWORK_PEER_PORT] = port
|
|
123
|
+
|
|
124
|
+
return data
|
|
@@ -11,7 +11,6 @@ from typing import TYPE_CHECKING
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
from redis import Redis
|
|
13
13
|
from sentry_sdk.integrations.redis import RedisIntegration
|
|
14
|
-
from sentry_sdk.tracing import Span
|
|
15
14
|
from typing import Any
|
|
16
15
|
|
|
17
16
|
|
|
@@ -43,26 +42,30 @@ def _get_db_span_description(integration, command_name, args):
|
|
|
43
42
|
return description
|
|
44
43
|
|
|
45
44
|
|
|
46
|
-
def
|
|
47
|
-
# type: (
|
|
48
|
-
|
|
45
|
+
def _get_connection_data(connection_params):
|
|
46
|
+
# type: (dict[str, Any]) -> dict[str, Any]
|
|
47
|
+
data = {
|
|
48
|
+
SPANDATA.DB_SYSTEM: "redis",
|
|
49
|
+
}
|
|
49
50
|
|
|
50
51
|
db = connection_params.get("db")
|
|
51
52
|
if db is not None:
|
|
52
|
-
|
|
53
|
+
data[SPANDATA.DB_NAME] = str(db)
|
|
53
54
|
|
|
54
55
|
host = connection_params.get("host")
|
|
55
56
|
if host is not None:
|
|
56
|
-
|
|
57
|
+
data[SPANDATA.SERVER_ADDRESS] = host
|
|
57
58
|
|
|
58
59
|
port = connection_params.get("port")
|
|
59
60
|
if port is not None:
|
|
60
|
-
|
|
61
|
+
data[SPANDATA.SERVER_PORT] = port
|
|
62
|
+
|
|
63
|
+
return data
|
|
61
64
|
|
|
62
65
|
|
|
63
|
-
def
|
|
64
|
-
# type: (
|
|
66
|
+
def _get_db_data(redis_instance):
|
|
67
|
+
# type: (Redis[Any]) -> dict[str, Any]
|
|
65
68
|
try:
|
|
66
|
-
|
|
69
|
+
return _get_connection_data(redis_instance.connection_pool.connection_kwargs)
|
|
67
70
|
except AttributeError:
|
|
68
|
-
|
|
71
|
+
return {} # connections_kwargs may be missing in some cases
|
|
@@ -5,7 +5,7 @@ https://github.com/getsentry/rb
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from sentry_sdk.integrations.redis._sync_common import patch_redis_client
|
|
8
|
-
from sentry_sdk.integrations.redis.modules.queries import
|
|
8
|
+
from sentry_sdk.integrations.redis.modules.queries import _get_db_data
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def _patch_rb():
|
|
@@ -18,15 +18,15 @@ def _patch_rb():
|
|
|
18
18
|
patch_redis_client(
|
|
19
19
|
rb.clients.FanoutClient,
|
|
20
20
|
is_cluster=False,
|
|
21
|
-
|
|
21
|
+
get_db_data_fn=_get_db_data,
|
|
22
22
|
)
|
|
23
23
|
patch_redis_client(
|
|
24
24
|
rb.clients.MappingClient,
|
|
25
25
|
is_cluster=False,
|
|
26
|
-
|
|
26
|
+
get_db_data_fn=_get_db_data,
|
|
27
27
|
)
|
|
28
28
|
patch_redis_client(
|
|
29
29
|
rb.clients.RoutingClient,
|
|
30
30
|
is_cluster=False,
|
|
31
|
-
|
|
31
|
+
get_db_data_fn=_get_db_data,
|
|
32
32
|
)
|
|
@@ -8,7 +8,7 @@ from sentry_sdk.integrations.redis._sync_common import (
|
|
|
8
8
|
patch_redis_client,
|
|
9
9
|
patch_redis_pipeline,
|
|
10
10
|
)
|
|
11
|
-
from sentry_sdk.integrations.redis.modules.queries import
|
|
11
|
+
from sentry_sdk.integrations.redis.modules.queries import _get_db_data
|
|
12
12
|
|
|
13
13
|
from typing import TYPE_CHECKING
|
|
14
14
|
|
|
@@ -26,13 +26,13 @@ def _patch_redis(StrictRedis, client): # noqa: N803
|
|
|
26
26
|
patch_redis_client(
|
|
27
27
|
StrictRedis,
|
|
28
28
|
is_cluster=False,
|
|
29
|
-
|
|
29
|
+
get_db_data_fn=_get_db_data,
|
|
30
30
|
)
|
|
31
31
|
patch_redis_pipeline(
|
|
32
32
|
client.Pipeline,
|
|
33
33
|
is_cluster=False,
|
|
34
34
|
get_command_args_fn=_get_redis_command_args,
|
|
35
|
-
|
|
35
|
+
get_db_data_fn=_get_db_data,
|
|
36
36
|
)
|
|
37
37
|
try:
|
|
38
38
|
strict_pipeline = client.StrictPipeline
|
|
@@ -43,7 +43,7 @@ def _patch_redis(StrictRedis, client): # noqa: N803
|
|
|
43
43
|
strict_pipeline,
|
|
44
44
|
is_cluster=False,
|
|
45
45
|
get_command_args_fn=_get_redis_command_args,
|
|
46
|
-
|
|
46
|
+
get_db_data_fn=_get_db_data,
|
|
47
47
|
)
|
|
48
48
|
|
|
49
49
|
try:
|
|
@@ -59,11 +59,11 @@ def _patch_redis(StrictRedis, client): # noqa: N803
|
|
|
59
59
|
patch_redis_async_client(
|
|
60
60
|
redis.asyncio.client.StrictRedis,
|
|
61
61
|
is_cluster=False,
|
|
62
|
-
|
|
62
|
+
get_db_data_fn=_get_db_data,
|
|
63
63
|
)
|
|
64
64
|
patch_redis_async_pipeline(
|
|
65
65
|
redis.asyncio.client.Pipeline,
|
|
66
66
|
False,
|
|
67
67
|
_get_redis_command_args,
|
|
68
|
-
|
|
68
|
+
get_db_data_fn=_get_db_data,
|
|
69
69
|
)
|
|
@@ -9,7 +9,7 @@ from sentry_sdk.integrations.redis._sync_common import (
|
|
|
9
9
|
patch_redis_client,
|
|
10
10
|
patch_redis_pipeline,
|
|
11
11
|
)
|
|
12
|
-
from sentry_sdk.integrations.redis.modules.queries import
|
|
12
|
+
from sentry_sdk.integrations.redis.modules.queries import _get_connection_data
|
|
13
13
|
from sentry_sdk.integrations.redis.utils import _parse_rediscluster_command
|
|
14
14
|
|
|
15
15
|
from sentry_sdk.utils import capture_internal_exceptions
|
|
@@ -23,18 +23,19 @@ if TYPE_CHECKING:
|
|
|
23
23
|
RedisCluster as AsyncRedisCluster,
|
|
24
24
|
ClusterPipeline as AsyncClusterPipeline,
|
|
25
25
|
)
|
|
26
|
-
from sentry_sdk.tracing import Span
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
def
|
|
30
|
-
# type: (
|
|
28
|
+
def _get_async_cluster_db_data(async_redis_cluster_instance):
|
|
29
|
+
# type: (AsyncRedisCluster[Any]) -> dict[str, Any]
|
|
31
30
|
default_node = async_redis_cluster_instance.get_default_node()
|
|
32
31
|
if default_node is not None and default_node.connection_kwargs is not None:
|
|
33
|
-
|
|
32
|
+
return _get_connection_data(default_node.connection_kwargs)
|
|
33
|
+
else:
|
|
34
|
+
return {}
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
def
|
|
37
|
-
# type: (
|
|
37
|
+
def _get_async_cluster_pipeline_db_data(async_redis_cluster_pipeline_instance):
|
|
38
|
+
# type: (AsyncClusterPipeline[Any]) -> dict[str, Any]
|
|
38
39
|
with capture_internal_exceptions():
|
|
39
40
|
client = getattr(async_redis_cluster_pipeline_instance, "cluster_client", None)
|
|
40
41
|
if client is None:
|
|
@@ -46,14 +47,11 @@ def _set_async_cluster_pipeline_db_data(span, async_redis_cluster_pipeline_insta
|
|
|
46
47
|
async_redis_cluster_pipeline_instance._client # type: ignore[attr-defined]
|
|
47
48
|
)
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
span,
|
|
51
|
-
client,
|
|
52
|
-
)
|
|
50
|
+
return _get_async_cluster_db_data(client)
|
|
53
51
|
|
|
54
52
|
|
|
55
|
-
def
|
|
56
|
-
# type: (
|
|
53
|
+
def _get_cluster_db_data(redis_cluster_instance):
|
|
54
|
+
# type: (RedisCluster[Any]) -> dict[str, Any]
|
|
57
55
|
default_node = redis_cluster_instance.get_default_node()
|
|
58
56
|
|
|
59
57
|
if default_node is not None:
|
|
@@ -61,7 +59,9 @@ def _set_cluster_db_data(span, redis_cluster_instance):
|
|
|
61
59
|
"host": default_node.host,
|
|
62
60
|
"port": default_node.port,
|
|
63
61
|
}
|
|
64
|
-
|
|
62
|
+
return _get_connection_data(connection_params)
|
|
63
|
+
else:
|
|
64
|
+
return {}
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
def _patch_redis_cluster():
|
|
@@ -75,13 +75,13 @@ def _patch_redis_cluster():
|
|
|
75
75
|
patch_redis_client(
|
|
76
76
|
RedisCluster,
|
|
77
77
|
is_cluster=True,
|
|
78
|
-
|
|
78
|
+
get_db_data_fn=_get_cluster_db_data,
|
|
79
79
|
)
|
|
80
80
|
patch_redis_pipeline(
|
|
81
81
|
cluster.ClusterPipeline,
|
|
82
82
|
is_cluster=True,
|
|
83
83
|
get_command_args_fn=_parse_rediscluster_command,
|
|
84
|
-
|
|
84
|
+
get_db_data_fn=_get_cluster_db_data,
|
|
85
85
|
)
|
|
86
86
|
|
|
87
87
|
try:
|
|
@@ -97,11 +97,11 @@ def _patch_redis_cluster():
|
|
|
97
97
|
patch_redis_async_client(
|
|
98
98
|
async_cluster.RedisCluster,
|
|
99
99
|
is_cluster=True,
|
|
100
|
-
|
|
100
|
+
get_db_data_fn=_get_async_cluster_db_data,
|
|
101
101
|
)
|
|
102
102
|
patch_redis_async_pipeline(
|
|
103
103
|
async_cluster.ClusterPipeline,
|
|
104
104
|
is_cluster=True,
|
|
105
105
|
get_command_args_fn=_parse_rediscluster_command,
|
|
106
|
-
|
|
106
|
+
get_db_data_fn=_get_async_cluster_pipeline_db_data,
|
|
107
107
|
)
|
|
@@ -9,7 +9,7 @@ from sentry_sdk.integrations.redis._sync_common import (
|
|
|
9
9
|
patch_redis_client,
|
|
10
10
|
patch_redis_pipeline,
|
|
11
11
|
)
|
|
12
|
-
from sentry_sdk.integrations.redis.modules.queries import
|
|
12
|
+
from sentry_sdk.integrations.redis.modules.queries import _get_db_data
|
|
13
13
|
from sentry_sdk.integrations.redis.utils import _parse_rediscluster_command
|
|
14
14
|
|
|
15
15
|
|
|
@@ -23,7 +23,7 @@ def _patch_rediscluster():
|
|
|
23
23
|
patch_redis_client(
|
|
24
24
|
rediscluster.RedisCluster,
|
|
25
25
|
is_cluster=True,
|
|
26
|
-
|
|
26
|
+
get_db_data_fn=_get_db_data,
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
# up to v1.3.6, __version__ attribute is a tuple
|
|
@@ -37,7 +37,7 @@ def _patch_rediscluster():
|
|
|
37
37
|
patch_redis_client(
|
|
38
38
|
rediscluster.StrictRedisCluster,
|
|
39
39
|
is_cluster=True,
|
|
40
|
-
|
|
40
|
+
get_db_data_fn=_get_db_data,
|
|
41
41
|
)
|
|
42
42
|
else:
|
|
43
43
|
pipeline_cls = rediscluster.pipeline.ClusterPipeline
|
|
@@ -46,5 +46,5 @@ def _patch_rediscluster():
|
|
|
46
46
|
pipeline_cls,
|
|
47
47
|
is_cluster=True,
|
|
48
48
|
get_command_args_fn=_parse_rediscluster_command,
|
|
49
|
-
|
|
49
|
+
get_db_data_fn=_get_db_data,
|
|
50
50
|
)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import sentry_sdk
|
|
1
2
|
from sentry_sdk.consts import SPANDATA
|
|
2
3
|
from sentry_sdk.integrations.redis.consts import (
|
|
3
4
|
_COMMANDS_INCLUDING_SENSITIVE_DATA,
|
|
@@ -16,6 +17,47 @@ if TYPE_CHECKING:
|
|
|
16
17
|
from sentry_sdk.tracing import Span
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
TAG_KEYS = [
|
|
21
|
+
"redis.command",
|
|
22
|
+
"redis.is_cluster",
|
|
23
|
+
"redis.key",
|
|
24
|
+
"redis.transaction",
|
|
25
|
+
SPANDATA.DB_OPERATION,
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _update_span(span, *data_bags):
|
|
30
|
+
# type: (Span, *dict[str, Any]) -> None
|
|
31
|
+
"""
|
|
32
|
+
Set tags and data on the given span to data from the given data bags.
|
|
33
|
+
"""
|
|
34
|
+
for data in data_bags:
|
|
35
|
+
for key, value in data.items():
|
|
36
|
+
if key in TAG_KEYS:
|
|
37
|
+
span.set_tag(key, value)
|
|
38
|
+
else:
|
|
39
|
+
span.set_attribute(key, value)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _create_breadcrumb(message, *data_bags):
|
|
43
|
+
# type: (str, *dict[str, Any]) -> None
|
|
44
|
+
"""
|
|
45
|
+
Create a breadcrumb containing the tags data from the given data bags.
|
|
46
|
+
"""
|
|
47
|
+
data = {}
|
|
48
|
+
for data in data_bags:
|
|
49
|
+
for key, value in data.items():
|
|
50
|
+
if key in TAG_KEYS:
|
|
51
|
+
data[key] = value
|
|
52
|
+
|
|
53
|
+
sentry_sdk.add_breadcrumb(
|
|
54
|
+
message=message,
|
|
55
|
+
type="redis",
|
|
56
|
+
category="redis",
|
|
57
|
+
data=data,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
19
61
|
def _get_safe_command(name, args):
|
|
20
62
|
# type: (str, Sequence[Any]) -> str
|
|
21
63
|
command_parts = [name]
|
|
@@ -105,44 +147,42 @@ def _parse_rediscluster_command(command):
|
|
|
105
147
|
return command.args
|
|
106
148
|
|
|
107
149
|
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
):
|
|
115
|
-
# type: (Span, bool, Any, bool, Sequence[Any]) -> None
|
|
116
|
-
span.set_tag("redis.is_cluster", is_cluster)
|
|
117
|
-
span.set_tag("redis.transaction", is_transaction)
|
|
150
|
+
def _get_pipeline_data(is_cluster, get_command_args_fn, is_transaction, command_seq):
|
|
151
|
+
# type: (bool, Any, bool, Sequence[Any]) -> dict[str, Any]
|
|
152
|
+
data = {
|
|
153
|
+
"redis.is_cluster": is_cluster,
|
|
154
|
+
"redis.transaction": is_transaction,
|
|
155
|
+
} # type: dict[str, Any]
|
|
118
156
|
|
|
119
157
|
commands = []
|
|
120
|
-
for i, arg in enumerate(
|
|
158
|
+
for i, arg in enumerate(command_seq):
|
|
121
159
|
if i >= _MAX_NUM_COMMANDS:
|
|
122
160
|
break
|
|
123
161
|
|
|
124
162
|
command = get_command_args_fn(arg)
|
|
125
163
|
commands.append(_get_safe_command(command[0], command[1:]))
|
|
126
164
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"first_ten": commands,
|
|
132
|
-
},
|
|
133
|
-
)
|
|
165
|
+
data["redis.commands.count"] = len(command_seq)
|
|
166
|
+
data["redis.commands.first_ten"] = commands
|
|
167
|
+
|
|
168
|
+
return data
|
|
134
169
|
|
|
135
170
|
|
|
136
|
-
def
|
|
137
|
-
# type: (
|
|
138
|
-
|
|
171
|
+
def _get_client_data(is_cluster, name, *args):
|
|
172
|
+
# type: (bool, str, *Any) -> dict[str, Any]
|
|
173
|
+
data = {
|
|
174
|
+
"redis.is_cluster": is_cluster,
|
|
175
|
+
} # type: dict[str, Any]
|
|
176
|
+
|
|
139
177
|
if name:
|
|
140
|
-
|
|
141
|
-
|
|
178
|
+
data["redis.command"] = name
|
|
179
|
+
data[SPANDATA.DB_OPERATION] = name
|
|
142
180
|
|
|
143
181
|
if name and args:
|
|
144
182
|
name_low = name.lower()
|
|
145
183
|
if (name_low in _SINGLE_KEY_COMMANDS) or (
|
|
146
184
|
name_low in _MULTI_KEY_COMMANDS and len(args) == 1
|
|
147
185
|
):
|
|
148
|
-
|
|
186
|
+
data["redis.key"] = args[0]
|
|
187
|
+
|
|
188
|
+
return data
|
sentry_sdk/integrations/rq.py
CHANGED
|
@@ -2,7 +2,6 @@ import weakref
|
|
|
2
2
|
|
|
3
3
|
import sentry_sdk
|
|
4
4
|
from sentry_sdk.consts import OP
|
|
5
|
-
from sentry_sdk.api import continue_trace
|
|
6
5
|
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
|
|
7
6
|
from sentry_sdk.integrations.logging import ignore_logger
|
|
8
7
|
from sentry_sdk.tracing import TransactionSource
|
|
@@ -33,6 +32,17 @@ if TYPE_CHECKING:
|
|
|
33
32
|
|
|
34
33
|
from rq.job import Job
|
|
35
34
|
|
|
35
|
+
DEFAULT_TRANSACTION_NAME = "unknown RQ task"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
JOB_PROPERTY_TO_ATTRIBUTE = {
|
|
39
|
+
"id": "messaging.message.id",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
QUEUE_PROPERTY_TO_ATTRIBUTE = {
|
|
43
|
+
"name": "messaging.destination.name",
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
|
|
37
47
|
class RqIntegration(Integration):
|
|
38
48
|
identifier = "rq"
|
|
@@ -47,28 +57,31 @@ class RqIntegration(Integration):
|
|
|
47
57
|
old_perform_job = Worker.perform_job
|
|
48
58
|
|
|
49
59
|
@ensure_integration_enabled(RqIntegration, old_perform_job)
|
|
50
|
-
def sentry_patched_perform_job(self, job, *args, **kwargs):
|
|
51
|
-
# type: (Any, Job,
|
|
60
|
+
def sentry_patched_perform_job(self, job, queue, *args, **kwargs):
|
|
61
|
+
# type: (Any, Job, Queue, *Any, **Any) -> bool
|
|
52
62
|
with sentry_sdk.new_scope() as scope:
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
try:
|
|
64
|
+
transaction_name = job.func_name or DEFAULT_TRANSACTION_NAME
|
|
65
|
+
except AttributeError:
|
|
66
|
+
transaction_name = DEFAULT_TRANSACTION_NAME
|
|
55
67
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
op=OP.QUEUE_TASK_RQ,
|
|
59
|
-
name="unknown RQ task",
|
|
60
|
-
source=TransactionSource.TASK,
|
|
61
|
-
origin=RqIntegration.origin,
|
|
68
|
+
scope.set_transaction_name(
|
|
69
|
+
transaction_name, source=TransactionSource.TASK
|
|
62
70
|
)
|
|
71
|
+
scope.clear_breadcrumbs()
|
|
72
|
+
scope.add_event_processor(_make_event_processor(weakref.ref(job)))
|
|
63
73
|
|
|
64
|
-
with
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
with sentry_sdk.start_transaction(
|
|
68
|
-
transaction,
|
|
69
|
-
custom_sampling_context={"rq_job": job},
|
|
74
|
+
with sentry_sdk.continue_trace(
|
|
75
|
+
job.meta.get("_sentry_trace_headers") or {}
|
|
70
76
|
):
|
|
71
|
-
|
|
77
|
+
with sentry_sdk.start_span(
|
|
78
|
+
op=OP.QUEUE_TASK_RQ,
|
|
79
|
+
name=transaction_name,
|
|
80
|
+
source=TransactionSource.TASK,
|
|
81
|
+
origin=RqIntegration.origin,
|
|
82
|
+
attributes=_prepopulate_attributes(job, queue),
|
|
83
|
+
):
|
|
84
|
+
rv = old_perform_job(self, job, queue, *args, **kwargs)
|
|
72
85
|
|
|
73
86
|
if self.is_horse:
|
|
74
87
|
# We're inside of a forked process and RQ is
|
|
@@ -102,11 +115,9 @@ class RqIntegration(Integration):
|
|
|
102
115
|
@ensure_integration_enabled(RqIntegration, old_enqueue_job)
|
|
103
116
|
def sentry_patched_enqueue_job(self, job, **kwargs):
|
|
104
117
|
# type: (Queue, Any, **Any) -> Any
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
scope.iter_trace_propagation_headers()
|
|
109
|
-
)
|
|
118
|
+
job.meta["_sentry_trace_headers"] = dict(
|
|
119
|
+
sentry_sdk.get_current_scope().iter_trace_propagation_headers()
|
|
120
|
+
)
|
|
110
121
|
|
|
111
122
|
return old_enqueue_job(self, job, **kwargs)
|
|
112
123
|
|
|
@@ -159,3 +170,37 @@ def _capture_exception(exc_info, **kwargs):
|
|
|
159
170
|
)
|
|
160
171
|
|
|
161
172
|
sentry_sdk.capture_event(event, hint=hint)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _prepopulate_attributes(job, queue):
|
|
176
|
+
# type: (Job, Queue) -> dict[str, Any]
|
|
177
|
+
attributes = {
|
|
178
|
+
"messaging.system": "rq",
|
|
179
|
+
"rq.job.id": job.id,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
for prop, attr in JOB_PROPERTY_TO_ATTRIBUTE.items():
|
|
183
|
+
if getattr(job, prop, None) is not None:
|
|
184
|
+
attributes[attr] = getattr(job, prop)
|
|
185
|
+
|
|
186
|
+
for prop, attr in QUEUE_PROPERTY_TO_ATTRIBUTE.items():
|
|
187
|
+
if getattr(queue, prop, None) is not None:
|
|
188
|
+
attributes[attr] = getattr(queue, prop)
|
|
189
|
+
|
|
190
|
+
if getattr(job, "args", None):
|
|
191
|
+
for i, arg in enumerate(job.args):
|
|
192
|
+
with capture_internal_exceptions():
|
|
193
|
+
attributes[f"rq.job.args.{i}"] = str(arg)
|
|
194
|
+
|
|
195
|
+
if getattr(job, "kwargs", None):
|
|
196
|
+
for kwarg, value in job.kwargs.items():
|
|
197
|
+
with capture_internal_exceptions():
|
|
198
|
+
attributes[f"rq.job.kwargs.{kwarg}"] = str(value)
|
|
199
|
+
|
|
200
|
+
func = job.func
|
|
201
|
+
if callable(func):
|
|
202
|
+
func = func.__name__
|
|
203
|
+
|
|
204
|
+
attributes["rq.job.func"] = str(func)
|
|
205
|
+
|
|
206
|
+
return attributes
|