Skip to content

Commit 6673978

Browse files
committed
stdlib
1 parent 3ca46dc commit 6673978

File tree

4 files changed

+148
-75
lines changed

4 files changed

+148
-75
lines changed

sentry_sdk/integrations/starlette.py

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -170,44 +170,37 @@ async def _create_span_call(
170170
middleware_name = app.__class__.__name__
171171

172172
if span_streaming:
173-
span_ctx = sentry_sdk.traces.start_span(name=middleware_name)
173+
middleware_span = sentry_sdk.traces.start_span(name=middleware_name)
174+
middleware_span.set_op(OP.MIDDLEWARE_STARLETTE)
175+
middleware_span.set_origin(StarletteIntegration.origin)
176+
middleware_span.set_attribute("starlette.middleware_name", middleware_name)
174177
else:
175-
span_ctx = sentry_sdk.start_span(
178+
middleware_span = sentry_sdk.start_span(
176179
op=OP.MIDDLEWARE_STARLETTE,
177180
name=middleware_name,
178181
origin=StarletteIntegration.origin,
179182
)
183+
middleware_span.set_tag("starlette.middleware_name", middleware_name)
180184

181-
with span_ctx as middleware_span:
182-
if span_streaming:
183-
middleware_span.set_op(OP.MIDDLEWARE_STARLETTE)
184-
middleware_span.set_origin(StarletteIntegration.origin)
185-
middleware_span.set_attribute(
186-
"starlette.middleware_name", middleware_name
187-
)
188-
else:
189-
middleware_span.set_tag("starlette.middleware_name", middleware_name)
190-
185+
with middleware_span:
191186
# Creating spans for the "receive" callback
192187
async def _sentry_receive(*args: "Any", **kwargs: "Any") -> "Any":
193188
if span_streaming:
194-
span_ctx = sentry_sdk.tracing.start_span(
189+
span = sentry_sdk.traces.start_span(
195190
name=getattr(receive, "__qualname__", str(receive)),
196191
)
192+
span.set_origin(StarletteIntegration.origin)
193+
span.set_op(OP.MIDDLEWARE_STARLETTE_RECEIVE)
194+
span.set_attribute("starlette.middleware_name", middleware_name)
197195
else:
198-
span_ctx = sentry_sdk.start_span(
196+
span = sentry_sdk.start_span(
199197
op=OP.MIDDLEWARE_STARLETTE_RECEIVE,
200198
name=getattr(receive, "__qualname__", str(receive)),
201199
origin=StarletteIntegration.origin,
202200
)
201+
span.set_tag("starlette.middleware_name", middleware_name)
203202

204-
with span_ctx as span:
205-
if span_streaming:
206-
span.set_origin(StarletteIntegration.origin)
207-
span.set_op(OP.MIDDLEWARE_STARLETTE_RECEIVE)
208-
span.set_attribute("starlette.middleware_name", middleware_name)
209-
else:
210-
span.set_tag("starlette.middleware_name", middleware_name)
203+
with span:
211204
return await receive(*args, **kwargs)
212205

213206
receive_name = getattr(receive, "__name__", str(receive))
@@ -217,24 +210,21 @@ async def _sentry_receive(*args: "Any", **kwargs: "Any") -> "Any":
217210
# Creating spans for the "send" callback
218211
async def _sentry_send(*args: "Any", **kwargs: "Any") -> "Any":
219212
if span_streaming:
220-
span_ctx = sentry_sdk.tracing.start_span(
213+
span = sentry_sdk.traces.start_span(
221214
name=getattr(send, "__qualname__", str(send)),
222215
)
216+
span.set_op(OP.MIDDLEWARE_STARLETTE_SEND)
217+
span.set_origin(StarletteIntegration.origin)
218+
span.set_attribute("starlette.middleware_name", middleware_name)
223219
else:
224-
span_ctx = sentry_sdk.start_span(
220+
span = sentry_sdk.start_span(
225221
op=OP.MIDDLEWARE_STARLETTE_SEND,
226222
name=getattr(send, "__qualname__", str(send)),
227223
origin=StarletteIntegration.origin,
228224
)
225+
span.set_tag("starlette.middleware_name", middleware_name)
229226

230-
with span_ctx as span:
231-
if span_streaming:
232-
span.set_op(OP.MIDDLEWARE_STARLETTE_SEND)
233-
span.set_origin(StarletteIntegration.origin)
234-
span.set_attribute("starlette.middleware_name", middleware_name)
235-
else:
236-
span.set_tag("starlette.middleware_name", middleware_name)
237-
227+
with span:
238228
return await send(*args, **kwargs)
239229

240230
send_name = getattr(send, "__name__", str(send))

sentry_sdk/integrations/stdlib.py

Lines changed: 104 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
from sentry_sdk.consts import OP, SPANDATA
99
from sentry_sdk.integrations import Integration
1010
from sentry_sdk.scope import add_global_event_processor
11+
from sentry_sdk.traces import StreamedSpan
1112
from sentry_sdk.tracing_utils import (
1213
EnvironHeaders,
1314
should_propagate_trace,
1415
add_http_request_source,
16+
has_span_streaming_enabled,
1517
)
1618
from sentry_sdk.utils import (
1719
SENSITIVE_DATA_SUBSTITUTE,
@@ -86,6 +88,8 @@ def putrequest(
8688
):
8789
return real_putrequest(self, method, url, *args, **kwargs)
8890

91+
span_streaming = has_span_streaming_enabled(client.options)
92+
8993
real_url = url
9094
if real_url is None or not real_url.startswith(("http://", "https://")):
9195
real_url = "%s://%s%s%s" % (
@@ -99,22 +103,44 @@ def putrequest(
99103
with capture_internal_exceptions():
100104
parsed_url = parse_url(real_url, sanitize=False)
101105

102-
span = sentry_sdk.start_span(
103-
op=OP.HTTP_CLIENT,
104-
name="%s %s"
105-
% (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE),
106-
origin="auto.http.stdlib.httplib",
107-
)
108-
span.set_data(SPANDATA.HTTP_METHOD, method)
109-
if parsed_url is not None:
110-
span.set_data("url", parsed_url.url)
111-
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
112-
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
113-
114-
# for proxies, these point to the proxy host/port
115-
if tunnel_host:
116-
span.set_data(SPANDATA.NETWORK_PEER_ADDRESS, self.host)
117-
span.set_data(SPANDATA.NETWORK_PEER_PORT, self.port)
106+
if span_streaming:
107+
span = sentry_sdk.traces.start_span(
108+
name=f"{method} {parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE}"
109+
)
110+
span.set_op(OP.HTTP_CLIENT)
111+
span.set_origin("auto.http.stdlib.httplib")
112+
113+
span.set_attribute(SPANDATA.HTTP_METHOD, method)
114+
if parsed_url is not None:
115+
span.set_attribute("url", parsed_url.url)
116+
span.set_attribute(SPANDATA.HTTP_QUERY, parsed_url.query)
117+
span.set_attribute(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
118+
119+
# for proxies, these point to the proxy host/port
120+
if tunnel_host:
121+
span.set_attribute(SPANDATA.NETWORK_PEER_ADDRESS, self.host)
122+
span.set_attribute(SPANDATA.NETWORK_PEER_PORT, self.port)
123+
124+
span.start()
125+
126+
else:
127+
span = sentry_sdk.start_span(
128+
op=OP.HTTP_CLIENT,
129+
name="%s %s"
130+
% (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE),
131+
origin="auto.http.stdlib.httplib",
132+
)
133+
134+
span.set_data(SPANDATA.HTTP_METHOD, method)
135+
if parsed_url is not None:
136+
span.set_data("url", parsed_url.url)
137+
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
138+
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
139+
140+
# for proxies, these point to the proxy host/port
141+
if tunnel_host:
142+
span.set_data(SPANDATA.NETWORK_PEER_ADDRESS, self.host)
143+
span.set_data(SPANDATA.NETWORK_PEER_PORT, self.port)
118144

119145
rv = real_putrequest(self, method, url, *args, **kwargs)
120146

@@ -146,7 +172,10 @@ def getresponse(self: "HTTPConnection", *args: "Any", **kwargs: "Any") -> "Any":
146172
rv = real_getresponse(self, *args, **kwargs)
147173

148174
span.set_http_status(int(rv.status))
149-
span.set_data("reason", rv.reason)
175+
if isinstance(span, StreamedSpan):
176+
span.set_attribute("reason", rv.reason)
177+
else:
178+
span.set_data("reason", rv.reason)
150179
finally:
151180
span.finish()
152181

@@ -226,11 +255,23 @@ def sentry_patched_popen_init(
226255

227256
env = None
228257

229-
with sentry_sdk.start_span(
230-
op=OP.SUBPROCESS,
231-
name=description,
232-
origin="auto.subprocess.stdlib.subprocess",
233-
) as span:
258+
client = sentry_sdk.get_client()
259+
span_streaming = has_span_streaming_enabled(client.options)
260+
261+
if span_streaming:
262+
span = sentry_sdk.traces.start_span(
263+
name=description,
264+
)
265+
span.set_op(OP.SUBPROCESS)
266+
span.set_origin("auto.subprocess.stdlib.subprocess")
267+
else:
268+
span = sentry_sdk.start_span(
269+
op=OP.SUBPROCESS,
270+
name=description,
271+
origin="auto.subprocess.stdlib.subprocess",
272+
)
273+
274+
with span:
234275
for k, v in sentry_sdk.get_current_scope().iter_trace_propagation_headers(
235276
span=span
236277
):
@@ -245,11 +286,18 @@ def sentry_patched_popen_init(
245286
env["SUBPROCESS_" + k.upper().replace("-", "_")] = v
246287

247288
if cwd:
248-
span.set_data("subprocess.cwd", cwd)
289+
if span_streaming:
290+
span.set_attribute("subprocess.cwd", cwd)
291+
else:
292+
span.set_data("subprocess.cwd", cwd)
249293

250294
rv = old_popen_init(self, *a, **kw)
251295

252-
span.set_tag("subprocess.pid", self.pid)
296+
if span_streaming:
297+
span.set_attribute("subprocess.pid", self.pid)
298+
else:
299+
span.set_tag("subprocess.pid", self.pid)
300+
253301
return rv
254302

255303
subprocess.Popen.__init__ = sentry_patched_popen_init # type: ignore
@@ -260,11 +308,22 @@ def sentry_patched_popen_init(
260308
def sentry_patched_popen_wait(
261309
self: "subprocess.Popen[Any]", *a: "Any", **kw: "Any"
262310
) -> "Any":
263-
with sentry_sdk.start_span(
264-
op=OP.SUBPROCESS_WAIT,
265-
origin="auto.subprocess.stdlib.subprocess",
266-
) as span:
311+
client = sentry_sdk.get_client()
312+
span_streaming = has_span_streaming_enabled(client.options)
313+
314+
if span_streaming:
315+
span = sentry_sdk.traces.start_span(name="subprocess popen")
316+
span.set_op(OP.SUBPROCESS_WAIT)
317+
span.set_origin("auto.subprocess.stdlib.subprocess")
318+
span.set_tag("subprocess.pid", self.pid)
319+
else:
320+
span = sentry_sdk.start_span(
321+
op=OP.SUBPROCESS_WAIT,
322+
origin="auto.subprocess.stdlib.subprocess",
323+
)
267324
span.set_tag("subprocess.pid", self.pid)
325+
326+
with span:
268327
return old_popen_wait(self, *a, **kw)
269328

270329
subprocess.Popen.wait = sentry_patched_popen_wait # type: ignore
@@ -275,11 +334,24 @@ def sentry_patched_popen_wait(
275334
def sentry_patched_popen_communicate(
276335
self: "subprocess.Popen[Any]", *a: "Any", **kw: "Any"
277336
) -> "Any":
278-
with sentry_sdk.start_span(
279-
op=OP.SUBPROCESS_COMMUNICATE,
280-
origin="auto.subprocess.stdlib.subprocess",
281-
) as span:
337+
client = sentry_sdk.get_client()
338+
span_streaming = has_span_streaming_enabled(client.options)
339+
340+
if span_streaming:
341+
span = sentry_sdk.traces.start_span(
342+
name="subprocess communicate",
343+
)
344+
span.set_op(OP.SUBPROCESS_COMMUNICATE)
345+
span.set_origin("auto.subprocess.stdlib.subprocess")
282346
span.set_tag("subprocess.pid", self.pid)
347+
else:
348+
span = sentry_sdk.start_span(
349+
op=OP.SUBPROCESS_COMMUNICATE,
350+
origin="auto.subprocess.stdlib.subprocess",
351+
)
352+
span.set_tag("subprocess.pid", self.pid)
353+
354+
with span:
283355
return old_popen_communicate(self, *a, **kw)
284356

285357
subprocess.Popen.communicate = sentry_patched_popen_communicate # type: ignore

sentry_sdk/traces.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
)
3131

3232
if TYPE_CHECKING:
33-
from typing import Any, Callable, Optional, ParamSpec, TypeVar, Union
33+
from typing import Any, Callable, Iterator, Optional, ParamSpec, TypeVar, Union
3434
from sentry_sdk._types import Attributes, AttributeValue, SamplingContext
3535
from sentry_sdk.scope import Scope
3636

@@ -40,6 +40,9 @@
4040

4141
FLAGS_CAPACITY = 10
4242

43+
BAGGAGE_HEADER_NAME = "baggage"
44+
SENTRY_TRACE_HEADER_NAME = "sentry-trace"
45+
4346

4447
class SpanStatus(str, Enum):
4548
OK = "ok"
@@ -362,7 +365,7 @@ def start(self):
362365
"""
363366
self.__enter__()
364367

365-
def end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None:
368+
def finish(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None:
366369
"""
367370
Finish this span and queue it for sending.
368371
@@ -455,18 +458,12 @@ def set_flag(self, flag: str, result: bool) -> None:
455458
self._flags[flag] = result
456459

457460
def set_op(self, op: "Union[OP, str]") -> None:
458-
if isinstance(op, Enum):
459-
op = op.value
460-
461461
self.set_attribute("sentry.op", op)
462462

463463
def set_origin(self, origin: str) -> None:
464464
self.set_attribute("sentry.origin", origin)
465465

466-
def set_source(self, source: "Union[SegmentSource, str]") -> None:
467-
if isinstance(source, Enum):
468-
source = source.value
469-
466+
def set_source(self, source: "SegmentSource") -> None:
470467
self.set_attribute("sentry.span.source", source)
471468

472469
def is_segment(self) -> bool:
@@ -497,7 +494,7 @@ def sampled(self) -> "Optional[bool]":
497494
return self._sampled
498495

499496
def dynamic_sampling_context(self) -> "dict[str, str]":
500-
return self.segment._get_baggage().dynamic_sampling_context()
497+
return self.segment.get_baggage().dynamic_sampling_context()
501498

502499
def to_traceparent(self) -> str:
503500
if self.sampled is True:
@@ -515,9 +512,19 @@ def to_traceparent(self) -> str:
515512

516513
def to_baggage(self) -> "Optional[Baggage]":
517514
if self.segment:
518-
return self.segment._get_baggage()
515+
return self.segment.get_baggage()
519516
return None
520517

518+
def iter_headers(self) -> "Iterator[tuple[str, str]]":
519+
if not self.segment:
520+
return
521+
522+
yield SENTRY_TRACE_HEADER_NAME, self.to_traceparent()
523+
524+
baggage = self.segment.get_baggage().serialize()
525+
if baggage:
526+
yield BAGGAGE_HEADER_NAME, baggage
527+
521528
def _update_active_thread(self) -> None:
522529
thread_id, thread_name = get_current_thread_meta()
523530
self._set_thread(thread_id, thread_name)
@@ -543,7 +550,7 @@ def set_http_status(self, http_status: int) -> None:
543550
else:
544551
self.set_status(SpanStatus.OK)
545552

546-
def _get_baggage(self) -> "Baggage":
553+
def get_baggage(self) -> "Baggage":
547554
"""
548555
Return the :py:class:`~sentry_sdk.tracing_utils.Baggage` associated with
549556
the segment.

sentry_sdk/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from collections import namedtuple
1515
from datetime import datetime, timezone
1616
from decimal import Decimal
17+
from enum import Enum
1718
from functools import partial, partialmethod, wraps
1819
from numbers import Real
1920
from urllib.parse import parse_qs, unquote, urlencode, urlsplit, urlunsplit
@@ -2059,6 +2060,9 @@ def format_attribute(val: "Any") -> "AttributeValue":
20592060
they're serialized further into the actual format the protocol expects:
20602061
https://develop.sentry.dev/sdk/telemetry/attributes/
20612062
"""
2063+
if isinstance(val, Enum):
2064+
val = val.value
2065+
20622066
if isinstance(val, (bool, int, float, str)):
20632067
return val
20642068

0 commit comments

Comments
 (0)