sentry-sdk 2.30.0__py2.py3-none-any.whl → 3.0.0a2__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sentry-sdk might be problematic. Click here for more details.

Files changed (109) hide show
  1. sentry_sdk/__init__.py +3 -8
  2. sentry_sdk/_compat.py +0 -1
  3. sentry_sdk/_init_implementation.py +6 -44
  4. sentry_sdk/_types.py +2 -64
  5. sentry_sdk/ai/monitoring.py +14 -10
  6. sentry_sdk/ai/utils.py +1 -1
  7. sentry_sdk/api.py +56 -169
  8. sentry_sdk/client.py +27 -72
  9. sentry_sdk/consts.py +60 -23
  10. sentry_sdk/debug.py +0 -10
  11. sentry_sdk/envelope.py +1 -3
  12. sentry_sdk/feature_flags.py +1 -1
  13. sentry_sdk/integrations/__init__.py +4 -2
  14. sentry_sdk/integrations/_asgi_common.py +5 -6
  15. sentry_sdk/integrations/_wsgi_common.py +11 -40
  16. sentry_sdk/integrations/aiohttp.py +104 -57
  17. sentry_sdk/integrations/anthropic.py +10 -7
  18. sentry_sdk/integrations/arq.py +24 -13
  19. sentry_sdk/integrations/asgi.py +102 -83
  20. sentry_sdk/integrations/asyncio.py +1 -0
  21. sentry_sdk/integrations/asyncpg.py +45 -30
  22. sentry_sdk/integrations/aws_lambda.py +109 -92
  23. sentry_sdk/integrations/boto3.py +38 -9
  24. sentry_sdk/integrations/bottle.py +1 -1
  25. sentry_sdk/integrations/celery/__init__.py +51 -41
  26. sentry_sdk/integrations/clickhouse_driver.py +59 -28
  27. sentry_sdk/integrations/cohere.py +2 -0
  28. sentry_sdk/integrations/django/__init__.py +25 -46
  29. sentry_sdk/integrations/django/asgi.py +6 -2
  30. sentry_sdk/integrations/django/caching.py +13 -22
  31. sentry_sdk/integrations/django/middleware.py +1 -0
  32. sentry_sdk/integrations/django/signals_handlers.py +3 -1
  33. sentry_sdk/integrations/django/templates.py +8 -12
  34. sentry_sdk/integrations/django/transactions.py +1 -6
  35. sentry_sdk/integrations/django/views.py +5 -2
  36. sentry_sdk/integrations/falcon.py +7 -25
  37. sentry_sdk/integrations/fastapi.py +3 -3
  38. sentry_sdk/integrations/flask.py +1 -1
  39. sentry_sdk/integrations/gcp.py +63 -38
  40. sentry_sdk/integrations/graphene.py +6 -13
  41. sentry_sdk/integrations/grpc/aio/client.py +14 -8
  42. sentry_sdk/integrations/grpc/aio/server.py +19 -21
  43. sentry_sdk/integrations/grpc/client.py +8 -6
  44. sentry_sdk/integrations/grpc/server.py +12 -14
  45. sentry_sdk/integrations/httpx.py +47 -12
  46. sentry_sdk/integrations/huey.py +26 -22
  47. sentry_sdk/integrations/huggingface_hub.py +1 -0
  48. sentry_sdk/integrations/langchain.py +22 -15
  49. sentry_sdk/integrations/litestar.py +4 -2
  50. sentry_sdk/integrations/logging.py +7 -2
  51. sentry_sdk/integrations/openai.py +2 -0
  52. sentry_sdk/integrations/pymongo.py +18 -25
  53. sentry_sdk/integrations/pyramid.py +1 -1
  54. sentry_sdk/integrations/quart.py +3 -3
  55. sentry_sdk/integrations/ray.py +23 -17
  56. sentry_sdk/integrations/redis/_async_common.py +29 -18
  57. sentry_sdk/integrations/redis/_sync_common.py +28 -19
  58. sentry_sdk/integrations/redis/modules/caches.py +13 -10
  59. sentry_sdk/integrations/redis/modules/queries.py +14 -11
  60. sentry_sdk/integrations/redis/rb.py +4 -4
  61. sentry_sdk/integrations/redis/redis.py +6 -6
  62. sentry_sdk/integrations/redis/redis_cluster.py +18 -18
  63. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +4 -4
  64. sentry_sdk/integrations/redis/utils.py +64 -24
  65. sentry_sdk/integrations/rq.py +68 -23
  66. sentry_sdk/integrations/rust_tracing.py +28 -43
  67. sentry_sdk/integrations/sanic.py +23 -13
  68. sentry_sdk/integrations/socket.py +9 -5
  69. sentry_sdk/integrations/sqlalchemy.py +8 -8
  70. sentry_sdk/integrations/starlette.py +11 -31
  71. sentry_sdk/integrations/starlite.py +4 -2
  72. sentry_sdk/integrations/stdlib.py +56 -9
  73. sentry_sdk/integrations/strawberry.py +40 -59
  74. sentry_sdk/integrations/threading.py +10 -26
  75. sentry_sdk/integrations/tornado.py +57 -18
  76. sentry_sdk/integrations/trytond.py +4 -1
  77. sentry_sdk/integrations/wsgi.py +84 -38
  78. sentry_sdk/opentelemetry/__init__.py +9 -0
  79. sentry_sdk/opentelemetry/consts.py +33 -0
  80. sentry_sdk/opentelemetry/contextvars_context.py +81 -0
  81. sentry_sdk/{integrations/opentelemetry → opentelemetry}/propagator.py +19 -28
  82. sentry_sdk/opentelemetry/sampler.py +326 -0
  83. sentry_sdk/opentelemetry/scope.py +218 -0
  84. sentry_sdk/opentelemetry/span_processor.py +335 -0
  85. sentry_sdk/opentelemetry/tracing.py +59 -0
  86. sentry_sdk/opentelemetry/utils.py +484 -0
  87. sentry_sdk/profiler/__init__.py +0 -40
  88. sentry_sdk/profiler/continuous_profiler.py +1 -30
  89. sentry_sdk/profiler/transaction_profiler.py +5 -56
  90. sentry_sdk/scope.py +108 -361
  91. sentry_sdk/sessions.py +0 -87
  92. sentry_sdk/tracing.py +415 -1161
  93. sentry_sdk/tracing_utils.py +130 -166
  94. sentry_sdk/transport.py +4 -104
  95. sentry_sdk/utils.py +169 -152
  96. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/METADATA +3 -5
  97. sentry_sdk-3.0.0a2.dist-info/RECORD +154 -0
  98. sentry_sdk-3.0.0a2.dist-info/entry_points.txt +2 -0
  99. sentry_sdk/hub.py +0 -739
  100. sentry_sdk/integrations/opentelemetry/__init__.py +0 -7
  101. sentry_sdk/integrations/opentelemetry/consts.py +0 -5
  102. sentry_sdk/integrations/opentelemetry/integration.py +0 -58
  103. sentry_sdk/integrations/opentelemetry/span_processor.py +0 -391
  104. sentry_sdk/metrics.py +0 -965
  105. sentry_sdk-2.30.0.dist-info/RECORD +0 -152
  106. sentry_sdk-2.30.0.dist-info/entry_points.txt +0 -2
  107. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/WHEEL +0 -0
  108. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/licenses/LICENSE +0 -0
  109. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/top_level.txt +0 -0
@@ -1,7 +0,0 @@
1
- from sentry_sdk.integrations.opentelemetry.span_processor import SentrySpanProcessor
2
- from sentry_sdk.integrations.opentelemetry.propagator import SentryPropagator
3
-
4
- __all__ = [
5
- "SentryPropagator",
6
- "SentrySpanProcessor",
7
- ]
@@ -1,5 +0,0 @@
1
- from opentelemetry.context import create_key
2
-
3
-
4
- SENTRY_TRACE_KEY = create_key("sentry-trace")
5
- SENTRY_BAGGAGE_KEY = create_key("sentry-baggage")
@@ -1,58 +0,0 @@
1
- """
2
- IMPORTANT: The contents of this file are part of a proof of concept and as such
3
- are experimental and not suitable for production use. They may be changed or
4
- removed at any time without prior notice.
5
- """
6
-
7
- from sentry_sdk.integrations import DidNotEnable, Integration
8
- from sentry_sdk.integrations.opentelemetry.propagator import SentryPropagator
9
- from sentry_sdk.integrations.opentelemetry.span_processor import SentrySpanProcessor
10
- from sentry_sdk.utils import logger
11
-
12
- try:
13
- from opentelemetry import trace
14
- from opentelemetry.propagate import set_global_textmap
15
- from opentelemetry.sdk.trace import TracerProvider
16
- except ImportError:
17
- raise DidNotEnable("opentelemetry not installed")
18
-
19
- try:
20
- from opentelemetry.instrumentation.django import DjangoInstrumentor # type: ignore[import-not-found]
21
- except ImportError:
22
- DjangoInstrumentor = None
23
-
24
-
25
- CONFIGURABLE_INSTRUMENTATIONS = {
26
- DjangoInstrumentor: {"is_sql_commentor_enabled": True},
27
- }
28
-
29
-
30
- class OpenTelemetryIntegration(Integration):
31
- identifier = "opentelemetry"
32
-
33
- @staticmethod
34
- def setup_once():
35
- # type: () -> None
36
- logger.warning(
37
- "[OTel] Initializing highly experimental OpenTelemetry support. "
38
- "Use at your own risk."
39
- )
40
-
41
- _setup_sentry_tracing()
42
- # _setup_instrumentors()
43
-
44
- logger.debug("[OTel] Finished setting up OpenTelemetry integration")
45
-
46
-
47
- def _setup_sentry_tracing():
48
- # type: () -> None
49
- provider = TracerProvider()
50
- provider.add_span_processor(SentrySpanProcessor())
51
- trace.set_tracer_provider(provider)
52
- set_global_textmap(SentryPropagator())
53
-
54
-
55
- def _setup_instrumentors():
56
- # type: () -> None
57
- for instrumentor, kwargs in CONFIGURABLE_INSTRUMENTATIONS.items():
58
- instrumentor().instrument(**kwargs)
@@ -1,391 +0,0 @@
1
- from datetime import datetime, timezone
2
- from time import time
3
- from typing import TYPE_CHECKING, cast
4
-
5
- from opentelemetry.context import get_value
6
- from opentelemetry.sdk.trace import SpanProcessor, ReadableSpan as OTelSpan
7
- from opentelemetry.semconv.trace import SpanAttributes
8
- from opentelemetry.trace import (
9
- format_span_id,
10
- format_trace_id,
11
- get_current_span,
12
- SpanKind,
13
- )
14
- from opentelemetry.trace.span import (
15
- INVALID_SPAN_ID,
16
- INVALID_TRACE_ID,
17
- )
18
- from sentry_sdk import get_client, start_transaction
19
- from sentry_sdk.consts import INSTRUMENTER, SPANSTATUS
20
- from sentry_sdk.integrations.opentelemetry.consts import (
21
- SENTRY_BAGGAGE_KEY,
22
- SENTRY_TRACE_KEY,
23
- )
24
- from sentry_sdk.scope import add_global_event_processor
25
- from sentry_sdk.tracing import Transaction, Span as SentrySpan
26
- from sentry_sdk.utils import Dsn
27
-
28
- from urllib3.util import parse_url as urlparse
29
-
30
- if TYPE_CHECKING:
31
- from typing import Any, Optional, Union
32
- from opentelemetry import context as context_api
33
- from sentry_sdk._types import Event, Hint
34
-
35
- OPEN_TELEMETRY_CONTEXT = "otel"
36
- SPAN_MAX_TIME_OPEN_MINUTES = 10
37
- SPAN_ORIGIN = "auto.otel"
38
-
39
-
40
- def link_trace_context_to_error_event(event, otel_span_map):
41
- # type: (Event, dict[str, Union[Transaction, SentrySpan]]) -> Event
42
- client = get_client()
43
-
44
- if client.options["instrumenter"] != INSTRUMENTER.OTEL:
45
- return event
46
-
47
- if hasattr(event, "type") and event["type"] == "transaction":
48
- return event
49
-
50
- otel_span = get_current_span()
51
- if not otel_span:
52
- return event
53
-
54
- ctx = otel_span.get_span_context()
55
-
56
- if ctx.trace_id == INVALID_TRACE_ID or ctx.span_id == INVALID_SPAN_ID:
57
- return event
58
-
59
- sentry_span = otel_span_map.get(format_span_id(ctx.span_id), None)
60
- if not sentry_span:
61
- return event
62
-
63
- contexts = event.setdefault("contexts", {})
64
- contexts.setdefault("trace", {}).update(sentry_span.get_trace_context())
65
-
66
- return event
67
-
68
-
69
- class SentrySpanProcessor(SpanProcessor):
70
- """
71
- Converts OTel spans into Sentry spans so they can be sent to the Sentry backend.
72
- """
73
-
74
- # The mapping from otel span ids to sentry spans
75
- otel_span_map = {} # type: dict[str, Union[Transaction, SentrySpan]]
76
-
77
- # The currently open spans. Elements will be discarded after SPAN_MAX_TIME_OPEN_MINUTES
78
- open_spans = {} # type: dict[int, set[str]]
79
-
80
- def __new__(cls):
81
- # type: () -> SentrySpanProcessor
82
- if not hasattr(cls, "instance"):
83
- cls.instance = super().__new__(cls)
84
-
85
- return cls.instance
86
-
87
- def __init__(self):
88
- # type: () -> None
89
- @add_global_event_processor
90
- def global_event_processor(event, hint):
91
- # type: (Event, Hint) -> Event
92
- return link_trace_context_to_error_event(event, self.otel_span_map)
93
-
94
- def _prune_old_spans(self):
95
- # type: (SentrySpanProcessor) -> None
96
- """
97
- Prune spans that have been open for too long.
98
- """
99
- current_time_minutes = int(time() / 60)
100
- for span_start_minutes in list(
101
- self.open_spans.keys()
102
- ): # making a list because we change the dict
103
- # prune empty open spans buckets
104
- if self.open_spans[span_start_minutes] == set():
105
- self.open_spans.pop(span_start_minutes)
106
-
107
- # prune old buckets
108
- elif current_time_minutes - span_start_minutes > SPAN_MAX_TIME_OPEN_MINUTES:
109
- for span_id in self.open_spans.pop(span_start_minutes):
110
- self.otel_span_map.pop(span_id, None)
111
-
112
- def on_start(self, otel_span, parent_context=None):
113
- # type: (OTelSpan, Optional[context_api.Context]) -> None
114
- client = get_client()
115
-
116
- if not client.dsn:
117
- return
118
-
119
- try:
120
- _ = Dsn(client.dsn)
121
- except Exception:
122
- return
123
-
124
- if client.options["instrumenter"] != INSTRUMENTER.OTEL:
125
- return
126
-
127
- if not otel_span.get_span_context().is_valid:
128
- return
129
-
130
- if self._is_sentry_span(otel_span):
131
- return
132
-
133
- trace_data = self._get_trace_data(otel_span, parent_context)
134
-
135
- parent_span_id = trace_data["parent_span_id"]
136
- sentry_parent_span = (
137
- self.otel_span_map.get(parent_span_id) if parent_span_id else None
138
- )
139
-
140
- start_timestamp = None
141
- if otel_span.start_time is not None:
142
- start_timestamp = datetime.fromtimestamp(
143
- otel_span.start_time / 1e9, timezone.utc
144
- ) # OTel spans have nanosecond precision
145
-
146
- sentry_span = None
147
- if sentry_parent_span:
148
- sentry_span = sentry_parent_span.start_child(
149
- span_id=trace_data["span_id"],
150
- name=otel_span.name,
151
- start_timestamp=start_timestamp,
152
- instrumenter=INSTRUMENTER.OTEL,
153
- origin=SPAN_ORIGIN,
154
- )
155
- else:
156
- sentry_span = start_transaction(
157
- name=otel_span.name,
158
- span_id=trace_data["span_id"],
159
- parent_span_id=parent_span_id,
160
- trace_id=trace_data["trace_id"],
161
- baggage=trace_data["baggage"],
162
- start_timestamp=start_timestamp,
163
- instrumenter=INSTRUMENTER.OTEL,
164
- origin=SPAN_ORIGIN,
165
- )
166
-
167
- self.otel_span_map[trace_data["span_id"]] = sentry_span
168
-
169
- if otel_span.start_time is not None:
170
- span_start_in_minutes = int(
171
- otel_span.start_time / 1e9 / 60
172
- ) # OTel spans have nanosecond precision
173
- self.open_spans.setdefault(span_start_in_minutes, set()).add(
174
- trace_data["span_id"]
175
- )
176
-
177
- self._prune_old_spans()
178
-
179
- def on_end(self, otel_span):
180
- # type: (OTelSpan) -> None
181
- client = get_client()
182
-
183
- if client.options["instrumenter"] != INSTRUMENTER.OTEL:
184
- return
185
-
186
- span_context = otel_span.get_span_context()
187
- if not span_context.is_valid:
188
- return
189
-
190
- span_id = format_span_id(span_context.span_id)
191
- sentry_span = self.otel_span_map.pop(span_id, None)
192
- if not sentry_span:
193
- return
194
-
195
- sentry_span.op = otel_span.name
196
-
197
- self._update_span_with_otel_status(sentry_span, otel_span)
198
-
199
- if isinstance(sentry_span, Transaction):
200
- sentry_span.name = otel_span.name
201
- sentry_span.set_context(
202
- OPEN_TELEMETRY_CONTEXT, self._get_otel_context(otel_span)
203
- )
204
- self._update_transaction_with_otel_data(sentry_span, otel_span)
205
-
206
- else:
207
- self._update_span_with_otel_data(sentry_span, otel_span)
208
-
209
- end_timestamp = None
210
- if otel_span.end_time is not None:
211
- end_timestamp = datetime.fromtimestamp(
212
- otel_span.end_time / 1e9, timezone.utc
213
- ) # OTel spans have nanosecond precision
214
-
215
- sentry_span.finish(end_timestamp=end_timestamp)
216
-
217
- if otel_span.start_time is not None:
218
- span_start_in_minutes = int(
219
- otel_span.start_time / 1e9 / 60
220
- ) # OTel spans have nanosecond precision
221
- self.open_spans.setdefault(span_start_in_minutes, set()).discard(span_id)
222
-
223
- self._prune_old_spans()
224
-
225
- def _is_sentry_span(self, otel_span):
226
- # type: (OTelSpan) -> bool
227
- """
228
- Break infinite loop:
229
- HTTP requests to Sentry are caught by OTel and send again to Sentry.
230
- """
231
- otel_span_url = None
232
- if otel_span.attributes is not None:
233
- otel_span_url = otel_span.attributes.get(SpanAttributes.HTTP_URL)
234
- otel_span_url = cast("Optional[str]", otel_span_url)
235
-
236
- dsn_url = None
237
- client = get_client()
238
- if client.dsn:
239
- dsn_url = Dsn(client.dsn).netloc
240
-
241
- if otel_span_url and dsn_url and dsn_url in otel_span_url:
242
- return True
243
-
244
- return False
245
-
246
- def _get_otel_context(self, otel_span):
247
- # type: (OTelSpan) -> dict[str, Any]
248
- """
249
- Returns the OTel context for Sentry.
250
- See: https://develop.sentry.dev/sdk/performance/opentelemetry/#step-5-add-opentelemetry-context
251
- """
252
- ctx = {}
253
-
254
- if otel_span.attributes:
255
- ctx["attributes"] = dict(otel_span.attributes)
256
-
257
- if otel_span.resource.attributes:
258
- ctx["resource"] = dict(otel_span.resource.attributes)
259
-
260
- return ctx
261
-
262
- def _get_trace_data(self, otel_span, parent_context):
263
- # type: (OTelSpan, Optional[context_api.Context]) -> dict[str, Any]
264
- """
265
- Extracts tracing information from one OTel span and its parent OTel context.
266
- """
267
- trace_data = {} # type: dict[str, Any]
268
- span_context = otel_span.get_span_context()
269
-
270
- span_id = format_span_id(span_context.span_id)
271
- trace_data["span_id"] = span_id
272
-
273
- trace_id = format_trace_id(span_context.trace_id)
274
- trace_data["trace_id"] = trace_id
275
-
276
- parent_span_id = (
277
- format_span_id(otel_span.parent.span_id) if otel_span.parent else None
278
- )
279
- trace_data["parent_span_id"] = parent_span_id
280
-
281
- sentry_trace_data = get_value(SENTRY_TRACE_KEY, parent_context)
282
- sentry_trace_data = cast("dict[str, Union[str, bool, None]]", sentry_trace_data)
283
- trace_data["parent_sampled"] = (
284
- sentry_trace_data["parent_sampled"] if sentry_trace_data else None
285
- )
286
-
287
- baggage = get_value(SENTRY_BAGGAGE_KEY, parent_context)
288
- trace_data["baggage"] = baggage
289
-
290
- return trace_data
291
-
292
- def _update_span_with_otel_status(self, sentry_span, otel_span):
293
- # type: (SentrySpan, OTelSpan) -> None
294
- """
295
- Set the Sentry span status from the OTel span
296
- """
297
- if otel_span.status.is_unset:
298
- return
299
-
300
- if otel_span.status.is_ok:
301
- sentry_span.set_status(SPANSTATUS.OK)
302
- return
303
-
304
- sentry_span.set_status(SPANSTATUS.INTERNAL_ERROR)
305
-
306
- def _update_span_with_otel_data(self, sentry_span, otel_span):
307
- # type: (SentrySpan, OTelSpan) -> None
308
- """
309
- Convert OTel span data and update the Sentry span with it.
310
- This should eventually happen on the server when ingesting the spans.
311
- """
312
- sentry_span.set_data("otel.kind", otel_span.kind)
313
-
314
- op = otel_span.name
315
- description = otel_span.name
316
-
317
- if otel_span.attributes is not None:
318
- for key, val in otel_span.attributes.items():
319
- sentry_span.set_data(key, val)
320
-
321
- http_method = otel_span.attributes.get(SpanAttributes.HTTP_METHOD)
322
- http_method = cast("Optional[str]", http_method)
323
-
324
- db_query = otel_span.attributes.get(SpanAttributes.DB_SYSTEM)
325
-
326
- if http_method:
327
- op = "http"
328
-
329
- if otel_span.kind == SpanKind.SERVER:
330
- op += ".server"
331
- elif otel_span.kind == SpanKind.CLIENT:
332
- op += ".client"
333
-
334
- description = http_method
335
-
336
- peer_name = otel_span.attributes.get(SpanAttributes.NET_PEER_NAME, None)
337
- if peer_name:
338
- description += " {}".format(peer_name)
339
-
340
- target = otel_span.attributes.get(SpanAttributes.HTTP_TARGET, None)
341
- if target:
342
- description += " {}".format(target)
343
-
344
- if not peer_name and not target:
345
- url = otel_span.attributes.get(SpanAttributes.HTTP_URL, None)
346
- url = cast("Optional[str]", url)
347
- if url:
348
- parsed_url = urlparse(url)
349
- url = "{}://{}{}".format(
350
- parsed_url.scheme, parsed_url.netloc, parsed_url.path
351
- )
352
- description += " {}".format(url)
353
-
354
- status_code = otel_span.attributes.get(
355
- SpanAttributes.HTTP_STATUS_CODE, None
356
- )
357
- status_code = cast("Optional[int]", status_code)
358
- if status_code:
359
- sentry_span.set_http_status(status_code)
360
-
361
- elif db_query:
362
- op = "db"
363
- statement = otel_span.attributes.get(SpanAttributes.DB_STATEMENT, None)
364
- statement = cast("Optional[str]", statement)
365
- if statement:
366
- description = statement
367
-
368
- sentry_span.op = op
369
- sentry_span.description = description
370
-
371
- def _update_transaction_with_otel_data(self, sentry_span, otel_span):
372
- # type: (SentrySpan, OTelSpan) -> None
373
- if otel_span.attributes is None:
374
- return
375
-
376
- http_method = otel_span.attributes.get(SpanAttributes.HTTP_METHOD)
377
-
378
- if http_method:
379
- status_code = otel_span.attributes.get(SpanAttributes.HTTP_STATUS_CODE)
380
- status_code = cast("Optional[int]", status_code)
381
- if status_code:
382
- sentry_span.set_http_status(status_code)
383
-
384
- op = "http"
385
-
386
- if otel_span.kind == SpanKind.SERVER:
387
- op += ".server"
388
- elif otel_span.kind == SpanKind.CLIENT:
389
- op += ".client"
390
-
391
- sentry_span.op = op