11import sentry_sdk
2- from sentry_sdk import start_span
32from sentry_sdk .consts import OP , SPANDATA
43from sentry_sdk .integrations import Integration , DidNotEnable
54from sentry_sdk .tracing import BAGGAGE_HEADER_NAME
65from sentry_sdk .tracing_utils import (
7- should_propagate_trace ,
86 add_http_request_source ,
7+ should_propagate_trace ,
98 add_sentry_baggage_to_headers ,
9+ has_span_streaming_enabled ,
1010)
1111from sentry_sdk .utils import (
1212 SENSITIVE_DATA_SUBSTITUTE ,
2020
2121if TYPE_CHECKING :
2222 from typing import Any
23+ from sentry_sdk ._types import Attributes
2324
2425
2526try :
@@ -49,48 +50,102 @@ def _install_httpx_client() -> None:
4950
5051 @ensure_integration_enabled (HttpxIntegration , real_send )
5152 def send (self : "Client" , request : "Request" , ** kwargs : "Any" ) -> "Response" :
53+ client = sentry_sdk .get_client ()
54+ is_span_streaming_enabled = has_span_streaming_enabled (client .options )
55+
5256 parsed_url = None
5357 with capture_internal_exceptions ():
5458 parsed_url = parse_url (str (request .url ), sanitize = False )
5559
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
60+ if is_span_streaming_enabled :
61+ with sentry_sdk .traces .start_span (
62+ name = "%s %s"
63+ % (
64+ request .method ,
65+ parsed_url .url if parsed_url else SENSITIVE_DATA_SUBSTITUTE ,
66+ ),
67+ attributes = {
68+ "sentry.op" : OP .HTTP_CLIENT ,
69+ "sentry.origin" : HttpxIntegration .origin ,
70+ "http.request.method" : request .method ,
71+ },
72+ ) as streamed_span :
73+ attributes : "Attributes" = {}
74+
75+ if parsed_url is not None :
76+ attributes ["url.full" ] = parsed_url .url
77+ if parsed_url .query :
78+ attributes ["url.query" ] = parsed_url .query
79+ if parsed_url .fragment :
80+ attributes ["url.fragment" ] = parsed_url .fragment
81+
82+ if should_propagate_trace (client , str (request .url )):
83+ for (
84+ key ,
85+ value ,
86+ ) in (
87+ sentry_sdk .get_current_scope ().iter_trace_propagation_headers ()
88+ ):
89+ logger .debug (
90+ f"[Tracing] Adding `{ key } ` header { value } to outgoing request to { request .url } ."
7991 )
80- )
8192
82- if key == BAGGAGE_HEADER_NAME :
83- add_sentry_baggage_to_headers (request .headers , value )
84- else :
85- request .headers [key ] = value
93+ if key == BAGGAGE_HEADER_NAME :
94+ add_sentry_baggage_to_headers (request .headers , value )
95+ else :
96+ request .headers [key ] = value
8697
87- rv = real_send (self , request , ** kwargs )
98+ try :
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+ streamed_span .status = "error" if rv .status_code >= 400 else "ok"
102+ attributes ["http.response.status_code" ] = rv .status_code
103+ finally :
104+ streamed_span .set_attributes (attributes )
91105
92- with capture_internal_exceptions ():
93- add_http_request_source (span )
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 (streamed_span )
110+ else :
111+ with sentry_sdk .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,103 @@ 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 sentry_sdk .traces .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+ "http.request.method" : request .method ,
181+ },
182+ ) as streamed_span :
183+ attributes : "Attributes" = {}
184+
185+ if parsed_url is not None :
186+ attributes ["url.full" ] = parsed_url .url
187+ if parsed_url .query :
188+ attributes ["url.query" ] = parsed_url .query
189+ if parsed_url .fragment :
190+ attributes ["url.fragment" ] = parsed_url .fragment
191+
192+ if should_propagate_trace (client , str (request .url )):
193+ for (
194+ key ,
195+ value ,
196+ ) in (
197+ sentry_sdk .get_current_scope ().iter_trace_propagation_headers ()
198+ ):
199+ logger .debug (
200+ f"[Tracing] Adding `{ key } ` header { value } to outgoing request to { request .url } ."
136201 )
137- )
138- if key == BAGGAGE_HEADER_NAME :
139- add_sentry_baggage_to_headers (request .headers , value )
140- else :
141- request .headers [key ] = value
142202
143- rv = await real_send (self , request , ** kwargs )
203+ if key == BAGGAGE_HEADER_NAME :
204+ add_sentry_baggage_to_headers (request .headers , value )
205+ else :
206+ request .headers [key ] = value
144207
145- span . set_http_status ( rv . status_code )
146- span . set_data ( "reason" , rv . reason_phrase )
208+ try :
209+ rv = await real_send ( self , request , ** kwargs )
147210
148- with capture_internal_exceptions ():
149- add_http_request_source (span )
211+ streamed_span .status = "error" if rv .status_code >= 400 else "ok"
212+ attributes ["http.response.status_code" ] = rv .status_code
213+ finally :
214+ streamed_span .set_attributes (attributes )
215+
216+ # Needs to happen within the context manager as we want to attach the
217+ # final data before the span finishes and is sent for ingesting.
218+ with capture_internal_exceptions ():
219+ add_http_request_source (streamed_span )
220+ else :
221+ with sentry_sdk .start_span (
222+ op = OP .HTTP_CLIENT ,
223+ name = "%s %s"
224+ % (
225+ request .method ,
226+ parsed_url .url if parsed_url else SENSITIVE_DATA_SUBSTITUTE ,
227+ ),
228+ origin = HttpxIntegration .origin ,
229+ ) as span :
230+ span .set_data (SPANDATA .HTTP_METHOD , request .method )
231+ if parsed_url is not None :
232+ span .set_data ("url" , parsed_url .url )
233+ span .set_data (SPANDATA .HTTP_QUERY , parsed_url .query )
234+ span .set_data (SPANDATA .HTTP_FRAGMENT , parsed_url .fragment )
235+
236+ if should_propagate_trace (client , str (request .url )):
237+ for (
238+ key ,
239+ value ,
240+ ) in (
241+ sentry_sdk .get_current_scope ().iter_trace_propagation_headers ()
242+ ):
243+ logger .debug (
244+ f"[Tracing] Adding `{ key } ` header { value } to outgoing request to { request .url } ."
245+ )
246+ if key == BAGGAGE_HEADER_NAME :
247+ add_sentry_baggage_to_headers (request .headers , value )
248+ else :
249+ request .headers [key ] = value
250+
251+ rv = await real_send (self , request , ** kwargs )
252+
253+ span .set_http_status (rv .status_code )
254+ span .set_data ("reason" , rv .reason_phrase )
255+
256+ with capture_internal_exceptions ():
257+ add_http_request_source (span )
150258
151259 return rv
152260
0 commit comments