sentry-sdk 2.26.1__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 +8 -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 +5 -1
- sentry_sdk/integrations/__init__.py +5 -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 +103 -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/launchdarkly.py +3 -3
- sentry_sdk/integrations/litestar.py +4 -2
- sentry_sdk/integrations/logging.py +12 -3
- sentry_sdk/integrations/openai.py +2 -0
- sentry_sdk/integrations/openfeature.py +3 -5
- 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/unleash.py +2 -3
- 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 -1134
- sentry_sdk/tracing_utils.py +134 -169
- sentry_sdk/transport.py +4 -104
- sentry_sdk/types.py +26 -2
- sentry_sdk/utils.py +169 -152
- {sentry_sdk-2.26.1.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.26.1.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.26.1.dist-info/RECORD +0 -152
- sentry_sdk-2.26.1.dist-info/entry_points.txt +0 -2
- {sentry_sdk-2.26.1.dist-info → sentry_sdk-3.0.0a1.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-2.26.1.dist-info → sentry_sdk-3.0.0a1.dist-info}/top_level.txt +0 -0
|
@@ -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,29 +23,29 @@ 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
|
-
span,
|
|
40
|
+
return _get_async_cluster_db_data(
|
|
41
41
|
# the AsyncClusterPipeline has always had a `_client` attr but it is private so potentially problematic and mypy
|
|
42
42
|
# does not recognize it - see https://github.com/redis/redis-py/blame/v5.0.0/redis/asyncio/cluster.py#L1386
|
|
43
43
|
async_redis_cluster_pipeline_instance._client, # type: ignore[attr-defined]
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def
|
|
48
|
-
# type: (
|
|
47
|
+
def _get_cluster_db_data(redis_cluster_instance):
|
|
48
|
+
# type: (RedisCluster[Any]) -> dict[str, Any]
|
|
49
49
|
default_node = redis_cluster_instance.get_default_node()
|
|
50
50
|
|
|
51
51
|
if default_node is not None:
|
|
@@ -53,7 +53,9 @@ def _set_cluster_db_data(span, redis_cluster_instance):
|
|
|
53
53
|
"host": default_node.host,
|
|
54
54
|
"port": default_node.port,
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
return _get_connection_data(connection_params)
|
|
57
|
+
else:
|
|
58
|
+
return {}
|
|
57
59
|
|
|
58
60
|
|
|
59
61
|
def _patch_redis_cluster():
|
|
@@ -67,13 +69,13 @@ def _patch_redis_cluster():
|
|
|
67
69
|
patch_redis_client(
|
|
68
70
|
RedisCluster,
|
|
69
71
|
is_cluster=True,
|
|
70
|
-
|
|
72
|
+
get_db_data_fn=_get_cluster_db_data,
|
|
71
73
|
)
|
|
72
74
|
patch_redis_pipeline(
|
|
73
75
|
cluster.ClusterPipeline,
|
|
74
76
|
is_cluster=True,
|
|
75
77
|
get_command_args_fn=_parse_rediscluster_command,
|
|
76
|
-
|
|
78
|
+
get_db_data_fn=_get_cluster_db_data,
|
|
77
79
|
)
|
|
78
80
|
|
|
79
81
|
try:
|
|
@@ -89,11 +91,11 @@ def _patch_redis_cluster():
|
|
|
89
91
|
patch_redis_async_client(
|
|
90
92
|
async_cluster.RedisCluster,
|
|
91
93
|
is_cluster=True,
|
|
92
|
-
|
|
94
|
+
get_db_data_fn=_get_async_cluster_db_data,
|
|
93
95
|
)
|
|
94
96
|
patch_redis_async_pipeline(
|
|
95
97
|
async_cluster.ClusterPipeline,
|
|
96
98
|
is_cluster=True,
|
|
97
99
|
get_command_args_fn=_parse_rediscluster_command,
|
|
98
|
-
|
|
100
|
+
get_db_data_fn=_get_async_cluster_pipeline_db_data,
|
|
99
101
|
)
|
|
@@ -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,12 +147,12 @@ def _parse_rediscluster_command(command):
|
|
|
105
147
|
return command.args
|
|
106
148
|
|
|
107
149
|
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
150
|
+
def _get_pipeline_data(is_cluster, get_command_args_fn, is_transaction, command_stack):
|
|
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]
|
|
114
156
|
|
|
115
157
|
commands = []
|
|
116
158
|
for i, arg in enumerate(command_stack):
|
|
@@ -120,25 +162,27 @@ def _set_pipeline_data(
|
|
|
120
162
|
command = get_command_args_fn(arg)
|
|
121
163
|
commands.append(_get_safe_command(command[0], command[1:]))
|
|
122
164
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
"first_ten": commands,
|
|
128
|
-
},
|
|
129
|
-
)
|
|
165
|
+
data["redis.commands.count"] = len(command_stack)
|
|
166
|
+
data["redis.commands.first_ten"] = commands
|
|
167
|
+
|
|
168
|
+
return data
|
|
130
169
|
|
|
131
170
|
|
|
132
|
-
def
|
|
133
|
-
# type: (
|
|
134
|
-
|
|
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
|
+
|
|
135
177
|
if name:
|
|
136
|
-
|
|
137
|
-
|
|
178
|
+
data["redis.command"] = name
|
|
179
|
+
data[SPANDATA.DB_OPERATION] = name
|
|
138
180
|
|
|
139
181
|
if name and args:
|
|
140
182
|
name_low = name.lower()
|
|
141
183
|
if (name_low in _SINGLE_KEY_COMMANDS) or (
|
|
142
184
|
name_low in _MULTI_KEY_COMMANDS and len(args) == 1
|
|
143
185
|
):
|
|
144
|
-
|
|
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
|
|
@@ -32,16 +32,14 @@ Each native extension requires its own integration.
|
|
|
32
32
|
|
|
33
33
|
import json
|
|
34
34
|
from enum import Enum, auto
|
|
35
|
-
from typing import Any, Callable, Dict,
|
|
35
|
+
from typing import Any, Callable, Dict, Optional
|
|
36
36
|
|
|
37
37
|
import sentry_sdk
|
|
38
38
|
from sentry_sdk.integrations import Integration
|
|
39
39
|
from sentry_sdk.scope import should_send_default_pii
|
|
40
|
-
from sentry_sdk.tracing import Span
|
|
40
|
+
from sentry_sdk.tracing import Span
|
|
41
41
|
from sentry_sdk.utils import SENSITIVE_DATA_SUBSTITUTE
|
|
42
42
|
|
|
43
|
-
TraceState = Optional[Tuple[Optional[SentrySpan], SentrySpan]]
|
|
44
|
-
|
|
45
43
|
|
|
46
44
|
class RustTracingLevel(Enum):
|
|
47
45
|
Trace = "TRACE"
|
|
@@ -171,7 +169,7 @@ class RustTracingLayer:
|
|
|
171
169
|
else self.include_tracing_fields
|
|
172
170
|
)
|
|
173
171
|
|
|
174
|
-
def on_event(self, event: str, _span_state:
|
|
172
|
+
def on_event(self, event: str, _span_state: Optional[Span]) -> None:
|
|
175
173
|
deserialized_event = json.loads(event)
|
|
176
174
|
metadata = deserialized_event.get("metadata", {})
|
|
177
175
|
|
|
@@ -185,7 +183,7 @@ class RustTracingLayer:
|
|
|
185
183
|
elif event_type == EventTypeMapping.Event:
|
|
186
184
|
process_event(deserialized_event)
|
|
187
185
|
|
|
188
|
-
def on_new_span(self, attrs: str, span_id: str) ->
|
|
186
|
+
def on_new_span(self, attrs: str, span_id: str) -> Optional[Span]:
|
|
189
187
|
attrs = json.loads(attrs)
|
|
190
188
|
metadata = attrs.get("metadata", {})
|
|
191
189
|
|
|
@@ -205,48 +203,35 @@ class RustTracingLayer:
|
|
|
205
203
|
else:
|
|
206
204
|
sentry_span_name = "<unknown>"
|
|
207
205
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
parent_sentry_span = scope.span
|
|
216
|
-
if parent_sentry_span:
|
|
217
|
-
sentry_span = parent_sentry_span.start_child(**kwargs)
|
|
218
|
-
else:
|
|
219
|
-
sentry_span = scope.start_span(**kwargs)
|
|
206
|
+
span = sentry_sdk.start_span(
|
|
207
|
+
op="function",
|
|
208
|
+
name=sentry_span_name,
|
|
209
|
+
origin=self.origin,
|
|
210
|
+
only_if_parent=True,
|
|
211
|
+
)
|
|
212
|
+
span.__enter__()
|
|
220
213
|
|
|
221
214
|
fields = metadata.get("fields", [])
|
|
222
215
|
for field in fields:
|
|
223
216
|
if self._include_tracing_fields():
|
|
224
|
-
|
|
225
|
-
else:
|
|
226
|
-
sentry_span.set_data(field, SENSITIVE_DATA_SUBSTITUTE)
|
|
227
|
-
|
|
228
|
-
scope.span = sentry_span
|
|
229
|
-
return (parent_sentry_span, sentry_span)
|
|
230
|
-
|
|
231
|
-
def on_close(self, span_id: str, span_state: TraceState) -> None:
|
|
232
|
-
if span_state is None:
|
|
233
|
-
return
|
|
234
|
-
|
|
235
|
-
parent_sentry_span, sentry_span = span_state
|
|
236
|
-
sentry_span.finish()
|
|
237
|
-
sentry_sdk.get_current_scope().span = parent_sentry_span
|
|
238
|
-
|
|
239
|
-
def on_record(self, span_id: str, values: str, span_state: TraceState) -> None:
|
|
240
|
-
if span_state is None:
|
|
241
|
-
return
|
|
242
|
-
_parent_sentry_span, sentry_span = span_state
|
|
243
|
-
|
|
244
|
-
deserialized_values = json.loads(values)
|
|
245
|
-
for key, value in deserialized_values.items():
|
|
246
|
-
if self._include_tracing_fields():
|
|
247
|
-
sentry_span.set_data(key, value)
|
|
217
|
+
span.set_attribute(field, attrs.get(field))
|
|
248
218
|
else:
|
|
249
|
-
|
|
219
|
+
span.set_attribute(field, SENSITIVE_DATA_SUBSTITUTE)
|
|
220
|
+
|
|
221
|
+
return span
|
|
222
|
+
|
|
223
|
+
def on_close(self, span_id: str, span: Optional[Span]) -> None:
|
|
224
|
+
if span is not None:
|
|
225
|
+
span.__exit__(None, None, None)
|
|
226
|
+
|
|
227
|
+
def on_record(self, span_id: str, values: str, span: Optional[Span]) -> None:
|
|
228
|
+
if span is not None:
|
|
229
|
+
deserialized_values = json.loads(values)
|
|
230
|
+
for key, value in deserialized_values.items():
|
|
231
|
+
if self._include_tracing_fields():
|
|
232
|
+
span.set_attribute(key, value)
|
|
233
|
+
else:
|
|
234
|
+
span.set_attribute(key, SENSITIVE_DATA_SUBSTITUTE)
|
|
250
235
|
|
|
251
236
|
|
|
252
237
|
class RustTracingIntegration(Integration):
|
sentry_sdk/integrations/sanic.py
CHANGED
|
@@ -4,7 +4,6 @@ from inspect import isawaitable
|
|
|
4
4
|
from urllib.parse import urlsplit
|
|
5
5
|
|
|
6
6
|
import sentry_sdk
|
|
7
|
-
from sentry_sdk import continue_trace
|
|
8
7
|
from sentry_sdk.consts import OP
|
|
9
8
|
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
|
|
10
9
|
from sentry_sdk.integrations._wsgi_common import RequestExtractor, _filter_headers
|
|
@@ -182,21 +181,25 @@ async def _context_enter(request):
|
|
|
182
181
|
return
|
|
183
182
|
|
|
184
183
|
weak_request = weakref.ref(request)
|
|
185
|
-
request.ctx.
|
|
186
|
-
scope = request.ctx.
|
|
184
|
+
request.ctx._sentry_scope_manager = sentry_sdk.isolation_scope()
|
|
185
|
+
scope = request.ctx._sentry_scope_manager.__enter__()
|
|
186
|
+
request.ctx._sentry_scope = scope
|
|
187
|
+
|
|
188
|
+
scope.set_transaction_name(request.path, TransactionSource.URL)
|
|
187
189
|
scope.clear_breadcrumbs()
|
|
188
190
|
scope.add_event_processor(_make_request_processor(weak_request))
|
|
189
191
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
# TODO-neel-potel test if this works
|
|
193
|
+
request.ctx._sentry_continue_trace = sentry_sdk.continue_trace(
|
|
194
|
+
dict(request.headers)
|
|
195
|
+
)
|
|
196
|
+
request.ctx._sentry_continue_trace.__enter__()
|
|
197
|
+
request.ctx._sentry_transaction = sentry_sdk.start_span(
|
|
192
198
|
op=OP.HTTP_SERVER,
|
|
193
199
|
# Unless the request results in a 404 error, the name and source will get overwritten in _set_transaction
|
|
194
200
|
name=request.path,
|
|
195
201
|
source=TransactionSource.URL,
|
|
196
202
|
origin=SanicIntegration.origin,
|
|
197
|
-
)
|
|
198
|
-
request.ctx._sentry_transaction = sentry_sdk.start_transaction(
|
|
199
|
-
transaction
|
|
200
203
|
).__enter__()
|
|
201
204
|
|
|
202
205
|
|
|
@@ -211,16 +214,23 @@ async def _context_exit(request, response=None):
|
|
|
211
214
|
response_status = None if response is None else response.status
|
|
212
215
|
|
|
213
216
|
# This capture_internal_exceptions block has been intentionally nested here, so that in case an exception
|
|
214
|
-
# happens while trying to end the transaction, we still attempt to exit the
|
|
217
|
+
# happens while trying to end the transaction, we still attempt to exit the scope.
|
|
215
218
|
with capture_internal_exceptions():
|
|
216
219
|
request.ctx._sentry_transaction.set_http_status(response_status)
|
|
217
|
-
|
|
220
|
+
|
|
221
|
+
if (
|
|
218
222
|
isinstance(integration, SanicIntegration)
|
|
219
|
-
and response_status
|
|
220
|
-
)
|
|
223
|
+
and response_status in integration._unsampled_statuses
|
|
224
|
+
):
|
|
225
|
+
# drop the event in an event processor
|
|
226
|
+
request.ctx._sentry_scope.add_event_processor(
|
|
227
|
+
lambda _event, _hint: None
|
|
228
|
+
)
|
|
229
|
+
|
|
221
230
|
request.ctx._sentry_transaction.__exit__(None, None, None)
|
|
231
|
+
request.ctx._sentry_continue_trace.__exit__(None, None, None)
|
|
222
232
|
|
|
223
|
-
request.ctx.
|
|
233
|
+
request.ctx._sentry_scope_manager.__exit__(None, None, None)
|
|
224
234
|
|
|
225
235
|
|
|
226
236
|
async def _set_transaction(request, route, **_):
|
|
@@ -61,10 +61,13 @@ def _patch_create_connection():
|
|
|
61
61
|
op=OP.SOCKET_CONNECTION,
|
|
62
62
|
name=_get_span_description(address[0], address[1]),
|
|
63
63
|
origin=SocketIntegration.origin,
|
|
64
|
+
only_if_parent=True,
|
|
64
65
|
) as span:
|
|
65
|
-
|
|
66
|
-
span.
|
|
67
|
-
span.
|
|
66
|
+
host, port = address
|
|
67
|
+
span.set_attribute("address.host", host)
|
|
68
|
+
span.set_attribute("address.port", port)
|
|
69
|
+
span.set_attribute("timeout", timeout)
|
|
70
|
+
span.set_attribute("source_address", source_address)
|
|
68
71
|
|
|
69
72
|
return real_create_connection(
|
|
70
73
|
address=address, timeout=timeout, source_address=source_address
|
|
@@ -87,9 +90,10 @@ def _patch_getaddrinfo():
|
|
|
87
90
|
op=OP.SOCKET_DNS,
|
|
88
91
|
name=_get_span_description(host, port),
|
|
89
92
|
origin=SocketIntegration.origin,
|
|
93
|
+
only_if_parent=True,
|
|
90
94
|
) as span:
|
|
91
|
-
span.
|
|
92
|
-
span.
|
|
95
|
+
span.set_attribute("host", host)
|
|
96
|
+
span.set_attribute("port", port)
|
|
93
97
|
|
|
94
98
|
return real_getaddrinfo(host, port, family, type, proto, flags)
|
|
95
99
|
|