Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a3f7747
feat(stdlib): Support span streaming
alexander-alderman-webb Apr 28, 2026
fb549ba
test(stdlib): Remove mocks in outgoing trace header tests
alexander-alderman-webb Apr 28, 2026
945394d
merge
alexander-alderman-webb Apr 28, 2026
694114f
wip tests
alexander-alderman-webb Apr 28, 2026
42d8c73
.
alexander-alderman-webb Apr 28, 2026
518e3c9
merge
alexander-alderman-webb Apr 28, 2026
be7e413
remove print
alexander-alderman-webb Apr 28, 2026
1c9f953
test(stdlib): Overwrite timestamps in getresponse instead of putrequest
alexander-alderman-webb Apr 28, 2026
e889d0e
merge
alexander-alderman-webb Apr 28, 2026
22b2886
update tests
alexander-alderman-webb Apr 28, 2026
b732d86
test iteration
alexander-alderman-webb Apr 28, 2026
1c549fb
remove reason attribute
alexander-alderman-webb Apr 28, 2026
5b3406a
parameterize tests
alexander-alderman-webb Apr 28, 2026
debbdda
cleanup
alexander-alderman-webb Apr 28, 2026
b15b7b0
adjust outgoing trace test
alexander-alderman-webb Apr 28, 2026
72b7c62
subprocess tests
alexander-alderman-webb Apr 28, 2026
c39c549
stdlib integration
alexander-alderman-webb Apr 28, 2026
a0c8bc7
use documented url attributes
alexander-alderman-webb Apr 28, 2026
d688f01
add type hint
alexander-alderman-webb Apr 28, 2026
50fe59b
use http.request.method
alexander-alderman-webb Apr 28, 2026
1cbd5e1
update
alexander-alderman-webb Apr 28, 2026
ef94687
flip order in if-elif
alexander-alderman-webb Apr 29, 2026
346d124
add tests again
alexander-alderman-webb Apr 29, 2026
9a54013
add len assertion
alexander-alderman-webb Apr 29, 2026
224d4a4
update span name
alexander-alderman-webb Apr 29, 2026
1106fe4
use non-deprecated function
alexander-alderman-webb Apr 29, 2026
5c0a875
set request source attributes before finishing span
alexander-alderman-webb Apr 29, 2026
771e846
remove double init
alexander-alderman-webb Apr 29, 2026
f64437c
update description
alexander-alderman-webb Apr 29, 2026
347101f
fix code source tests
alexander-alderman-webb Apr 29, 2026
bc1f32d
merge master
alexander-alderman-webb Apr 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,12 @@ class SPANDATA:
Example: "tcp", "udp", "unix"
"""

PROCESS_PID = "process.pid"
"""
The process ID of the running process.
Example: 12345
"""

PROFILER_ID = "profiler_id"
"""
Label identifying the profiler id that the span occurred in. This should be a string.
Expand Down Expand Up @@ -924,6 +930,24 @@ class SPANDATA:
Example: "MainThread"
"""

URL_FULL = "url.full"
"""
The URL of the resource that was fetched.
Example: "https://example.com/test?foo=bar#buzz"
"""

URL_FRAGMENT = "url.fragment"
"""
The fragments present in the URI. Note that this does not contain the leading # character, while the `http.fragment` attribute does.
Example: "details"
"""

URL_QUERY = "url.query"
"""
The query string present in the URL. Note that this does not contain the leading ? character, while the `http.query` attribute does.
Example: "foo=bar&bar=baz"
"""

MCP_TOOL_NAME = "mcp.tool.name"
"""
The name of the MCP tool being called.
Expand Down
153 changes: 116 additions & 37 deletions sentry_sdk/integrations/stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
from sentry_sdk.consts import OP, SPANDATA
from sentry_sdk.integrations import Integration
from sentry_sdk.scope import add_global_event_processor
from sentry_sdk.tracing import Span
from sentry_sdk.traces import StreamedSpan
from sentry_sdk.tracing_utils import (
EnvironHeaders,
should_propagate_trace,
add_http_request_source,
has_span_streaming_enabled,
)
from sentry_sdk.utils import (
SENSITIVE_DATA_SUBSTITUTE,
Expand All @@ -31,6 +34,7 @@
from typing import Dict
from typing import Optional
from typing import List
from typing import Union

from sentry_sdk._types import Event, Hint

Expand Down Expand Up @@ -99,22 +103,46 @@ def putrequest(
with capture_internal_exceptions():
parsed_url = parse_url(real_url, sanitize=False)

span = sentry_sdk.start_span(
op=OP.HTTP_CLIENT,
name="%s %s"
% (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE),
origin="auto.http.stdlib.httplib",
)
span.set_data(SPANDATA.HTTP_METHOD, method)
if parsed_url is not None:
span.set_data("url", parsed_url.url)
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
span_streaming = has_span_streaming_enabled(client.options)
span: "Union[Span, StreamedSpan]"
if span_streaming:
span = sentry_sdk.traces.start_span(
name="%s %s"
% (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE),
attributes={
"sentry.origin": "auto.http.stdlib.httplib",
"sentry.op": OP.HTTP_CLIENT,
SPANDATA.HTTP_REQUEST_METHOD: method,
},
)

if parsed_url is not None:
span.set_attribute(SPANDATA.URL_FULL, parsed_url.url)
span.set_attribute(SPANDATA.URL_QUERY, parsed_url.query)
span.set_attribute(SPANDATA.URL_FRAGMENT, parsed_url.fragment)

set_on_span = span.set_attribute

else:
span = sentry_sdk.start_span(
op=OP.HTTP_CLIENT,
name="%s %s"
% (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE),
origin="auto.http.stdlib.httplib",
)

span.set_data(SPANDATA.HTTP_METHOD, method)
if parsed_url is not None:
span.set_data("url", parsed_url.url)
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)

set_on_span = span.set_data

# for proxies, these point to the proxy host/port
if tunnel_host:
span.set_data(SPANDATA.NETWORK_PEER_ADDRESS, self.host)
span.set_data(SPANDATA.NETWORK_PEER_PORT, self.port)
set_on_span(SPANDATA.NETWORK_PEER_ADDRESS, self.host)
set_on_span(SPANDATA.NETWORK_PEER_PORT, self.port)

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

Expand Down Expand Up @@ -145,13 +173,23 @@ def getresponse(self: "HTTPConnection", *args: "Any", **kwargs: "Any") -> "Any":
try:
rv = real_getresponse(self, *args, **kwargs)

span.set_http_status(int(rv.status))
span.set_data("reason", rv.reason)
if isinstance(span, StreamedSpan):
status_code = int(rv.status)
span.status = "error" if status_code >= 400 else "ok"
span.set_attribute("http.response.status_code", status_code)
else:
span.set_http_status(int(rv.status))
span.set_data("reason", rv.reason)
finally:
Comment thread
sentry[bot] marked this conversation as resolved.
span.finish()
if isinstance(span, StreamedSpan):
with capture_internal_exceptions():
add_http_request_source(span)
span.end()
else:
span.finish()
Comment thread
cursor[bot] marked this conversation as resolved.

with capture_internal_exceptions():
add_http_request_source(span)
with capture_internal_exceptions():
add_http_request_source(span)

return rv

Expand Down Expand Up @@ -226,11 +264,24 @@ def sentry_patched_popen_init(

env = None

with sentry_sdk.start_span(
op=OP.SUBPROCESS,
name=description,
origin="auto.subprocess.stdlib.subprocess",
) as span:
span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options)
span: "Union[Span, StreamedSpan]"
if span_streaming:
span = sentry_sdk.traces.start_span(
name=description,
attributes={
"sentry.op": OP.SUBPROCESS,
"sentry.origin": "auto.subprocess.stdlib.subprocess",
},
)
else:
span = sentry_sdk.start_span(
op=OP.SUBPROCESS,
name=description,
origin="auto.subprocess.stdlib.subprocess",
Comment thread
sentry[bot] marked this conversation as resolved.
)
Comment thread
alexander-alderman-webb marked this conversation as resolved.

with span:
for k, v in sentry_sdk.get_current_scope().iter_trace_propagation_headers(
span=span
):
Expand All @@ -244,12 +295,16 @@ def sentry_patched_popen_init(
)
env["SUBPROCESS_" + k.upper().replace("-", "_")] = v

if cwd:
if cwd and isinstance(span, Span):
span.set_data("subprocess.cwd", cwd)

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

span.set_tag("subprocess.pid", self.pid)
if isinstance(span, StreamedSpan):
span.set_attribute(SPANDATA.PROCESS_PID, self.pid)
else:
span.set_tag("subprocess.pid", self.pid)

return rv

subprocess.Popen.__init__ = sentry_patched_popen_init # type: ignore
Expand All @@ -260,12 +315,24 @@ def sentry_patched_popen_init(
def sentry_patched_popen_wait(
self: "subprocess.Popen[Any]", *a: "Any", **kw: "Any"
) -> "Any":
with sentry_sdk.start_span(
op=OP.SUBPROCESS_WAIT,
origin="auto.subprocess.stdlib.subprocess",
) as span:
span.set_tag("subprocess.pid", self.pid)
return old_popen_wait(self, *a, **kw)
span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options)
if span_streaming:
with sentry_sdk.traces.start_span(
name=OP.SUBPROCESS_WAIT,
attributes={
Comment thread
sentry[bot] marked this conversation as resolved.
"sentry.op": OP.SUBPROCESS_WAIT,
"sentry.origin": "auto.subprocess.stdlib.subprocess",
},
) as span:
span.set_attribute(SPANDATA.PROCESS_PID, self.pid)
return old_popen_wait(self, *a, **kw)
else:
with sentry_sdk.start_span(
op=OP.SUBPROCESS_WAIT,
origin="auto.subprocess.stdlib.subprocess",
) as span:
span.set_tag("subprocess.pid", self.pid)
return old_popen_wait(self, *a, **kw)

subprocess.Popen.wait = sentry_patched_popen_wait # type: ignore

Expand All @@ -275,12 +342,24 @@ def sentry_patched_popen_wait(
def sentry_patched_popen_communicate(
self: "subprocess.Popen[Any]", *a: "Any", **kw: "Any"
) -> "Any":
with sentry_sdk.start_span(
op=OP.SUBPROCESS_COMMUNICATE,
origin="auto.subprocess.stdlib.subprocess",
) as span:
span.set_tag("subprocess.pid", self.pid)
return old_popen_communicate(self, *a, **kw)
span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options)
if span_streaming:
with sentry_sdk.traces.start_span(
name=OP.SUBPROCESS_COMMUNICATE,
attributes={
"sentry.op": OP.SUBPROCESS_COMMUNICATE,
"sentry.origin": "auto.subprocess.stdlib.subprocess",
},
) as span:
span.set_attribute(SPANDATA.PROCESS_PID, self.pid)
return old_popen_communicate(self, *a, **kw)
else:
with sentry_sdk.start_span(
op=OP.SUBPROCESS_COMMUNICATE,
origin="auto.subprocess.stdlib.subprocess",
) as span:
span.set_tag("subprocess.pid", self.pid)
return old_popen_communicate(self, *a, **kw)

subprocess.Popen.communicate = sentry_patched_popen_communicate # type: ignore

Expand Down
Loading
Loading