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.
Files changed (193) hide show
  1. sentry_sdk/__init__.py +48 -30
  2. sentry_sdk/_compat.py +74 -61
  3. sentry_sdk/_init_implementation.py +84 -0
  4. sentry_sdk/_log_batcher.py +172 -0
  5. sentry_sdk/_lru_cache.py +47 -0
  6. sentry_sdk/_metrics_batcher.py +167 -0
  7. sentry_sdk/_queue.py +289 -0
  8. sentry_sdk/_types.py +338 -0
  9. sentry_sdk/_werkzeug.py +98 -0
  10. sentry_sdk/ai/__init__.py +7 -0
  11. sentry_sdk/ai/monitoring.py +137 -0
  12. sentry_sdk/ai/utils.py +144 -0
  13. sentry_sdk/api.py +496 -80
  14. sentry_sdk/attachments.py +75 -0
  15. sentry_sdk/client.py +1023 -103
  16. sentry_sdk/consts.py +1438 -66
  17. sentry_sdk/crons/__init__.py +10 -0
  18. sentry_sdk/crons/api.py +62 -0
  19. sentry_sdk/crons/consts.py +4 -0
  20. sentry_sdk/crons/decorator.py +135 -0
  21. sentry_sdk/debug.py +15 -14
  22. sentry_sdk/envelope.py +369 -0
  23. sentry_sdk/feature_flags.py +71 -0
  24. sentry_sdk/hub.py +611 -280
  25. sentry_sdk/integrations/__init__.py +276 -49
  26. sentry_sdk/integrations/_asgi_common.py +108 -0
  27. sentry_sdk/integrations/_wsgi_common.py +180 -44
  28. sentry_sdk/integrations/aiohttp.py +291 -42
  29. sentry_sdk/integrations/anthropic.py +439 -0
  30. sentry_sdk/integrations/argv.py +9 -8
  31. sentry_sdk/integrations/ariadne.py +161 -0
  32. sentry_sdk/integrations/arq.py +247 -0
  33. sentry_sdk/integrations/asgi.py +341 -0
  34. sentry_sdk/integrations/asyncio.py +144 -0
  35. sentry_sdk/integrations/asyncpg.py +208 -0
  36. sentry_sdk/integrations/atexit.py +17 -10
  37. sentry_sdk/integrations/aws_lambda.py +377 -62
  38. sentry_sdk/integrations/beam.py +176 -0
  39. sentry_sdk/integrations/boto3.py +137 -0
  40. sentry_sdk/integrations/bottle.py +221 -0
  41. sentry_sdk/integrations/celery/__init__.py +529 -0
  42. sentry_sdk/integrations/celery/beat.py +293 -0
  43. sentry_sdk/integrations/celery/utils.py +43 -0
  44. sentry_sdk/integrations/chalice.py +134 -0
  45. sentry_sdk/integrations/clickhouse_driver.py +177 -0
  46. sentry_sdk/integrations/cloud_resource_context.py +280 -0
  47. sentry_sdk/integrations/cohere.py +274 -0
  48. sentry_sdk/integrations/dedupe.py +48 -14
  49. sentry_sdk/integrations/django/__init__.py +584 -191
  50. sentry_sdk/integrations/django/asgi.py +245 -0
  51. sentry_sdk/integrations/django/caching.py +204 -0
  52. sentry_sdk/integrations/django/middleware.py +187 -0
  53. sentry_sdk/integrations/django/signals_handlers.py +91 -0
  54. sentry_sdk/integrations/django/templates.py +79 -5
  55. sentry_sdk/integrations/django/transactions.py +49 -22
  56. sentry_sdk/integrations/django/views.py +96 -0
  57. sentry_sdk/integrations/dramatiq.py +226 -0
  58. sentry_sdk/integrations/excepthook.py +50 -13
  59. sentry_sdk/integrations/executing.py +67 -0
  60. sentry_sdk/integrations/falcon.py +272 -0
  61. sentry_sdk/integrations/fastapi.py +141 -0
  62. sentry_sdk/integrations/flask.py +142 -88
  63. sentry_sdk/integrations/gcp.py +239 -0
  64. sentry_sdk/integrations/gnu_backtrace.py +99 -0
  65. sentry_sdk/integrations/google_genai/__init__.py +301 -0
  66. sentry_sdk/integrations/google_genai/consts.py +16 -0
  67. sentry_sdk/integrations/google_genai/streaming.py +155 -0
  68. sentry_sdk/integrations/google_genai/utils.py +576 -0
  69. sentry_sdk/integrations/gql.py +162 -0
  70. sentry_sdk/integrations/graphene.py +151 -0
  71. sentry_sdk/integrations/grpc/__init__.py +168 -0
  72. sentry_sdk/integrations/grpc/aio/__init__.py +7 -0
  73. sentry_sdk/integrations/grpc/aio/client.py +95 -0
  74. sentry_sdk/integrations/grpc/aio/server.py +100 -0
  75. sentry_sdk/integrations/grpc/client.py +91 -0
  76. sentry_sdk/integrations/grpc/consts.py +1 -0
  77. sentry_sdk/integrations/grpc/server.py +66 -0
  78. sentry_sdk/integrations/httpx.py +178 -0
  79. sentry_sdk/integrations/huey.py +174 -0
  80. sentry_sdk/integrations/huggingface_hub.py +378 -0
  81. sentry_sdk/integrations/langchain.py +1132 -0
  82. sentry_sdk/integrations/langgraph.py +337 -0
  83. sentry_sdk/integrations/launchdarkly.py +61 -0
  84. sentry_sdk/integrations/litellm.py +287 -0
  85. sentry_sdk/integrations/litestar.py +315 -0
  86. sentry_sdk/integrations/logging.py +307 -96
  87. sentry_sdk/integrations/loguru.py +213 -0
  88. sentry_sdk/integrations/mcp.py +566 -0
  89. sentry_sdk/integrations/modules.py +14 -31
  90. sentry_sdk/integrations/openai.py +725 -0
  91. sentry_sdk/integrations/openai_agents/__init__.py +61 -0
  92. sentry_sdk/integrations/openai_agents/consts.py +1 -0
  93. sentry_sdk/integrations/openai_agents/patches/__init__.py +5 -0
  94. sentry_sdk/integrations/openai_agents/patches/agent_run.py +140 -0
  95. sentry_sdk/integrations/openai_agents/patches/error_tracing.py +77 -0
  96. sentry_sdk/integrations/openai_agents/patches/models.py +50 -0
  97. sentry_sdk/integrations/openai_agents/patches/runner.py +45 -0
  98. sentry_sdk/integrations/openai_agents/patches/tools.py +77 -0
  99. sentry_sdk/integrations/openai_agents/spans/__init__.py +5 -0
  100. sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +21 -0
  101. sentry_sdk/integrations/openai_agents/spans/ai_client.py +42 -0
  102. sentry_sdk/integrations/openai_agents/spans/execute_tool.py +48 -0
  103. sentry_sdk/integrations/openai_agents/spans/handoff.py +19 -0
  104. sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +86 -0
  105. sentry_sdk/integrations/openai_agents/utils.py +199 -0
  106. sentry_sdk/integrations/openfeature.py +35 -0
  107. sentry_sdk/integrations/opentelemetry/__init__.py +7 -0
  108. sentry_sdk/integrations/opentelemetry/consts.py +5 -0
  109. sentry_sdk/integrations/opentelemetry/integration.py +58 -0
  110. sentry_sdk/integrations/opentelemetry/propagator.py +117 -0
  111. sentry_sdk/integrations/opentelemetry/span_processor.py +391 -0
  112. sentry_sdk/integrations/otlp.py +82 -0
  113. sentry_sdk/integrations/pure_eval.py +141 -0
  114. sentry_sdk/integrations/pydantic_ai/__init__.py +47 -0
  115. sentry_sdk/integrations/pydantic_ai/consts.py +1 -0
  116. sentry_sdk/integrations/pydantic_ai/patches/__init__.py +4 -0
  117. sentry_sdk/integrations/pydantic_ai/patches/agent_run.py +215 -0
  118. sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py +110 -0
  119. sentry_sdk/integrations/pydantic_ai/patches/model_request.py +40 -0
  120. sentry_sdk/integrations/pydantic_ai/patches/tools.py +98 -0
  121. sentry_sdk/integrations/pydantic_ai/spans/__init__.py +3 -0
  122. sentry_sdk/integrations/pydantic_ai/spans/ai_client.py +246 -0
  123. sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py +49 -0
  124. sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py +112 -0
  125. sentry_sdk/integrations/pydantic_ai/utils.py +223 -0
  126. sentry_sdk/integrations/pymongo.py +214 -0
  127. sentry_sdk/integrations/pyramid.py +112 -68
  128. sentry_sdk/integrations/quart.py +237 -0
  129. sentry_sdk/integrations/ray.py +165 -0
  130. sentry_sdk/integrations/redis/__init__.py +48 -0
  131. sentry_sdk/integrations/redis/_async_common.py +116 -0
  132. sentry_sdk/integrations/redis/_sync_common.py +119 -0
  133. sentry_sdk/integrations/redis/consts.py +19 -0
  134. sentry_sdk/integrations/redis/modules/__init__.py +0 -0
  135. sentry_sdk/integrations/redis/modules/caches.py +118 -0
  136. sentry_sdk/integrations/redis/modules/queries.py +65 -0
  137. sentry_sdk/integrations/redis/rb.py +32 -0
  138. sentry_sdk/integrations/redis/redis.py +69 -0
  139. sentry_sdk/integrations/redis/redis_cluster.py +107 -0
  140. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +50 -0
  141. sentry_sdk/integrations/redis/utils.py +148 -0
  142. sentry_sdk/integrations/rq.py +95 -37
  143. sentry_sdk/integrations/rust_tracing.py +284 -0
  144. sentry_sdk/integrations/sanic.py +294 -123
  145. sentry_sdk/integrations/serverless.py +48 -19
  146. sentry_sdk/integrations/socket.py +96 -0
  147. sentry_sdk/integrations/spark/__init__.py +4 -0
  148. sentry_sdk/integrations/spark/spark_driver.py +316 -0
  149. sentry_sdk/integrations/spark/spark_worker.py +116 -0
  150. sentry_sdk/integrations/sqlalchemy.py +142 -0
  151. sentry_sdk/integrations/starlette.py +737 -0
  152. sentry_sdk/integrations/starlite.py +292 -0
  153. sentry_sdk/integrations/statsig.py +37 -0
  154. sentry_sdk/integrations/stdlib.py +235 -29
  155. sentry_sdk/integrations/strawberry.py +394 -0
  156. sentry_sdk/integrations/sys_exit.py +70 -0
  157. sentry_sdk/integrations/threading.py +158 -28
  158. sentry_sdk/integrations/tornado.py +84 -52
  159. sentry_sdk/integrations/trytond.py +50 -0
  160. sentry_sdk/integrations/typer.py +60 -0
  161. sentry_sdk/integrations/unleash.py +33 -0
  162. sentry_sdk/integrations/unraisablehook.py +53 -0
  163. sentry_sdk/integrations/wsgi.py +201 -119
  164. sentry_sdk/logger.py +96 -0
  165. sentry_sdk/metrics.py +81 -0
  166. sentry_sdk/monitor.py +120 -0
  167. sentry_sdk/profiler/__init__.py +49 -0
  168. sentry_sdk/profiler/continuous_profiler.py +730 -0
  169. sentry_sdk/profiler/transaction_profiler.py +839 -0
  170. sentry_sdk/profiler/utils.py +195 -0
  171. sentry_sdk/py.typed +0 -0
  172. sentry_sdk/scope.py +1713 -85
  173. sentry_sdk/scrubber.py +177 -0
  174. sentry_sdk/serializer.py +405 -0
  175. sentry_sdk/session.py +177 -0
  176. sentry_sdk/sessions.py +275 -0
  177. sentry_sdk/spotlight.py +242 -0
  178. sentry_sdk/tracing.py +1486 -0
  179. sentry_sdk/tracing_utils.py +1236 -0
  180. sentry_sdk/transport.py +806 -134
  181. sentry_sdk/types.py +52 -0
  182. sentry_sdk/utils.py +1625 -465
  183. sentry_sdk/worker.py +54 -25
  184. sentry_sdk-2.46.0.dist-info/METADATA +268 -0
  185. sentry_sdk-2.46.0.dist-info/RECORD +189 -0
  186. {sentry_sdk-0.7.5.dist-info → sentry_sdk-2.46.0.dist-info}/WHEEL +1 -1
  187. sentry_sdk-2.46.0.dist-info/entry_points.txt +2 -0
  188. sentry_sdk-2.46.0.dist-info/licenses/LICENSE +21 -0
  189. sentry_sdk/integrations/celery.py +0 -119
  190. sentry_sdk-0.7.5.dist-info/LICENSE +0 -9
  191. sentry_sdk-0.7.5.dist-info/METADATA +0 -36
  192. sentry_sdk-0.7.5.dist-info/RECORD +0 -39
  193. {sentry_sdk-0.7.5.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