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
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import sentry_sdk
|
|
2
|
+
from sentry_sdk.consts import OP
|
|
3
|
+
from sentry_sdk.integrations import DidNotEnable
|
|
4
|
+
from sentry_sdk.integrations.grpc.consts import SPAN_ORIGIN
|
|
5
|
+
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from typing import Any, Callable, Iterator, Iterable, Union
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import grpc
|
|
13
|
+
from grpc import ClientCallDetails, Call
|
|
14
|
+
from grpc._interceptor import _UnaryOutcome
|
|
15
|
+
from grpc.aio._interceptor import UnaryStreamCall
|
|
16
|
+
from google.protobuf.message import Message
|
|
17
|
+
except ImportError:
|
|
18
|
+
raise DidNotEnable("grpcio is not installed")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ClientInterceptor(
|
|
22
|
+
grpc.UnaryUnaryClientInterceptor, # type: ignore
|
|
23
|
+
grpc.UnaryStreamClientInterceptor, # type: ignore
|
|
24
|
+
):
|
|
25
|
+
_is_intercepted = False
|
|
26
|
+
|
|
27
|
+
def intercept_unary_unary(self, continuation, client_call_details, request):
|
|
28
|
+
# type: (ClientInterceptor, Callable[[ClientCallDetails, Message], _UnaryOutcome], ClientCallDetails, Message) -> _UnaryOutcome
|
|
29
|
+
method = client_call_details.method
|
|
30
|
+
|
|
31
|
+
with sentry_sdk.start_span(
|
|
32
|
+
op=OP.GRPC_CLIENT,
|
|
33
|
+
name="unary unary call to %s" % method,
|
|
34
|
+
origin=SPAN_ORIGIN,
|
|
35
|
+
) as span:
|
|
36
|
+
span.set_data("type", "unary unary")
|
|
37
|
+
span.set_data("method", method)
|
|
38
|
+
|
|
39
|
+
client_call_details = self._update_client_call_details_metadata_from_scope(
|
|
40
|
+
client_call_details
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
response = continuation(client_call_details, request)
|
|
44
|
+
span.set_data("code", response.code().name)
|
|
45
|
+
|
|
46
|
+
return response
|
|
47
|
+
|
|
48
|
+
def intercept_unary_stream(self, continuation, client_call_details, request):
|
|
49
|
+
# type: (ClientInterceptor, Callable[[ClientCallDetails, Message], Union[Iterable[Any], UnaryStreamCall]], ClientCallDetails, Message) -> Union[Iterator[Message], Call]
|
|
50
|
+
method = client_call_details.method
|
|
51
|
+
|
|
52
|
+
with sentry_sdk.start_span(
|
|
53
|
+
op=OP.GRPC_CLIENT,
|
|
54
|
+
name="unary stream call to %s" % method,
|
|
55
|
+
origin=SPAN_ORIGIN,
|
|
56
|
+
) as span:
|
|
57
|
+
span.set_data("type", "unary stream")
|
|
58
|
+
span.set_data("method", method)
|
|
59
|
+
|
|
60
|
+
client_call_details = self._update_client_call_details_metadata_from_scope(
|
|
61
|
+
client_call_details
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
response = continuation(client_call_details, request) # type: UnaryStreamCall
|
|
65
|
+
# Setting code on unary-stream leads to execution getting stuck
|
|
66
|
+
# span.set_data("code", response.code().name)
|
|
67
|
+
|
|
68
|
+
return response
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def _update_client_call_details_metadata_from_scope(client_call_details):
|
|
72
|
+
# type: (ClientCallDetails) -> ClientCallDetails
|
|
73
|
+
metadata = (
|
|
74
|
+
list(client_call_details.metadata) if client_call_details.metadata else []
|
|
75
|
+
)
|
|
76
|
+
for (
|
|
77
|
+
key,
|
|
78
|
+
value,
|
|
79
|
+
) in sentry_sdk.get_current_scope().iter_trace_propagation_headers():
|
|
80
|
+
metadata.append((key, value))
|
|
81
|
+
|
|
82
|
+
client_call_details = grpc._interceptor._ClientCallDetails(
|
|
83
|
+
method=client_call_details.method,
|
|
84
|
+
timeout=client_call_details.timeout,
|
|
85
|
+
metadata=metadata,
|
|
86
|
+
credentials=client_call_details.credentials,
|
|
87
|
+
wait_for_ready=client_call_details.wait_for_ready,
|
|
88
|
+
compression=client_call_details.compression,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return client_call_details
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SPAN_ORIGIN = "auto.grpc.grpc"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import sentry_sdk
|
|
2
|
+
from sentry_sdk.consts import OP
|
|
3
|
+
from sentry_sdk.integrations import DidNotEnable
|
|
4
|
+
from sentry_sdk.integrations.grpc.consts import SPAN_ORIGIN
|
|
5
|
+
from sentry_sdk.tracing import Transaction, TransactionSource
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from typing import Callable, Optional
|
|
11
|
+
from google.protobuf.message import Message
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import grpc
|
|
15
|
+
from grpc import ServicerContext, HandlerCallDetails, RpcMethodHandler
|
|
16
|
+
except ImportError:
|
|
17
|
+
raise DidNotEnable("grpcio is not installed")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ServerInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
21
|
+
def __init__(self, find_name=None):
|
|
22
|
+
# type: (ServerInterceptor, Optional[Callable[[ServicerContext], str]]) -> None
|
|
23
|
+
self._find_method_name = find_name or ServerInterceptor._find_name
|
|
24
|
+
|
|
25
|
+
super().__init__()
|
|
26
|
+
|
|
27
|
+
def intercept_service(self, continuation, handler_call_details):
|
|
28
|
+
# type: (ServerInterceptor, Callable[[HandlerCallDetails], RpcMethodHandler], HandlerCallDetails) -> RpcMethodHandler
|
|
29
|
+
handler = continuation(handler_call_details)
|
|
30
|
+
if not handler or not handler.unary_unary:
|
|
31
|
+
return handler
|
|
32
|
+
|
|
33
|
+
def behavior(request, context):
|
|
34
|
+
# type: (Message, ServicerContext) -> Message
|
|
35
|
+
with sentry_sdk.isolation_scope():
|
|
36
|
+
name = self._find_method_name(context)
|
|
37
|
+
|
|
38
|
+
if name:
|
|
39
|
+
metadata = dict(context.invocation_metadata())
|
|
40
|
+
|
|
41
|
+
transaction = Transaction.continue_from_headers(
|
|
42
|
+
metadata,
|
|
43
|
+
op=OP.GRPC_SERVER,
|
|
44
|
+
name=name,
|
|
45
|
+
source=TransactionSource.CUSTOM,
|
|
46
|
+
origin=SPAN_ORIGIN,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
with sentry_sdk.start_transaction(transaction=transaction):
|
|
50
|
+
try:
|
|
51
|
+
return handler.unary_unary(request, context)
|
|
52
|
+
except BaseException as e:
|
|
53
|
+
raise e
|
|
54
|
+
else:
|
|
55
|
+
return handler.unary_unary(request, context)
|
|
56
|
+
|
|
57
|
+
return grpc.unary_unary_rpc_method_handler(
|
|
58
|
+
behavior,
|
|
59
|
+
request_deserializer=handler.request_deserializer,
|
|
60
|
+
response_serializer=handler.response_serializer,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def _find_name(context):
|
|
65
|
+
# type: (ServicerContext) -> str
|
|
66
|
+
return context._rpc_event.call_details.method.decode()
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import sentry_sdk
|
|
2
|
+
from sentry_sdk import start_span
|
|
3
|
+
from sentry_sdk.consts import OP, SPANDATA
|
|
4
|
+
from sentry_sdk.integrations import Integration, DidNotEnable
|
|
5
|
+
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME
|
|
6
|
+
from sentry_sdk.tracing_utils import (
|
|
7
|
+
Baggage,
|
|
8
|
+
should_propagate_trace,
|
|
9
|
+
add_http_request_source,
|
|
10
|
+
)
|
|
11
|
+
from sentry_sdk.utils import (
|
|
12
|
+
SENSITIVE_DATA_SUBSTITUTE,
|
|
13
|
+
capture_internal_exceptions,
|
|
14
|
+
ensure_integration_enabled,
|
|
15
|
+
logger,
|
|
16
|
+
parse_url,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import MutableMapping
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
from httpx import AsyncClient, Client, Request, Response # type: ignore
|
|
28
|
+
except ImportError:
|
|
29
|
+
raise DidNotEnable("httpx is not installed")
|
|
30
|
+
|
|
31
|
+
__all__ = ["HttpxIntegration"]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class HttpxIntegration(Integration):
|
|
35
|
+
identifier = "httpx"
|
|
36
|
+
origin = f"auto.http.{identifier}"
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def setup_once():
|
|
40
|
+
# type: () -> None
|
|
41
|
+
"""
|
|
42
|
+
httpx has its own transport layer and can be customized when needed,
|
|
43
|
+
so patch Client.send and AsyncClient.send to support both synchronous and async interfaces.
|
|
44
|
+
"""
|
|
45
|
+
_install_httpx_client()
|
|
46
|
+
_install_httpx_async_client()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _install_httpx_client():
|
|
50
|
+
# type: () -> None
|
|
51
|
+
real_send = Client.send
|
|
52
|
+
|
|
53
|
+
@ensure_integration_enabled(HttpxIntegration, real_send)
|
|
54
|
+
def send(self, request, **kwargs):
|
|
55
|
+
# type: (Client, Request, **Any) -> Response
|
|
56
|
+
parsed_url = None
|
|
57
|
+
with capture_internal_exceptions():
|
|
58
|
+
parsed_url = parse_url(str(request.url), sanitize=False)
|
|
59
|
+
|
|
60
|
+
with start_span(
|
|
61
|
+
op=OP.HTTP_CLIENT,
|
|
62
|
+
name="%s %s"
|
|
63
|
+
% (
|
|
64
|
+
request.method,
|
|
65
|
+
parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE,
|
|
66
|
+
),
|
|
67
|
+
origin=HttpxIntegration.origin,
|
|
68
|
+
) as span:
|
|
69
|
+
span.set_data(SPANDATA.HTTP_METHOD, request.method)
|
|
70
|
+
if parsed_url is not None:
|
|
71
|
+
span.set_data("url", parsed_url.url)
|
|
72
|
+
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
|
|
73
|
+
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
|
|
74
|
+
|
|
75
|
+
if should_propagate_trace(sentry_sdk.get_client(), str(request.url)):
|
|
76
|
+
for (
|
|
77
|
+
key,
|
|
78
|
+
value,
|
|
79
|
+
) in sentry_sdk.get_current_scope().iter_trace_propagation_headers():
|
|
80
|
+
logger.debug(
|
|
81
|
+
"[Tracing] Adding `{key}` header {value} to outgoing request to {url}.".format(
|
|
82
|
+
key=key, value=value, url=request.url
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if key == BAGGAGE_HEADER_NAME:
|
|
87
|
+
_add_sentry_baggage_to_headers(request.headers, value)
|
|
88
|
+
else:
|
|
89
|
+
request.headers[key] = value
|
|
90
|
+
|
|
91
|
+
rv = real_send(self, request, **kwargs)
|
|
92
|
+
|
|
93
|
+
span.set_http_status(rv.status_code)
|
|
94
|
+
span.set_data("reason", rv.reason_phrase)
|
|
95
|
+
|
|
96
|
+
with capture_internal_exceptions():
|
|
97
|
+
add_http_request_source(span)
|
|
98
|
+
|
|
99
|
+
return rv
|
|
100
|
+
|
|
101
|
+
Client.send = send
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _install_httpx_async_client():
|
|
105
|
+
# type: () -> None
|
|
106
|
+
real_send = AsyncClient.send
|
|
107
|
+
|
|
108
|
+
async def send(self, request, **kwargs):
|
|
109
|
+
# type: (AsyncClient, Request, **Any) -> Response
|
|
110
|
+
if sentry_sdk.get_client().get_integration(HttpxIntegration) is None:
|
|
111
|
+
return await real_send(self, request, **kwargs)
|
|
112
|
+
|
|
113
|
+
parsed_url = None
|
|
114
|
+
with capture_internal_exceptions():
|
|
115
|
+
parsed_url = parse_url(str(request.url), sanitize=False)
|
|
116
|
+
|
|
117
|
+
with start_span(
|
|
118
|
+
op=OP.HTTP_CLIENT,
|
|
119
|
+
name="%s %s"
|
|
120
|
+
% (
|
|
121
|
+
request.method,
|
|
122
|
+
parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE,
|
|
123
|
+
),
|
|
124
|
+
origin=HttpxIntegration.origin,
|
|
125
|
+
) as span:
|
|
126
|
+
span.set_data(SPANDATA.HTTP_METHOD, request.method)
|
|
127
|
+
if parsed_url is not None:
|
|
128
|
+
span.set_data("url", parsed_url.url)
|
|
129
|
+
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
|
|
130
|
+
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
|
|
131
|
+
|
|
132
|
+
if should_propagate_trace(sentry_sdk.get_client(), str(request.url)):
|
|
133
|
+
for (
|
|
134
|
+
key,
|
|
135
|
+
value,
|
|
136
|
+
) in sentry_sdk.get_current_scope().iter_trace_propagation_headers():
|
|
137
|
+
logger.debug(
|
|
138
|
+
"[Tracing] Adding `{key}` header {value} to outgoing request to {url}.".format(
|
|
139
|
+
key=key, value=value, url=request.url
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
if key == BAGGAGE_HEADER_NAME and request.headers.get(
|
|
143
|
+
BAGGAGE_HEADER_NAME
|
|
144
|
+
):
|
|
145
|
+
# do not overwrite any existing baggage, just append to it
|
|
146
|
+
request.headers[key] += "," + value
|
|
147
|
+
else:
|
|
148
|
+
request.headers[key] = value
|
|
149
|
+
|
|
150
|
+
rv = await real_send(self, request, **kwargs)
|
|
151
|
+
|
|
152
|
+
span.set_http_status(rv.status_code)
|
|
153
|
+
span.set_data("reason", rv.reason_phrase)
|
|
154
|
+
|
|
155
|
+
with capture_internal_exceptions():
|
|
156
|
+
add_http_request_source(span)
|
|
157
|
+
|
|
158
|
+
return rv
|
|
159
|
+
|
|
160
|
+
AsyncClient.send = send
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _add_sentry_baggage_to_headers(headers, sentry_baggage):
|
|
164
|
+
# type: (MutableMapping[str, str], str) -> None
|
|
165
|
+
"""Add the Sentry baggage to the headers.
|
|
166
|
+
|
|
167
|
+
This function directly mutates the provided headers. The provided sentry_baggage
|
|
168
|
+
is appended to the existing baggage. If the baggage already contains Sentry items,
|
|
169
|
+
they are stripped out first.
|
|
170
|
+
"""
|
|
171
|
+
existing_baggage = headers.get(BAGGAGE_HEADER_NAME, "")
|
|
172
|
+
stripped_existing_baggage = Baggage.strip_sentry_baggage(existing_baggage)
|
|
173
|
+
|
|
174
|
+
separator = "," if len(stripped_existing_baggage) > 0 else ""
|
|
175
|
+
|
|
176
|
+
headers[BAGGAGE_HEADER_NAME] = (
|
|
177
|
+
stripped_existing_baggage + separator + sentry_baggage
|
|
178
|
+
)
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
import sentry_sdk
|
|
5
|
+
from sentry_sdk.api import continue_trace, get_baggage, get_traceparent
|
|
6
|
+
from sentry_sdk.consts import OP, SPANSTATUS
|
|
7
|
+
from sentry_sdk.integrations import DidNotEnable, Integration
|
|
8
|
+
from sentry_sdk.scope import should_send_default_pii
|
|
9
|
+
from sentry_sdk.tracing import (
|
|
10
|
+
BAGGAGE_HEADER_NAME,
|
|
11
|
+
SENTRY_TRACE_HEADER_NAME,
|
|
12
|
+
TransactionSource,
|
|
13
|
+
)
|
|
14
|
+
from sentry_sdk.utils import (
|
|
15
|
+
capture_internal_exceptions,
|
|
16
|
+
ensure_integration_enabled,
|
|
17
|
+
event_from_exception,
|
|
18
|
+
SENSITIVE_DATA_SUBSTITUTE,
|
|
19
|
+
reraise,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from typing import Any, Callable, Optional, Union, TypeVar
|
|
26
|
+
|
|
27
|
+
from sentry_sdk._types import EventProcessor, Event, Hint
|
|
28
|
+
from sentry_sdk.utils import ExcInfo
|
|
29
|
+
|
|
30
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
from huey.api import Huey, Result, ResultGroup, Task, PeriodicTask
|
|
34
|
+
from huey.exceptions import CancelExecution, RetryTask, TaskLockedException
|
|
35
|
+
except ImportError:
|
|
36
|
+
raise DidNotEnable("Huey is not installed")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
HUEY_CONTROL_FLOW_EXCEPTIONS = (CancelExecution, RetryTask, TaskLockedException)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class HueyIntegration(Integration):
|
|
43
|
+
identifier = "huey"
|
|
44
|
+
origin = f"auto.queue.{identifier}"
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def setup_once():
|
|
48
|
+
# type: () -> None
|
|
49
|
+
patch_enqueue()
|
|
50
|
+
patch_execute()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def patch_enqueue():
|
|
54
|
+
# type: () -> None
|
|
55
|
+
old_enqueue = Huey.enqueue
|
|
56
|
+
|
|
57
|
+
@ensure_integration_enabled(HueyIntegration, old_enqueue)
|
|
58
|
+
def _sentry_enqueue(self, task):
|
|
59
|
+
# type: (Huey, Task) -> Optional[Union[Result, ResultGroup]]
|
|
60
|
+
with sentry_sdk.start_span(
|
|
61
|
+
op=OP.QUEUE_SUBMIT_HUEY,
|
|
62
|
+
name=task.name,
|
|
63
|
+
origin=HueyIntegration.origin,
|
|
64
|
+
):
|
|
65
|
+
if not isinstance(task, PeriodicTask):
|
|
66
|
+
# Attach trace propagation data to task kwargs. We do
|
|
67
|
+
# not do this for periodic tasks, as these don't
|
|
68
|
+
# really have an originating transaction.
|
|
69
|
+
task.kwargs["sentry_headers"] = {
|
|
70
|
+
BAGGAGE_HEADER_NAME: get_baggage(),
|
|
71
|
+
SENTRY_TRACE_HEADER_NAME: get_traceparent(),
|
|
72
|
+
}
|
|
73
|
+
return old_enqueue(self, task)
|
|
74
|
+
|
|
75
|
+
Huey.enqueue = _sentry_enqueue
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _make_event_processor(task):
|
|
79
|
+
# type: (Any) -> EventProcessor
|
|
80
|
+
def event_processor(event, hint):
|
|
81
|
+
# type: (Event, Hint) -> Optional[Event]
|
|
82
|
+
|
|
83
|
+
with capture_internal_exceptions():
|
|
84
|
+
tags = event.setdefault("tags", {})
|
|
85
|
+
tags["huey_task_id"] = task.id
|
|
86
|
+
tags["huey_task_retry"] = task.default_retries > task.retries
|
|
87
|
+
extra = event.setdefault("extra", {})
|
|
88
|
+
extra["huey-job"] = {
|
|
89
|
+
"task": task.name,
|
|
90
|
+
"args": (
|
|
91
|
+
task.args
|
|
92
|
+
if should_send_default_pii()
|
|
93
|
+
else SENSITIVE_DATA_SUBSTITUTE
|
|
94
|
+
),
|
|
95
|
+
"kwargs": (
|
|
96
|
+
task.kwargs
|
|
97
|
+
if should_send_default_pii()
|
|
98
|
+
else SENSITIVE_DATA_SUBSTITUTE
|
|
99
|
+
),
|
|
100
|
+
"retry": (task.default_retries or 0) - task.retries,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return event
|
|
104
|
+
|
|
105
|
+
return event_processor
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _capture_exception(exc_info):
|
|
109
|
+
# type: (ExcInfo) -> None
|
|
110
|
+
scope = sentry_sdk.get_current_scope()
|
|
111
|
+
|
|
112
|
+
if exc_info[0] in HUEY_CONTROL_FLOW_EXCEPTIONS:
|
|
113
|
+
scope.transaction.set_status(SPANSTATUS.ABORTED)
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
scope.transaction.set_status(SPANSTATUS.INTERNAL_ERROR)
|
|
117
|
+
event, hint = event_from_exception(
|
|
118
|
+
exc_info,
|
|
119
|
+
client_options=sentry_sdk.get_client().options,
|
|
120
|
+
mechanism={"type": HueyIntegration.identifier, "handled": False},
|
|
121
|
+
)
|
|
122
|
+
scope.capture_event(event, hint=hint)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _wrap_task_execute(func):
|
|
126
|
+
# type: (F) -> F
|
|
127
|
+
|
|
128
|
+
@ensure_integration_enabled(HueyIntegration, func)
|
|
129
|
+
def _sentry_execute(*args, **kwargs):
|
|
130
|
+
# type: (*Any, **Any) -> Any
|
|
131
|
+
try:
|
|
132
|
+
result = func(*args, **kwargs)
|
|
133
|
+
except Exception:
|
|
134
|
+
exc_info = sys.exc_info()
|
|
135
|
+
_capture_exception(exc_info)
|
|
136
|
+
reraise(*exc_info)
|
|
137
|
+
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
return _sentry_execute # type: ignore
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def patch_execute():
|
|
144
|
+
# type: () -> None
|
|
145
|
+
old_execute = Huey._execute
|
|
146
|
+
|
|
147
|
+
@ensure_integration_enabled(HueyIntegration, old_execute)
|
|
148
|
+
def _sentry_execute(self, task, timestamp=None):
|
|
149
|
+
# type: (Huey, Task, Optional[datetime]) -> Any
|
|
150
|
+
with sentry_sdk.isolation_scope() as scope:
|
|
151
|
+
with capture_internal_exceptions():
|
|
152
|
+
scope._name = "huey"
|
|
153
|
+
scope.clear_breadcrumbs()
|
|
154
|
+
scope.add_event_processor(_make_event_processor(task))
|
|
155
|
+
|
|
156
|
+
sentry_headers = task.kwargs.pop("sentry_headers", None)
|
|
157
|
+
|
|
158
|
+
transaction = continue_trace(
|
|
159
|
+
sentry_headers or {},
|
|
160
|
+
name=task.name,
|
|
161
|
+
op=OP.QUEUE_TASK_HUEY,
|
|
162
|
+
source=TransactionSource.TASK,
|
|
163
|
+
origin=HueyIntegration.origin,
|
|
164
|
+
)
|
|
165
|
+
transaction.set_status(SPANSTATUS.OK)
|
|
166
|
+
|
|
167
|
+
if not getattr(task, "_sentry_is_patched", False):
|
|
168
|
+
task.execute = _wrap_task_execute(task.execute)
|
|
169
|
+
task._sentry_is_patched = True
|
|
170
|
+
|
|
171
|
+
with sentry_sdk.start_transaction(transaction):
|
|
172
|
+
return old_execute(self, task, timestamp)
|
|
173
|
+
|
|
174
|
+
Huey._execute = _sentry_execute
|