sentry-sdk 0.18.0__py2.py3-none-any.whl → 2.46.0__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.
- sentry_sdk/__init__.py +48 -6
- sentry_sdk/_compat.py +64 -56
- sentry_sdk/_init_implementation.py +84 -0
- sentry_sdk/_log_batcher.py +172 -0
- sentry_sdk/_lru_cache.py +47 -0
- sentry_sdk/_metrics_batcher.py +167 -0
- sentry_sdk/_queue.py +81 -19
- sentry_sdk/_types.py +311 -11
- sentry_sdk/_werkzeug.py +98 -0
- sentry_sdk/ai/__init__.py +7 -0
- sentry_sdk/ai/monitoring.py +137 -0
- sentry_sdk/ai/utils.py +144 -0
- sentry_sdk/api.py +409 -67
- sentry_sdk/attachments.py +75 -0
- sentry_sdk/client.py +849 -103
- sentry_sdk/consts.py +1389 -34
- sentry_sdk/crons/__init__.py +10 -0
- sentry_sdk/crons/api.py +62 -0
- sentry_sdk/crons/consts.py +4 -0
- sentry_sdk/crons/decorator.py +135 -0
- sentry_sdk/debug.py +12 -15
- sentry_sdk/envelope.py +112 -61
- sentry_sdk/feature_flags.py +71 -0
- sentry_sdk/hub.py +442 -386
- sentry_sdk/integrations/__init__.py +228 -58
- sentry_sdk/integrations/_asgi_common.py +108 -0
- sentry_sdk/integrations/_wsgi_common.py +131 -40
- sentry_sdk/integrations/aiohttp.py +221 -72
- sentry_sdk/integrations/anthropic.py +439 -0
- sentry_sdk/integrations/argv.py +4 -6
- sentry_sdk/integrations/ariadne.py +161 -0
- sentry_sdk/integrations/arq.py +247 -0
- sentry_sdk/integrations/asgi.py +237 -135
- sentry_sdk/integrations/asyncio.py +144 -0
- sentry_sdk/integrations/asyncpg.py +208 -0
- sentry_sdk/integrations/atexit.py +13 -18
- sentry_sdk/integrations/aws_lambda.py +233 -80
- sentry_sdk/integrations/beam.py +27 -35
- sentry_sdk/integrations/boto3.py +137 -0
- sentry_sdk/integrations/bottle.py +91 -69
- sentry_sdk/integrations/celery/__init__.py +529 -0
- sentry_sdk/integrations/celery/beat.py +293 -0
- sentry_sdk/integrations/celery/utils.py +43 -0
- sentry_sdk/integrations/chalice.py +35 -28
- sentry_sdk/integrations/clickhouse_driver.py +177 -0
- sentry_sdk/integrations/cloud_resource_context.py +280 -0
- sentry_sdk/integrations/cohere.py +274 -0
- sentry_sdk/integrations/dedupe.py +32 -8
- sentry_sdk/integrations/django/__init__.py +343 -89
- sentry_sdk/integrations/django/asgi.py +201 -22
- sentry_sdk/integrations/django/caching.py +204 -0
- sentry_sdk/integrations/django/middleware.py +80 -32
- sentry_sdk/integrations/django/signals_handlers.py +91 -0
- sentry_sdk/integrations/django/templates.py +69 -2
- sentry_sdk/integrations/django/transactions.py +39 -14
- sentry_sdk/integrations/django/views.py +69 -16
- sentry_sdk/integrations/dramatiq.py +226 -0
- sentry_sdk/integrations/excepthook.py +19 -13
- sentry_sdk/integrations/executing.py +5 -6
- sentry_sdk/integrations/falcon.py +128 -65
- sentry_sdk/integrations/fastapi.py +141 -0
- sentry_sdk/integrations/flask.py +114 -75
- sentry_sdk/integrations/gcp.py +67 -36
- sentry_sdk/integrations/gnu_backtrace.py +14 -22
- sentry_sdk/integrations/google_genai/__init__.py +301 -0
- sentry_sdk/integrations/google_genai/consts.py +16 -0
- sentry_sdk/integrations/google_genai/streaming.py +155 -0
- sentry_sdk/integrations/google_genai/utils.py +576 -0
- sentry_sdk/integrations/gql.py +162 -0
- sentry_sdk/integrations/graphene.py +151 -0
- sentry_sdk/integrations/grpc/__init__.py +168 -0
- sentry_sdk/integrations/grpc/aio/__init__.py +7 -0
- sentry_sdk/integrations/grpc/aio/client.py +95 -0
- sentry_sdk/integrations/grpc/aio/server.py +100 -0
- sentry_sdk/integrations/grpc/client.py +91 -0
- sentry_sdk/integrations/grpc/consts.py +1 -0
- sentry_sdk/integrations/grpc/server.py +66 -0
- sentry_sdk/integrations/httpx.py +178 -0
- sentry_sdk/integrations/huey.py +174 -0
- sentry_sdk/integrations/huggingface_hub.py +378 -0
- sentry_sdk/integrations/langchain.py +1132 -0
- sentry_sdk/integrations/langgraph.py +337 -0
- sentry_sdk/integrations/launchdarkly.py +61 -0
- sentry_sdk/integrations/litellm.py +287 -0
- sentry_sdk/integrations/litestar.py +315 -0
- sentry_sdk/integrations/logging.py +261 -85
- sentry_sdk/integrations/loguru.py +213 -0
- sentry_sdk/integrations/mcp.py +566 -0
- sentry_sdk/integrations/modules.py +6 -33
- sentry_sdk/integrations/openai.py +725 -0
- sentry_sdk/integrations/openai_agents/__init__.py +61 -0
- sentry_sdk/integrations/openai_agents/consts.py +1 -0
- sentry_sdk/integrations/openai_agents/patches/__init__.py +5 -0
- sentry_sdk/integrations/openai_agents/patches/agent_run.py +140 -0
- sentry_sdk/integrations/openai_agents/patches/error_tracing.py +77 -0
- sentry_sdk/integrations/openai_agents/patches/models.py +50 -0
- sentry_sdk/integrations/openai_agents/patches/runner.py +45 -0
- sentry_sdk/integrations/openai_agents/patches/tools.py +77 -0
- sentry_sdk/integrations/openai_agents/spans/__init__.py +5 -0
- sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +21 -0
- sentry_sdk/integrations/openai_agents/spans/ai_client.py +42 -0
- sentry_sdk/integrations/openai_agents/spans/execute_tool.py +48 -0
- sentry_sdk/integrations/openai_agents/spans/handoff.py +19 -0
- sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +86 -0
- sentry_sdk/integrations/openai_agents/utils.py +199 -0
- sentry_sdk/integrations/openfeature.py +35 -0
- sentry_sdk/integrations/opentelemetry/__init__.py +7 -0
- sentry_sdk/integrations/opentelemetry/consts.py +5 -0
- sentry_sdk/integrations/opentelemetry/integration.py +58 -0
- sentry_sdk/integrations/opentelemetry/propagator.py +117 -0
- sentry_sdk/integrations/opentelemetry/span_processor.py +391 -0
- sentry_sdk/integrations/otlp.py +82 -0
- sentry_sdk/integrations/pure_eval.py +20 -11
- sentry_sdk/integrations/pydantic_ai/__init__.py +47 -0
- sentry_sdk/integrations/pydantic_ai/consts.py +1 -0
- sentry_sdk/integrations/pydantic_ai/patches/__init__.py +4 -0
- sentry_sdk/integrations/pydantic_ai/patches/agent_run.py +215 -0
- sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py +110 -0
- sentry_sdk/integrations/pydantic_ai/patches/model_request.py +40 -0
- sentry_sdk/integrations/pydantic_ai/patches/tools.py +98 -0
- sentry_sdk/integrations/pydantic_ai/spans/__init__.py +3 -0
- sentry_sdk/integrations/pydantic_ai/spans/ai_client.py +246 -0
- sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py +49 -0
- sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py +112 -0
- sentry_sdk/integrations/pydantic_ai/utils.py +223 -0
- sentry_sdk/integrations/pymongo.py +214 -0
- sentry_sdk/integrations/pyramid.py +71 -60
- sentry_sdk/integrations/quart.py +237 -0
- sentry_sdk/integrations/ray.py +165 -0
- sentry_sdk/integrations/redis/__init__.py +48 -0
- sentry_sdk/integrations/redis/_async_common.py +116 -0
- sentry_sdk/integrations/redis/_sync_common.py +119 -0
- sentry_sdk/integrations/redis/consts.py +19 -0
- sentry_sdk/integrations/redis/modules/__init__.py +0 -0
- sentry_sdk/integrations/redis/modules/caches.py +118 -0
- sentry_sdk/integrations/redis/modules/queries.py +65 -0
- sentry_sdk/integrations/redis/rb.py +32 -0
- sentry_sdk/integrations/redis/redis.py +69 -0
- sentry_sdk/integrations/redis/redis_cluster.py +107 -0
- sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +50 -0
- sentry_sdk/integrations/redis/utils.py +148 -0
- sentry_sdk/integrations/rq.py +62 -52
- sentry_sdk/integrations/rust_tracing.py +284 -0
- sentry_sdk/integrations/sanic.py +248 -114
- sentry_sdk/integrations/serverless.py +13 -22
- sentry_sdk/integrations/socket.py +96 -0
- sentry_sdk/integrations/spark/spark_driver.py +115 -62
- sentry_sdk/integrations/spark/spark_worker.py +42 -50
- sentry_sdk/integrations/sqlalchemy.py +82 -37
- sentry_sdk/integrations/starlette.py +737 -0
- sentry_sdk/integrations/starlite.py +292 -0
- sentry_sdk/integrations/statsig.py +37 -0
- sentry_sdk/integrations/stdlib.py +100 -58
- sentry_sdk/integrations/strawberry.py +394 -0
- sentry_sdk/integrations/sys_exit.py +70 -0
- sentry_sdk/integrations/threading.py +142 -38
- sentry_sdk/integrations/tornado.py +68 -53
- sentry_sdk/integrations/trytond.py +15 -20
- sentry_sdk/integrations/typer.py +60 -0
- sentry_sdk/integrations/unleash.py +33 -0
- sentry_sdk/integrations/unraisablehook.py +53 -0
- sentry_sdk/integrations/wsgi.py +126 -125
- sentry_sdk/logger.py +96 -0
- sentry_sdk/metrics.py +81 -0
- sentry_sdk/monitor.py +120 -0
- sentry_sdk/profiler/__init__.py +49 -0
- sentry_sdk/profiler/continuous_profiler.py +730 -0
- sentry_sdk/profiler/transaction_profiler.py +839 -0
- sentry_sdk/profiler/utils.py +195 -0
- sentry_sdk/scope.py +1542 -112
- sentry_sdk/scrubber.py +177 -0
- sentry_sdk/serializer.py +152 -210
- sentry_sdk/session.py +177 -0
- sentry_sdk/sessions.py +202 -179
- sentry_sdk/spotlight.py +242 -0
- sentry_sdk/tracing.py +1202 -294
- sentry_sdk/tracing_utils.py +1236 -0
- sentry_sdk/transport.py +693 -189
- sentry_sdk/types.py +52 -0
- sentry_sdk/utils.py +1395 -228
- sentry_sdk/worker.py +30 -17
- sentry_sdk-2.46.0.dist-info/METADATA +268 -0
- sentry_sdk-2.46.0.dist-info/RECORD +189 -0
- {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/WHEEL +1 -1
- sentry_sdk-2.46.0.dist-info/entry_points.txt +2 -0
- sentry_sdk-2.46.0.dist-info/licenses/LICENSE +21 -0
- sentry_sdk/_functools.py +0 -66
- sentry_sdk/integrations/celery.py +0 -275
- sentry_sdk/integrations/redis.py +0 -103
- sentry_sdk-0.18.0.dist-info/LICENSE +0 -9
- sentry_sdk-0.18.0.dist-info/METADATA +0 -66
- sentry_sdk-0.18.0.dist-info/RECORD +0 -65
- {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/top_level.txt +0 -0
|
@@ -1,29 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
from __future__ import absolute_import
|
|
3
|
-
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
4
2
|
from threading import Lock
|
|
5
3
|
|
|
6
|
-
from sentry_sdk._compat import iteritems
|
|
7
4
|
from sentry_sdk.utils import logger
|
|
8
5
|
|
|
9
|
-
from
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
10
7
|
|
|
11
|
-
if
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from collections.abc import Sequence
|
|
12
10
|
from typing import Callable
|
|
13
11
|
from typing import Dict
|
|
14
12
|
from typing import Iterator
|
|
15
13
|
from typing import List
|
|
14
|
+
from typing import Optional
|
|
16
15
|
from typing import Set
|
|
17
|
-
from typing import Tuple
|
|
18
16
|
from typing import Type
|
|
17
|
+
from typing import Union
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
_DEFAULT_FAILED_REQUEST_STATUS_CODES = frozenset(range(500, 600))
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
_installer_lock = Lock()
|
|
25
|
+
|
|
26
|
+
# Set of all integration identifiers we have attempted to install
|
|
27
|
+
_processed_integrations = set() # type: Set[str]
|
|
28
|
+
|
|
29
|
+
# Set of all integration identifiers we have actually installed
|
|
22
30
|
_installed_integrations = set() # type: Set[str]
|
|
23
31
|
|
|
24
32
|
|
|
25
|
-
def _generate_default_integrations_iterator(
|
|
26
|
-
# type:
|
|
33
|
+
def _generate_default_integrations_iterator(
|
|
34
|
+
integrations, # type: List[str]
|
|
35
|
+
auto_enabling_integrations, # type: List[str]
|
|
36
|
+
):
|
|
37
|
+
# type: (...) -> Callable[[bool], Iterator[Type[Integration]]]
|
|
27
38
|
|
|
28
39
|
def iter_default_integrations(with_auto_enabling_integrations):
|
|
29
40
|
# type: (bool) -> Iterator[Type[Integration]]
|
|
@@ -51,45 +62,148 @@ def _generate_default_integrations_iterator(integrations, auto_enabling_integrat
|
|
|
51
62
|
return iter_default_integrations
|
|
52
63
|
|
|
53
64
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"sentry_sdk.integrations.
|
|
65
|
+
_DEFAULT_INTEGRATIONS = [
|
|
66
|
+
# stdlib/base runtime integrations
|
|
67
|
+
"sentry_sdk.integrations.argv.ArgvIntegration",
|
|
68
|
+
"sentry_sdk.integrations.atexit.AtexitIntegration",
|
|
69
|
+
"sentry_sdk.integrations.dedupe.DedupeIntegration",
|
|
70
|
+
"sentry_sdk.integrations.excepthook.ExcepthookIntegration",
|
|
71
|
+
"sentry_sdk.integrations.logging.LoggingIntegration",
|
|
72
|
+
"sentry_sdk.integrations.modules.ModulesIntegration",
|
|
73
|
+
"sentry_sdk.integrations.stdlib.StdlibIntegration",
|
|
74
|
+
"sentry_sdk.integrations.threading.ThreadingIntegration",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
_AUTO_ENABLING_INTEGRATIONS = [
|
|
78
|
+
"sentry_sdk.integrations.aiohttp.AioHttpIntegration",
|
|
79
|
+
"sentry_sdk.integrations.anthropic.AnthropicIntegration",
|
|
80
|
+
"sentry_sdk.integrations.ariadne.AriadneIntegration",
|
|
81
|
+
"sentry_sdk.integrations.arq.ArqIntegration",
|
|
82
|
+
"sentry_sdk.integrations.asyncpg.AsyncPGIntegration",
|
|
83
|
+
"sentry_sdk.integrations.boto3.Boto3Integration",
|
|
57
84
|
"sentry_sdk.integrations.bottle.BottleIntegration",
|
|
58
|
-
"sentry_sdk.integrations.falcon.FalconIntegration",
|
|
59
|
-
"sentry_sdk.integrations.sanic.SanicIntegration",
|
|
60
85
|
"sentry_sdk.integrations.celery.CeleryIntegration",
|
|
86
|
+
"sentry_sdk.integrations.chalice.ChaliceIntegration",
|
|
87
|
+
"sentry_sdk.integrations.clickhouse_driver.ClickhouseDriverIntegration",
|
|
88
|
+
"sentry_sdk.integrations.cohere.CohereIntegration",
|
|
89
|
+
"sentry_sdk.integrations.django.DjangoIntegration",
|
|
90
|
+
"sentry_sdk.integrations.falcon.FalconIntegration",
|
|
91
|
+
"sentry_sdk.integrations.fastapi.FastApiIntegration",
|
|
92
|
+
"sentry_sdk.integrations.flask.FlaskIntegration",
|
|
93
|
+
"sentry_sdk.integrations.gql.GQLIntegration",
|
|
94
|
+
"sentry_sdk.integrations.graphene.GrapheneIntegration",
|
|
95
|
+
"sentry_sdk.integrations.httpx.HttpxIntegration",
|
|
96
|
+
"sentry_sdk.integrations.huey.HueyIntegration",
|
|
97
|
+
"sentry_sdk.integrations.huggingface_hub.HuggingfaceHubIntegration",
|
|
98
|
+
"sentry_sdk.integrations.langchain.LangchainIntegration",
|
|
99
|
+
"sentry_sdk.integrations.langgraph.LanggraphIntegration",
|
|
100
|
+
"sentry_sdk.integrations.litestar.LitestarIntegration",
|
|
101
|
+
"sentry_sdk.integrations.loguru.LoguruIntegration",
|
|
102
|
+
"sentry_sdk.integrations.mcp.MCPIntegration",
|
|
103
|
+
"sentry_sdk.integrations.openai.OpenAIIntegration",
|
|
104
|
+
"sentry_sdk.integrations.openai_agents.OpenAIAgentsIntegration",
|
|
105
|
+
"sentry_sdk.integrations.pydantic_ai.PydanticAIIntegration",
|
|
106
|
+
"sentry_sdk.integrations.pymongo.PyMongoIntegration",
|
|
107
|
+
"sentry_sdk.integrations.pyramid.PyramidIntegration",
|
|
108
|
+
"sentry_sdk.integrations.quart.QuartIntegration",
|
|
109
|
+
"sentry_sdk.integrations.redis.RedisIntegration",
|
|
61
110
|
"sentry_sdk.integrations.rq.RqIntegration",
|
|
62
|
-
"sentry_sdk.integrations.
|
|
63
|
-
"sentry_sdk.integrations.tornado.TornadoIntegration",
|
|
111
|
+
"sentry_sdk.integrations.sanic.SanicIntegration",
|
|
64
112
|
"sentry_sdk.integrations.sqlalchemy.SqlalchemyIntegration",
|
|
65
|
-
|
|
66
|
-
|
|
113
|
+
"sentry_sdk.integrations.starlette.StarletteIntegration",
|
|
114
|
+
"sentry_sdk.integrations.starlite.StarliteIntegration",
|
|
115
|
+
"sentry_sdk.integrations.strawberry.StrawberryIntegration",
|
|
116
|
+
"sentry_sdk.integrations.tornado.TornadoIntegration",
|
|
117
|
+
]
|
|
67
118
|
|
|
68
119
|
iter_default_integrations = _generate_default_integrations_iterator(
|
|
69
|
-
integrations=
|
|
70
|
-
# stdlib/base runtime integrations
|
|
71
|
-
"sentry_sdk.integrations.logging.LoggingIntegration",
|
|
72
|
-
"sentry_sdk.integrations.stdlib.StdlibIntegration",
|
|
73
|
-
"sentry_sdk.integrations.excepthook.ExcepthookIntegration",
|
|
74
|
-
"sentry_sdk.integrations.dedupe.DedupeIntegration",
|
|
75
|
-
"sentry_sdk.integrations.atexit.AtexitIntegration",
|
|
76
|
-
"sentry_sdk.integrations.modules.ModulesIntegration",
|
|
77
|
-
"sentry_sdk.integrations.argv.ArgvIntegration",
|
|
78
|
-
"sentry_sdk.integrations.threading.ThreadingIntegration",
|
|
79
|
-
),
|
|
120
|
+
integrations=_DEFAULT_INTEGRATIONS,
|
|
80
121
|
auto_enabling_integrations=_AUTO_ENABLING_INTEGRATIONS,
|
|
81
122
|
)
|
|
82
123
|
|
|
83
124
|
del _generate_default_integrations_iterator
|
|
84
125
|
|
|
85
126
|
|
|
127
|
+
_MIN_VERSIONS = {
|
|
128
|
+
"aiohttp": (3, 4),
|
|
129
|
+
"anthropic": (0, 16),
|
|
130
|
+
"ariadne": (0, 20),
|
|
131
|
+
"arq": (0, 23),
|
|
132
|
+
"asyncpg": (0, 23),
|
|
133
|
+
"beam": (2, 12),
|
|
134
|
+
"boto3": (1, 12), # botocore
|
|
135
|
+
"bottle": (0, 12),
|
|
136
|
+
"celery": (4, 4, 7),
|
|
137
|
+
"chalice": (1, 16, 0),
|
|
138
|
+
"clickhouse_driver": (0, 2, 0),
|
|
139
|
+
"cohere": (5, 4, 0),
|
|
140
|
+
"django": (1, 8),
|
|
141
|
+
"dramatiq": (1, 9),
|
|
142
|
+
"falcon": (1, 4),
|
|
143
|
+
"fastapi": (0, 79, 0),
|
|
144
|
+
"flask": (1, 1, 4),
|
|
145
|
+
"gql": (3, 4, 1),
|
|
146
|
+
"graphene": (3, 3),
|
|
147
|
+
"google_genai": (1, 29, 0), # google-genai
|
|
148
|
+
"grpc": (1, 32, 0), # grpcio
|
|
149
|
+
"httpx": (0, 16, 0),
|
|
150
|
+
"huggingface_hub": (0, 24, 7),
|
|
151
|
+
"langchain": (0, 1, 0),
|
|
152
|
+
"langgraph": (0, 6, 6),
|
|
153
|
+
"launchdarkly": (9, 8, 0),
|
|
154
|
+
"litellm": (1, 77, 5),
|
|
155
|
+
"loguru": (0, 7, 0),
|
|
156
|
+
"mcp": (1, 15, 0),
|
|
157
|
+
"openai": (1, 0, 0),
|
|
158
|
+
"openai_agents": (0, 0, 19),
|
|
159
|
+
"openfeature": (0, 7, 1),
|
|
160
|
+
"pydantic_ai": (1, 0, 0),
|
|
161
|
+
"quart": (0, 16, 0),
|
|
162
|
+
"ray": (2, 7, 0),
|
|
163
|
+
"requests": (2, 0, 0),
|
|
164
|
+
"rq": (0, 6),
|
|
165
|
+
"sanic": (0, 8),
|
|
166
|
+
"sqlalchemy": (1, 2),
|
|
167
|
+
"starlette": (0, 16),
|
|
168
|
+
"starlite": (1, 48),
|
|
169
|
+
"statsig": (0, 55, 3),
|
|
170
|
+
"strawberry": (0, 209, 5),
|
|
171
|
+
"tornado": (6, 0),
|
|
172
|
+
"typer": (0, 15),
|
|
173
|
+
"unleash": (6, 0, 1),
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
_INTEGRATION_DEACTIVATES = {
|
|
178
|
+
"langchain": {"openai", "anthropic"},
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
86
182
|
def setup_integrations(
|
|
87
|
-
integrations,
|
|
183
|
+
integrations, # type: Sequence[Integration]
|
|
184
|
+
with_defaults=True, # type: bool
|
|
185
|
+
with_auto_enabling_integrations=False, # type: bool
|
|
186
|
+
disabled_integrations=None, # type: Optional[Sequence[Union[type[Integration], Integration]]]
|
|
187
|
+
options=None, # type: Optional[Dict[str, Any]]
|
|
88
188
|
):
|
|
89
|
-
# type: (
|
|
90
|
-
"""
|
|
91
|
-
|
|
189
|
+
# type: (...) -> Dict[str, Integration]
|
|
190
|
+
"""
|
|
191
|
+
Given a list of integration instances, this installs them all.
|
|
192
|
+
|
|
193
|
+
When `with_defaults` is set to `True` all default integrations are added
|
|
92
194
|
unless they were already provided before.
|
|
195
|
+
|
|
196
|
+
`disabled_integrations` takes precedence over `with_defaults` and
|
|
197
|
+
`with_auto_enabling_integrations`.
|
|
198
|
+
|
|
199
|
+
Some integrations are designed to automatically deactivate other integrations
|
|
200
|
+
in order to avoid conflicts and prevent duplicate telemetry from being collected.
|
|
201
|
+
For example, enabling the `langchain` integration will auto-deactivate both the
|
|
202
|
+
`openai` and `anthropic` integrations.
|
|
203
|
+
|
|
204
|
+
Users can override this behavior by:
|
|
205
|
+
- Explicitly providing an integration in the `integrations=[]` list, or
|
|
206
|
+
- Disabling the higher-level integration via the `disabled_integrations` option.
|
|
93
207
|
"""
|
|
94
208
|
integrations = dict(
|
|
95
209
|
(integration.identifier, integration) for integration in integrations or ()
|
|
@@ -97,6 +211,14 @@ def setup_integrations(
|
|
|
97
211
|
|
|
98
212
|
logger.debug("Setting up integrations (with default = %s)", with_defaults)
|
|
99
213
|
|
|
214
|
+
user_provided_integrations = set(integrations.keys())
|
|
215
|
+
|
|
216
|
+
# Integrations that will not be enabled
|
|
217
|
+
disabled_integrations = [
|
|
218
|
+
integration if isinstance(integration, type) else type(integration)
|
|
219
|
+
for integration in disabled_integrations or []
|
|
220
|
+
]
|
|
221
|
+
|
|
100
222
|
# Integrations that are not explicitly set up by the user.
|
|
101
223
|
used_as_default_integration = set()
|
|
102
224
|
|
|
@@ -109,33 +231,56 @@ def setup_integrations(
|
|
|
109
231
|
integrations[instance.identifier] = instance
|
|
110
232
|
used_as_default_integration.add(instance.identifier)
|
|
111
233
|
|
|
112
|
-
|
|
234
|
+
disabled_integration_identifiers = {
|
|
235
|
+
integration.identifier for integration in disabled_integrations
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
for integration, targets_to_deactivate in _INTEGRATION_DEACTIVATES.items():
|
|
239
|
+
if (
|
|
240
|
+
integration in integrations
|
|
241
|
+
and integration not in disabled_integration_identifiers
|
|
242
|
+
):
|
|
243
|
+
for target in targets_to_deactivate:
|
|
244
|
+
if target not in user_provided_integrations:
|
|
245
|
+
for cls in iter_default_integrations(True):
|
|
246
|
+
if cls.identifier == target:
|
|
247
|
+
if cls not in disabled_integrations:
|
|
248
|
+
disabled_integrations.append(cls)
|
|
249
|
+
logger.debug(
|
|
250
|
+
"Auto-deactivating %s integration because %s integration is active",
|
|
251
|
+
target,
|
|
252
|
+
integration,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
for identifier, integration in integrations.items():
|
|
113
256
|
with _installer_lock:
|
|
114
|
-
if identifier not in
|
|
115
|
-
|
|
116
|
-
"
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
257
|
+
if identifier not in _processed_integrations:
|
|
258
|
+
if type(integration) in disabled_integrations:
|
|
259
|
+
logger.debug("Ignoring integration %s", identifier)
|
|
260
|
+
else:
|
|
261
|
+
logger.debug(
|
|
262
|
+
"Setting up previously not enabled integration %s", identifier
|
|
263
|
+
)
|
|
264
|
+
try:
|
|
265
|
+
type(integration).setup_once()
|
|
266
|
+
integration.setup_once_with_options(options)
|
|
267
|
+
except DidNotEnable as e:
|
|
268
|
+
if identifier not in used_as_default_integration:
|
|
269
|
+
raise
|
|
270
|
+
|
|
271
|
+
logger.debug(
|
|
272
|
+
"Did not enable default integration %s: %s", identifier, e
|
|
126
273
|
)
|
|
127
|
-
integration.install()
|
|
128
274
|
else:
|
|
129
|
-
|
|
130
|
-
except DidNotEnable as e:
|
|
131
|
-
if identifier not in used_as_default_integration:
|
|
132
|
-
raise
|
|
275
|
+
_installed_integrations.add(identifier)
|
|
133
276
|
|
|
134
|
-
|
|
135
|
-
"Did not enable default integration %s: %s", identifier, e
|
|
136
|
-
)
|
|
277
|
+
_processed_integrations.add(identifier)
|
|
137
278
|
|
|
138
|
-
|
|
279
|
+
integrations = {
|
|
280
|
+
identifier: integration
|
|
281
|
+
for identifier, integration in integrations.items()
|
|
282
|
+
if identifier in _installed_integrations
|
|
283
|
+
}
|
|
139
284
|
|
|
140
285
|
for identifier in integrations:
|
|
141
286
|
logger.debug("Enabling integration %s", identifier)
|
|
@@ -143,7 +288,24 @@ def setup_integrations(
|
|
|
143
288
|
return integrations
|
|
144
289
|
|
|
145
290
|
|
|
146
|
-
|
|
291
|
+
def _check_minimum_version(integration, version, package=None):
|
|
292
|
+
# type: (type[Integration], Optional[tuple[int, ...]], Optional[str]) -> None
|
|
293
|
+
package = package or integration.identifier
|
|
294
|
+
|
|
295
|
+
if version is None:
|
|
296
|
+
raise DidNotEnable(f"Unparsable {package} version.")
|
|
297
|
+
|
|
298
|
+
min_version = _MIN_VERSIONS.get(integration.identifier)
|
|
299
|
+
if min_version is None:
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
if version < min_version:
|
|
303
|
+
raise DidNotEnable(
|
|
304
|
+
f"Integration only supports {package} {'.'.join(map(str, min_version))} or newer."
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class DidNotEnable(Exception): # noqa: N818
|
|
147
309
|
"""
|
|
148
310
|
The integration could not be enabled due to a trivial user error like
|
|
149
311
|
`flask` not being installed for the `FlaskIntegration`.
|
|
@@ -153,7 +315,7 @@ class DidNotEnable(Exception):
|
|
|
153
315
|
"""
|
|
154
316
|
|
|
155
317
|
|
|
156
|
-
class Integration(
|
|
318
|
+
class Integration(ABC):
|
|
157
319
|
"""Baseclass for all integrations.
|
|
158
320
|
|
|
159
321
|
To accept options for an integration, implement your own constructor that
|
|
@@ -167,6 +329,7 @@ class Integration(object):
|
|
|
167
329
|
"""String unique ID of integration type"""
|
|
168
330
|
|
|
169
331
|
@staticmethod
|
|
332
|
+
@abstractmethod
|
|
170
333
|
def setup_once():
|
|
171
334
|
# type: () -> None
|
|
172
335
|
"""
|
|
@@ -179,4 +342,11 @@ class Integration(object):
|
|
|
179
342
|
Inside those hooks `Integration.current` can be used to access the
|
|
180
343
|
instance again.
|
|
181
344
|
"""
|
|
182
|
-
|
|
345
|
+
pass
|
|
346
|
+
|
|
347
|
+
def setup_once_with_options(self, options=None):
|
|
348
|
+
# type: (Optional[Dict[str, Any]]) -> None
|
|
349
|
+
"""
|
|
350
|
+
Called after setup_once in rare cases on the instance and with options since we don't have those available above.
|
|
351
|
+
"""
|
|
352
|
+
pass
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import urllib
|
|
2
|
+
|
|
3
|
+
from sentry_sdk.scope import should_send_default_pii
|
|
4
|
+
from sentry_sdk.integrations._wsgi_common import _filter_headers
|
|
5
|
+
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from typing import Any
|
|
10
|
+
from typing import Dict
|
|
11
|
+
from typing import Optional
|
|
12
|
+
from typing import Union
|
|
13
|
+
from typing_extensions import Literal
|
|
14
|
+
|
|
15
|
+
from sentry_sdk.utils import AnnotatedValue
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _get_headers(asgi_scope):
|
|
19
|
+
# type: (Any) -> Dict[str, str]
|
|
20
|
+
"""
|
|
21
|
+
Extract headers from the ASGI scope, in the format that the Sentry protocol expects.
|
|
22
|
+
"""
|
|
23
|
+
headers = {} # type: Dict[str, str]
|
|
24
|
+
for raw_key, raw_value in asgi_scope["headers"]:
|
|
25
|
+
key = raw_key.decode("latin-1")
|
|
26
|
+
value = raw_value.decode("latin-1")
|
|
27
|
+
if key in headers:
|
|
28
|
+
headers[key] = headers[key] + ", " + value
|
|
29
|
+
else:
|
|
30
|
+
headers[key] = value
|
|
31
|
+
|
|
32
|
+
return headers
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _get_url(asgi_scope, default_scheme, host):
|
|
36
|
+
# type: (Dict[str, Any], Literal["ws", "http"], Optional[Union[AnnotatedValue, str]]) -> str
|
|
37
|
+
"""
|
|
38
|
+
Extract URL from the ASGI scope, without also including the querystring.
|
|
39
|
+
"""
|
|
40
|
+
scheme = asgi_scope.get("scheme", default_scheme)
|
|
41
|
+
|
|
42
|
+
server = asgi_scope.get("server", None)
|
|
43
|
+
path = asgi_scope.get("root_path", "") + asgi_scope.get("path", "")
|
|
44
|
+
|
|
45
|
+
if host:
|
|
46
|
+
return "%s://%s%s" % (scheme, host, path)
|
|
47
|
+
|
|
48
|
+
if server is not None:
|
|
49
|
+
host, port = server
|
|
50
|
+
default_port = {"http": 80, "https": 443, "ws": 80, "wss": 443}.get(scheme)
|
|
51
|
+
if port != default_port:
|
|
52
|
+
return "%s://%s:%s%s" % (scheme, host, port, path)
|
|
53
|
+
return "%s://%s%s" % (scheme, host, path)
|
|
54
|
+
return path
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _get_query(asgi_scope):
|
|
58
|
+
# type: (Any) -> Any
|
|
59
|
+
"""
|
|
60
|
+
Extract querystring from the ASGI scope, in the format that the Sentry protocol expects.
|
|
61
|
+
"""
|
|
62
|
+
qs = asgi_scope.get("query_string")
|
|
63
|
+
if not qs:
|
|
64
|
+
return None
|
|
65
|
+
return urllib.parse.unquote(qs.decode("latin-1"))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _get_ip(asgi_scope):
|
|
69
|
+
# type: (Any) -> str
|
|
70
|
+
"""
|
|
71
|
+
Extract IP Address from the ASGI scope based on request headers with fallback to scope client.
|
|
72
|
+
"""
|
|
73
|
+
headers = _get_headers(asgi_scope)
|
|
74
|
+
try:
|
|
75
|
+
return headers["x-forwarded-for"].split(",")[0].strip()
|
|
76
|
+
except (KeyError, IndexError):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
return headers["x-real-ip"]
|
|
81
|
+
except KeyError:
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
return asgi_scope.get("client")[0]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _get_request_data(asgi_scope):
|
|
88
|
+
# type: (Any) -> Dict[str, Any]
|
|
89
|
+
"""
|
|
90
|
+
Returns data related to the HTTP request from the ASGI scope.
|
|
91
|
+
"""
|
|
92
|
+
request_data = {} # type: Dict[str, Any]
|
|
93
|
+
ty = asgi_scope["type"]
|
|
94
|
+
if ty in ("http", "websocket"):
|
|
95
|
+
request_data["method"] = asgi_scope.get("method")
|
|
96
|
+
|
|
97
|
+
request_data["headers"] = headers = _filter_headers(_get_headers(asgi_scope))
|
|
98
|
+
request_data["query_string"] = _get_query(asgi_scope)
|
|
99
|
+
|
|
100
|
+
request_data["url"] = _get_url(
|
|
101
|
+
asgi_scope, "http" if ty == "http" else "ws", headers.get("host")
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
client = asgi_scope.get("client")
|
|
105
|
+
if client and should_send_default_pii():
|
|
106
|
+
request_data["env"] = {"REMOTE_ADDR": _get_ip(asgi_scope)}
|
|
107
|
+
|
|
108
|
+
return request_data
|