sentry-sdk 0.7.5__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 -30
- sentry_sdk/_compat.py +74 -61
- 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 +289 -0
- sentry_sdk/_types.py +338 -0
- 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 +496 -80
- sentry_sdk/attachments.py +75 -0
- sentry_sdk/client.py +1023 -103
- sentry_sdk/consts.py +1438 -66
- 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 +15 -14
- sentry_sdk/envelope.py +369 -0
- sentry_sdk/feature_flags.py +71 -0
- sentry_sdk/hub.py +611 -280
- sentry_sdk/integrations/__init__.py +276 -49
- sentry_sdk/integrations/_asgi_common.py +108 -0
- sentry_sdk/integrations/_wsgi_common.py +180 -44
- sentry_sdk/integrations/aiohttp.py +291 -42
- sentry_sdk/integrations/anthropic.py +439 -0
- sentry_sdk/integrations/argv.py +9 -8
- sentry_sdk/integrations/ariadne.py +161 -0
- sentry_sdk/integrations/arq.py +247 -0
- sentry_sdk/integrations/asgi.py +341 -0
- sentry_sdk/integrations/asyncio.py +144 -0
- sentry_sdk/integrations/asyncpg.py +208 -0
- sentry_sdk/integrations/atexit.py +17 -10
- sentry_sdk/integrations/aws_lambda.py +377 -62
- sentry_sdk/integrations/beam.py +176 -0
- sentry_sdk/integrations/boto3.py +137 -0
- sentry_sdk/integrations/bottle.py +221 -0
- 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 +134 -0
- 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 +48 -14
- sentry_sdk/integrations/django/__init__.py +584 -191
- sentry_sdk/integrations/django/asgi.py +245 -0
- sentry_sdk/integrations/django/caching.py +204 -0
- sentry_sdk/integrations/django/middleware.py +187 -0
- sentry_sdk/integrations/django/signals_handlers.py +91 -0
- sentry_sdk/integrations/django/templates.py +79 -5
- sentry_sdk/integrations/django/transactions.py +49 -22
- sentry_sdk/integrations/django/views.py +96 -0
- sentry_sdk/integrations/dramatiq.py +226 -0
- sentry_sdk/integrations/excepthook.py +50 -13
- sentry_sdk/integrations/executing.py +67 -0
- sentry_sdk/integrations/falcon.py +272 -0
- sentry_sdk/integrations/fastapi.py +141 -0
- sentry_sdk/integrations/flask.py +142 -88
- sentry_sdk/integrations/gcp.py +239 -0
- sentry_sdk/integrations/gnu_backtrace.py +99 -0
- 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 +307 -96
- sentry_sdk/integrations/loguru.py +213 -0
- sentry_sdk/integrations/mcp.py +566 -0
- sentry_sdk/integrations/modules.py +14 -31
- 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 +141 -0
- 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 +112 -68
- 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 +95 -37
- sentry_sdk/integrations/rust_tracing.py +284 -0
- sentry_sdk/integrations/sanic.py +294 -123
- sentry_sdk/integrations/serverless.py +48 -19
- sentry_sdk/integrations/socket.py +96 -0
- sentry_sdk/integrations/spark/__init__.py +4 -0
- sentry_sdk/integrations/spark/spark_driver.py +316 -0
- sentry_sdk/integrations/spark/spark_worker.py +116 -0
- sentry_sdk/integrations/sqlalchemy.py +142 -0
- 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 +235 -29
- sentry_sdk/integrations/strawberry.py +394 -0
- sentry_sdk/integrations/sys_exit.py +70 -0
- sentry_sdk/integrations/threading.py +158 -28
- sentry_sdk/integrations/tornado.py +84 -52
- sentry_sdk/integrations/trytond.py +50 -0
- 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 +201 -119
- 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/py.typed +0 -0
- sentry_sdk/scope.py +1713 -85
- sentry_sdk/scrubber.py +177 -0
- sentry_sdk/serializer.py +405 -0
- sentry_sdk/session.py +177 -0
- sentry_sdk/sessions.py +275 -0
- sentry_sdk/spotlight.py +242 -0
- sentry_sdk/tracing.py +1486 -0
- sentry_sdk/tracing_utils.py +1236 -0
- sentry_sdk/transport.py +806 -134
- sentry_sdk/types.py +52 -0
- sentry_sdk/utils.py +1625 -465
- sentry_sdk/worker.py +54 -25
- sentry_sdk-2.46.0.dist-info/METADATA +268 -0
- sentry_sdk-2.46.0.dist-info/RECORD +189 -0
- {sentry_sdk-0.7.5.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/integrations/celery.py +0 -119
- sentry_sdk-0.7.5.dist-info/LICENSE +0 -9
- sentry_sdk-0.7.5.dist-info/METADATA +0 -36
- sentry_sdk-0.7.5.dist-info/RECORD +0 -39
- {sentry_sdk-0.7.5.dist-info → sentry_sdk-2.46.0.dist-info}/top_level.txt +0 -0
|
@@ -1,63 +1,209 @@
|
|
|
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
|
-
|
|
10
|
-
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
from typing import Callable
|
|
11
11
|
from typing import Dict
|
|
12
|
+
from typing import Iterator
|
|
12
13
|
from typing import List
|
|
14
|
+
from typing import Optional
|
|
13
15
|
from typing import Set
|
|
14
16
|
from typing import Type
|
|
15
|
-
from typing import
|
|
17
|
+
from typing import Union
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
_DEFAULT_FAILED_REQUEST_STATUS_CODES = frozenset(range(500, 600))
|
|
16
22
|
|
|
17
23
|
|
|
18
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
|
|
19
30
|
_installed_integrations = set() # type: Set[str]
|
|
20
31
|
|
|
21
32
|
|
|
22
|
-
def _generate_default_integrations_iterator(
|
|
23
|
-
# type:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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]]]
|
|
38
|
+
|
|
39
|
+
def iter_default_integrations(with_auto_enabling_integrations):
|
|
40
|
+
# type: (bool) -> Iterator[Type[Integration]]
|
|
41
|
+
"""Returns an iterator of the default integration classes:"""
|
|
28
42
|
from importlib import import_module
|
|
29
43
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
if with_auto_enabling_integrations:
|
|
45
|
+
all_import_strings = integrations + auto_enabling_integrations
|
|
46
|
+
else:
|
|
47
|
+
all_import_strings = integrations
|
|
33
48
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
for import_string in all_import_strings:
|
|
50
|
+
try:
|
|
51
|
+
module, cls = import_string.rsplit(".", 1)
|
|
52
|
+
yield getattr(import_module(module), cls)
|
|
53
|
+
except (DidNotEnable, SyntaxError) as e:
|
|
54
|
+
logger.debug(
|
|
55
|
+
"Did not import default integration %s: %s", import_string, e
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if isinstance(iter_default_integrations.__doc__, str):
|
|
59
|
+
for import_string in integrations:
|
|
60
|
+
iter_default_integrations.__doc__ += "\n- `{}`".format(import_string)
|
|
38
61
|
|
|
39
62
|
return iter_default_integrations
|
|
40
63
|
|
|
41
64
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"sentry_sdk.integrations.
|
|
45
|
-
"sentry_sdk.integrations.excepthook.ExcepthookIntegration",
|
|
46
|
-
"sentry_sdk.integrations.dedupe.DedupeIntegration",
|
|
65
|
+
_DEFAULT_INTEGRATIONS = [
|
|
66
|
+
# stdlib/base runtime integrations
|
|
67
|
+
"sentry_sdk.integrations.argv.ArgvIntegration",
|
|
47
68
|
"sentry_sdk.integrations.atexit.AtexitIntegration",
|
|
69
|
+
"sentry_sdk.integrations.dedupe.DedupeIntegration",
|
|
70
|
+
"sentry_sdk.integrations.excepthook.ExcepthookIntegration",
|
|
71
|
+
"sentry_sdk.integrations.logging.LoggingIntegration",
|
|
48
72
|
"sentry_sdk.integrations.modules.ModulesIntegration",
|
|
49
|
-
"sentry_sdk.integrations.
|
|
73
|
+
"sentry_sdk.integrations.stdlib.StdlibIntegration",
|
|
50
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",
|
|
84
|
+
"sentry_sdk.integrations.bottle.BottleIntegration",
|
|
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",
|
|
110
|
+
"sentry_sdk.integrations.rq.RqIntegration",
|
|
111
|
+
"sentry_sdk.integrations.sanic.SanicIntegration",
|
|
112
|
+
"sentry_sdk.integrations.sqlalchemy.SqlalchemyIntegration",
|
|
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
|
+
]
|
|
118
|
+
|
|
119
|
+
iter_default_integrations = _generate_default_integrations_iterator(
|
|
120
|
+
integrations=_DEFAULT_INTEGRATIONS,
|
|
121
|
+
auto_enabling_integrations=_AUTO_ENABLING_INTEGRATIONS,
|
|
51
122
|
)
|
|
52
123
|
|
|
53
124
|
del _generate_default_integrations_iterator
|
|
54
125
|
|
|
55
126
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
""
|
|
59
|
-
|
|
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
|
+
|
|
182
|
+
def setup_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]]
|
|
188
|
+
):
|
|
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
|
|
60
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.
|
|
61
207
|
"""
|
|
62
208
|
integrations = dict(
|
|
63
209
|
(integration.identifier, integration) for integration in integrations or ()
|
|
@@ -65,31 +211,76 @@ def setup_integrations(integrations, with_defaults=True):
|
|
|
65
211
|
|
|
66
212
|
logger.debug("Setting up integrations (with default = %s)", with_defaults)
|
|
67
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
|
+
|
|
222
|
+
# Integrations that are not explicitly set up by the user.
|
|
223
|
+
used_as_default_integration = set()
|
|
224
|
+
|
|
68
225
|
if with_defaults:
|
|
69
|
-
for integration_cls in iter_default_integrations(
|
|
226
|
+
for integration_cls in iter_default_integrations(
|
|
227
|
+
with_auto_enabling_integrations
|
|
228
|
+
):
|
|
70
229
|
if integration_cls.identifier not in integrations:
|
|
71
230
|
instance = integration_cls()
|
|
72
231
|
integrations[instance.identifier] = instance
|
|
232
|
+
used_as_default_integration.add(instance.identifier)
|
|
233
|
+
|
|
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
|
+
)
|
|
73
254
|
|
|
74
|
-
for identifier, integration in
|
|
255
|
+
for identifier, integration in integrations.items():
|
|
75
256
|
with _installer_lock:
|
|
76
|
-
if identifier not in
|
|
77
|
-
|
|
78
|
-
"
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
88
273
|
)
|
|
89
|
-
integration.install()
|
|
90
274
|
else:
|
|
91
|
-
|
|
92
|
-
|
|
275
|
+
_installed_integrations.add(identifier)
|
|
276
|
+
|
|
277
|
+
_processed_integrations.add(identifier)
|
|
278
|
+
|
|
279
|
+
integrations = {
|
|
280
|
+
identifier: integration
|
|
281
|
+
for identifier, integration in integrations.items()
|
|
282
|
+
if identifier in _installed_integrations
|
|
283
|
+
}
|
|
93
284
|
|
|
94
285
|
for identifier in integrations:
|
|
95
286
|
logger.debug("Enabling integration %s", identifier)
|
|
@@ -97,7 +288,34 @@ def setup_integrations(integrations, with_defaults=True):
|
|
|
97
288
|
return integrations
|
|
98
289
|
|
|
99
290
|
|
|
100
|
-
|
|
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
|
|
309
|
+
"""
|
|
310
|
+
The integration could not be enabled due to a trivial user error like
|
|
311
|
+
`flask` not being installed for the `FlaskIntegration`.
|
|
312
|
+
|
|
313
|
+
This exception is silently swallowed for default integrations, but reraised
|
|
314
|
+
for explicitly enabled integrations.
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class Integration(ABC):
|
|
101
319
|
"""Baseclass for all integrations.
|
|
102
320
|
|
|
103
321
|
To accept options for an integration, implement your own constructor that
|
|
@@ -111,7 +329,9 @@ class Integration(object):
|
|
|
111
329
|
"""String unique ID of integration type"""
|
|
112
330
|
|
|
113
331
|
@staticmethod
|
|
332
|
+
@abstractmethod
|
|
114
333
|
def setup_once():
|
|
334
|
+
# type: () -> None
|
|
115
335
|
"""
|
|
116
336
|
Initialize the integration.
|
|
117
337
|
|
|
@@ -122,4 +342,11 @@ class Integration(object):
|
|
|
122
342
|
Inside those hooks `Integration.current` can be used to access the
|
|
123
343
|
instance again.
|
|
124
344
|
"""
|
|
125
|
-
|
|
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
|