11import sentry_sdk
2- from sentry_sdk import start_span
2+ from sentry_sdk import start_span as legacy_start_span
33from sentry_sdk .consts import OP , SPANDATA
44from sentry_sdk .integrations import Integration , DidNotEnable
5+ from sentry_sdk .traces import start_span
56from sentry_sdk .tracing import BAGGAGE_HEADER_NAME
67from sentry_sdk .tracing_utils import (
8+ add_http_request_source_for_streamed_span ,
79 should_propagate_trace ,
810 add_http_request_source ,
911 add_sentry_baggage_to_headers ,
12+ has_span_streaming_enabled ,
1013)
1114from sentry_sdk .utils import (
1215 SENSITIVE_DATA_SUBSTITUTE ,
2023
2124if TYPE_CHECKING :
2225 from typing import Any
26+ from sentry_sdk ._types import Attributes
2327
2428
2529try :
@@ -49,48 +53,99 @@ def _install_httpx_client() -> None:
4953
5054 @ensure_integration_enabled (HttpxIntegration , real_send )
5155 def send (self : "Client" , request : "Request" , ** kwargs : "Any" ) -> "Response" :
56+ client = sentry_sdk .get_client ()
57+ is_span_streaming_enabled = has_span_streaming_enabled (client .options )
58+
5259 parsed_url = None
5360 with capture_internal_exceptions ():
5461 parsed_url = parse_url (str (request .url ), sanitize = False )
5562
56- with start_span (
57- op = OP .HTTP_CLIENT ,
58- name = "%s %s"
59- % (
60- request .method ,
61- parsed_url .url if parsed_url else SENSITIVE_DATA_SUBSTITUTE ,
62- ),
63- origin = HttpxIntegration .origin ,
64- ) as span :
65- span .set_data (SPANDATA .HTTP_METHOD , request .method )
66- if parsed_url is not None :
67- span .set_data ("url" , parsed_url .url )
68- span .set_data (SPANDATA .HTTP_QUERY , parsed_url .query )
69- span .set_data (SPANDATA .HTTP_FRAGMENT , parsed_url .fragment )
70-
71- if should_propagate_trace (sentry_sdk .get_client (), str (request .url )):
72- for (
73- key ,
74- value ,
75- ) in sentry_sdk .get_current_scope ().iter_trace_propagation_headers ():
76- logger .debug (
77- "[Tracing] Adding `{key}` header {value} to outgoing request to {url}." .format (
78- key = key , value = value , url = request .url
63+ if is_span_streaming_enabled :
64+ with start_span (
65+ name = "%s %s"
66+ % (
67+ request .method ,
68+ parsed_url .url if parsed_url else SENSITIVE_DATA_SUBSTITUTE ,
69+ ),
70+ attributes = {
71+ "sentry.op" : OP .HTTP_CLIENT ,
72+ "sentry.origin" : HttpxIntegration .origin ,
73+ SPANDATA .HTTP_METHOD : request .method ,
74+ },
75+ ) as segment :
76+ attributes : "Attributes" = {}
77+
78+ if parsed_url is not None :
79+ attributes ["url" ] = parsed_url .url
80+ attributes [SPANDATA .HTTP_QUERY ] = parsed_url .query
81+ attributes [SPANDATA .HTTP_FRAGMENT ] = parsed_url .fragment
82+
83+ if should_propagate_trace (client , str (request .url )):
84+ for (
85+ key ,
86+ value ,
87+ ) in (
88+ sentry_sdk .get_current_scope ().iter_trace_propagation_headers ()
89+ ):
90+ logger .debug (
91+ f"[Tracing] Adding `{ key } ` header { value } to outgoing request to { request .url } ."
7992 )
80- )
8193
82- if key == BAGGAGE_HEADER_NAME :
83- add_sentry_baggage_to_headers (request .headers , value )
84- else :
85- request .headers [key ] = value
94+ if key == BAGGAGE_HEADER_NAME :
95+ add_sentry_baggage_to_headers (request .headers , value )
96+ else :
97+ request .headers [key ] = value
8698
87- rv = real_send (self , request , ** kwargs )
99+ rv = real_send (self , request , ** kwargs )
88100
89- span . set_http_status ( rv .status_code )
90- span . set_data ( "reason" , rv .reason_phrase )
101+ segment . status = "error" if rv .status_code >= 400 else "ok"
102+ attributes [ SPANDATA . HTTP_STATUS_CODE ] = rv .status_code
91103
92- with capture_internal_exceptions ():
93- add_http_request_source (span )
104+ segment .set_attributes (attributes )
105+
106+ # Needs to happen within the context manager as we want to attach the
107+ # final data before the span finishes and is sent for ingesting.
108+ with capture_internal_exceptions ():
109+ add_http_request_source_for_streamed_span (segment )
110+ else :
111+ with legacy_start_span (
112+ op = OP .HTTP_CLIENT ,
113+ name = "%s %s"
114+ % (
115+ request .method ,
116+ parsed_url .url if parsed_url else SENSITIVE_DATA_SUBSTITUTE ,
117+ ),
118+ origin = HttpxIntegration .origin ,
119+ ) as span :
120+ span .set_data (SPANDATA .HTTP_METHOD , request .method )
121+ if parsed_url is not None :
122+ span .set_data ("url" , parsed_url .url )
123+ span .set_data (SPANDATA .HTTP_QUERY , parsed_url .query )
124+ span .set_data (SPANDATA .HTTP_FRAGMENT , parsed_url .fragment )
125+
126+ if should_propagate_trace (client , str (request .url )):
127+ for (
128+ key ,
129+ value ,
130+ ) in (
131+ sentry_sdk .get_current_scope ().iter_trace_propagation_headers ()
132+ ):
133+ logger .debug (
134+ f"[Tracing] Adding `{ key } ` header { value } to outgoing request to { request .url } ."
135+ )
136+
137+ if key == BAGGAGE_HEADER_NAME :
138+ add_sentry_baggage_to_headers (request .headers , value )
139+ else :
140+ request .headers [key ] = value
141+
142+ rv = real_send (self , request , ** kwargs )
143+
144+ span .set_http_status (rv .status_code )
145+ span .set_data ("reason" , rv .reason_phrase )
146+
147+ with capture_internal_exceptions ():
148+ add_http_request_source (span )
94149
95150 return rv
96151
@@ -103,50 +158,100 @@ def _install_httpx_async_client() -> None:
103158 async def send (
104159 self : "AsyncClient" , request : "Request" , ** kwargs : "Any"
105160 ) -> "Response" :
106- if sentry_sdk .get_client ().get_integration (HttpxIntegration ) is None :
161+ client = sentry_sdk .get_client ()
162+ if client .get_integration (HttpxIntegration ) is None :
107163 return await real_send (self , request , ** kwargs )
108164
165+ is_span_streaming_enabled = has_span_streaming_enabled (client .options )
109166 parsed_url = None
110167 with capture_internal_exceptions ():
111168 parsed_url = parse_url (str (request .url ), sanitize = False )
112169
113- with start_span (
114- op = OP .HTTP_CLIENT ,
115- name = "%s %s"
116- % (
117- request .method ,
118- parsed_url .url if parsed_url else SENSITIVE_DATA_SUBSTITUTE ,
119- ),
120- origin = HttpxIntegration .origin ,
121- ) as span :
122- span .set_data (SPANDATA .HTTP_METHOD , request .method )
123- if parsed_url is not None :
124- span .set_data ("url" , parsed_url .url )
125- span .set_data (SPANDATA .HTTP_QUERY , parsed_url .query )
126- span .set_data (SPANDATA .HTTP_FRAGMENT , parsed_url .fragment )
127-
128- if should_propagate_trace (sentry_sdk .get_client (), str (request .url )):
129- for (
130- key ,
131- value ,
132- ) in sentry_sdk .get_current_scope ().iter_trace_propagation_headers ():
133- logger .debug (
134- "[Tracing] Adding `{key}` header {value} to outgoing request to {url}." .format (
135- key = key , value = value , url = request .url
170+ if is_span_streaming_enabled :
171+ with start_span (
172+ name = "%s %s"
173+ % (
174+ request .method ,
175+ parsed_url .url if parsed_url else SENSITIVE_DATA_SUBSTITUTE ,
176+ ),
177+ attributes = {
178+ "sentry.op" : OP .HTTP_CLIENT ,
179+ "sentry.origin" : HttpxIntegration .origin ,
180+ SPANDATA .HTTP_METHOD : request .method ,
181+ },
182+ ) as segment :
183+ attributes : "Attributes" = {}
184+
185+ if parsed_url is not None :
186+ attributes ["url" ] = parsed_url .url
187+ attributes [SPANDATA .HTTP_QUERY ] = parsed_url .query
188+ attributes [SPANDATA .HTTP_FRAGMENT ] = parsed_url .fragment
189+
190+ if should_propagate_trace (client , str (request .url )):
191+ for (
192+ key ,
193+ value ,
194+ ) in (
195+ sentry_sdk .get_current_scope ().iter_trace_propagation_headers ()
196+ ):
197+ logger .debug (
198+ f"[Tracing] Adding `{ key } ` header { value } to outgoing request to { request .url } ."
136199 )
137- )
138- if key == BAGGAGE_HEADER_NAME :
139- add_sentry_baggage_to_headers (request .headers , value )
140- else :
141- request .headers [key ] = value
142200
143- rv = await real_send (self , request , ** kwargs )
201+ if key == BAGGAGE_HEADER_NAME :
202+ add_sentry_baggage_to_headers (request .headers , value )
203+ else :
204+ request .headers [key ] = value
144205
145- span .set_http_status (rv .status_code )
146- span .set_data ("reason" , rv .reason_phrase )
206+ rv = await real_send (self , request , ** kwargs )
147207
148- with capture_internal_exceptions ():
149- add_http_request_source (span )
208+ segment .status = "error" if rv .status_code >= 400 else "ok"
209+ attributes [SPANDATA .HTTP_STATUS_CODE ] = rv .status_code
210+
211+ segment .set_attributes (attributes )
212+
213+ # Needs to happen within the context manager as we want to attach the
214+ # final data before the span finishes and is sent for ingesting.
215+ with capture_internal_exceptions ():
216+ add_http_request_source_for_streamed_span (segment )
217+ else :
218+ with legacy_start_span (
219+ op = OP .HTTP_CLIENT ,
220+ name = "%s %s"
221+ % (
222+ request .method ,
223+ parsed_url .url if parsed_url else SENSITIVE_DATA_SUBSTITUTE ,
224+ ),
225+ origin = HttpxIntegration .origin ,
226+ ) as span :
227+ span .set_data (SPANDATA .HTTP_METHOD , request .method )
228+ if parsed_url is not None :
229+ span .set_data ("url" , parsed_url .url )
230+ span .set_data (SPANDATA .HTTP_QUERY , parsed_url .query )
231+ span .set_data (SPANDATA .HTTP_FRAGMENT , parsed_url .fragment )
232+
233+ if should_propagate_trace (client , str (request .url )):
234+ for (
235+ key ,
236+ value ,
237+ ) in (
238+ sentry_sdk .get_current_scope ().iter_trace_propagation_headers ()
239+ ):
240+ logger .debug (
241+ f"[Tracing] Adding `{ key } ` header { value } to outgoing request to { request .url } ."
242+ )
243+ if key == BAGGAGE_HEADER_NAME :
244+ add_sentry_baggage_to_headers (request .headers , value )
245+ else :
246+ request .headers [key ] = value
247+
248+ rv = await real_send (self , request , ** kwargs )
249+
250+ span .set_http_status (rv .status_code )
251+ span .set_data ("reason" , rv .reason_phrase )
252+
253+ with capture_internal_exceptions ():
254+ add_http_request_source (span )
150255
151256 return rv
152257
0 commit comments