sentry-sdk 2.27.0__py2.py3-none-any.whl → 3.0.0a1__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.
- sentry_sdk/__init__.py +4 -8
- sentry_sdk/_compat.py +0 -1
- sentry_sdk/_init_implementation.py +6 -44
- sentry_sdk/_log_batcher.py +47 -28
- sentry_sdk/_types.py +2 -64
- sentry_sdk/ai/monitoring.py +14 -10
- sentry_sdk/ai/utils.py +1 -1
- sentry_sdk/api.py +69 -163
- sentry_sdk/client.py +25 -72
- sentry_sdk/consts.py +42 -23
- sentry_sdk/debug.py +0 -10
- sentry_sdk/envelope.py +2 -10
- sentry_sdk/feature_flags.py +2 -2
- sentry_sdk/integrations/__init__.py +4 -2
- sentry_sdk/integrations/_asgi_common.py +3 -3
- sentry_sdk/integrations/_wsgi_common.py +11 -40
- sentry_sdk/integrations/aiohttp.py +104 -57
- sentry_sdk/integrations/anthropic.py +10 -7
- sentry_sdk/integrations/arq.py +24 -13
- sentry_sdk/integrations/asgi.py +102 -83
- sentry_sdk/integrations/asyncio.py +1 -0
- sentry_sdk/integrations/asyncpg.py +45 -30
- sentry_sdk/integrations/aws_lambda.py +109 -92
- sentry_sdk/integrations/boto3.py +38 -9
- sentry_sdk/integrations/bottle.py +1 -1
- sentry_sdk/integrations/celery/__init__.py +48 -38
- sentry_sdk/integrations/clickhouse_driver.py +59 -28
- sentry_sdk/integrations/cohere.py +2 -0
- sentry_sdk/integrations/django/__init__.py +25 -46
- sentry_sdk/integrations/django/asgi.py +6 -2
- sentry_sdk/integrations/django/caching.py +13 -22
- sentry_sdk/integrations/django/middleware.py +1 -0
- sentry_sdk/integrations/django/signals_handlers.py +3 -1
- sentry_sdk/integrations/django/templates.py +8 -12
- sentry_sdk/integrations/django/transactions.py +1 -6
- sentry_sdk/integrations/django/views.py +5 -2
- sentry_sdk/integrations/falcon.py +7 -25
- sentry_sdk/integrations/fastapi.py +3 -3
- sentry_sdk/integrations/flask.py +1 -1
- sentry_sdk/integrations/gcp.py +63 -38
- sentry_sdk/integrations/graphene.py +6 -13
- sentry_sdk/integrations/grpc/aio/client.py +14 -8
- sentry_sdk/integrations/grpc/aio/server.py +19 -21
- sentry_sdk/integrations/grpc/client.py +8 -6
- sentry_sdk/integrations/grpc/server.py +12 -14
- sentry_sdk/integrations/httpx.py +47 -12
- sentry_sdk/integrations/huey.py +26 -22
- sentry_sdk/integrations/huggingface_hub.py +1 -0
- sentry_sdk/integrations/langchain.py +22 -15
- sentry_sdk/integrations/litestar.py +4 -2
- sentry_sdk/integrations/logging.py +12 -3
- sentry_sdk/integrations/openai.py +2 -0
- sentry_sdk/integrations/pymongo.py +18 -25
- sentry_sdk/integrations/pyramid.py +1 -1
- sentry_sdk/integrations/quart.py +3 -3
- sentry_sdk/integrations/ray.py +23 -17
- sentry_sdk/integrations/redis/_async_common.py +30 -18
- sentry_sdk/integrations/redis/_sync_common.py +28 -18
- sentry_sdk/integrations/redis/modules/caches.py +13 -10
- sentry_sdk/integrations/redis/modules/queries.py +14 -11
- sentry_sdk/integrations/redis/rb.py +4 -4
- sentry_sdk/integrations/redis/redis.py +6 -6
- sentry_sdk/integrations/redis/redis_cluster.py +18 -16
- sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +4 -4
- sentry_sdk/integrations/redis/utils.py +63 -19
- sentry_sdk/integrations/rq.py +68 -23
- sentry_sdk/integrations/rust_tracing.py +28 -43
- sentry_sdk/integrations/sanic.py +23 -13
- sentry_sdk/integrations/socket.py +9 -5
- sentry_sdk/integrations/sqlalchemy.py +8 -8
- sentry_sdk/integrations/starlette.py +11 -31
- sentry_sdk/integrations/starlite.py +4 -2
- sentry_sdk/integrations/stdlib.py +56 -9
- sentry_sdk/integrations/strawberry.py +40 -59
- sentry_sdk/integrations/threading.py +10 -26
- sentry_sdk/integrations/tornado.py +57 -18
- sentry_sdk/integrations/trytond.py +4 -1
- sentry_sdk/integrations/wsgi.py +84 -38
- sentry_sdk/opentelemetry/__init__.py +9 -0
- sentry_sdk/opentelemetry/consts.py +33 -0
- sentry_sdk/opentelemetry/contextvars_context.py +73 -0
- sentry_sdk/{integrations/opentelemetry → opentelemetry}/propagator.py +19 -28
- sentry_sdk/opentelemetry/sampler.py +326 -0
- sentry_sdk/opentelemetry/scope.py +218 -0
- sentry_sdk/opentelemetry/span_processor.py +329 -0
- sentry_sdk/opentelemetry/tracing.py +35 -0
- sentry_sdk/opentelemetry/utils.py +476 -0
- sentry_sdk/profiler/__init__.py +0 -40
- sentry_sdk/profiler/continuous_profiler.py +1 -30
- sentry_sdk/profiler/transaction_profiler.py +5 -56
- sentry_sdk/scope.py +107 -351
- sentry_sdk/sessions.py +0 -87
- sentry_sdk/tracing.py +418 -1144
- sentry_sdk/tracing_utils.py +126 -164
- sentry_sdk/transport.py +4 -104
- sentry_sdk/utils.py +169 -152
- {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/METADATA +3 -5
- sentry_sdk-3.0.0a1.dist-info/RECORD +154 -0
- {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/WHEEL +1 -1
- sentry_sdk-3.0.0a1.dist-info/entry_points.txt +2 -0
- sentry_sdk/hub.py +0 -739
- sentry_sdk/integrations/opentelemetry/__init__.py +0 -7
- sentry_sdk/integrations/opentelemetry/consts.py +0 -5
- sentry_sdk/integrations/opentelemetry/integration.py +0 -58
- sentry_sdk/integrations/opentelemetry/span_processor.py +0 -391
- sentry_sdk/metrics.py +0 -965
- sentry_sdk-2.27.0.dist-info/RECORD +0 -152
- sentry_sdk-2.27.0.dist-info/entry_points.txt +0 -2
- {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-2.27.0.dist-info → sentry_sdk-3.0.0a1.dist-info}/top_level.txt +0 -0
|
@@ -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
|