77from sentry_sdk .scope import should_send_default_pii
88from sentry_sdk .traces import StreamedSpan
99from sentry_sdk .tracing import Span
10- from sentry_sdk .tracing_utils import has_span_streaming_enabled
10+ from sentry_sdk .tracing_utils import add_http_breadcrumb , has_span_streaming_enabled
1111from sentry_sdk .utils import (
1212 capture_internal_exceptions ,
1313 parse_url ,
@@ -64,54 +64,83 @@ def _sentry_request_created(
6464 if client .get_integration (Boto3Integration ) is None :
6565 return
6666
67+ parsed_url = None
68+ if request .url is not None :
69+ with capture_internal_exceptions ():
70+ parsed_url = parse_url (request .url , sanitize = False )
71+
72+ breadcrumb : "dict[str, Any]" = {}
73+
6774 is_span_streaming_enabled = has_span_streaming_enabled (client .options )
68- span : "Union[Span, StreamedSpan]"
75+ span : "Union[Span, StreamedSpan, None]" = None
6976 if is_span_streaming_enabled :
70- if sentry_sdk .traces .get_current_span () is None :
71- return
72- span = sentry_sdk .traces .start_span (
73- name = description ,
74- attributes = {
75- "sentry.op" : OP .HTTP_CLIENT ,
76- "sentry.origin" : Boto3Integration .origin ,
77- SPANDATA .RPC_METHOD : f"{ service_id } /{ operation_name } " ,
78- },
79- )
80- if request .url is not None and should_send_default_pii ():
81- with capture_internal_exceptions ():
82- parsed_url = parse_url (request .url , sanitize = False )
83- span .set_attribute (SPANDATA .URL_FULL , parsed_url .url )
84- span .set_attribute (SPANDATA .URL_QUERY , parsed_url .query )
85- span .set_attribute (SPANDATA .URL_FRAGMENT , parsed_url .fragment )
77+ if parsed_url and should_send_default_pii ():
78+ breadcrumb .update (
79+ {
80+ SPANDATA .URL_FULL : parsed_url .url ,
81+ SPANDATA .URL_QUERY : parsed_url .query ,
82+ SPANDATA .URL_FRAGMENT : parsed_url .fragment ,
83+ }
84+ )
8685
8786 if request .method is not None :
88- span .set_attribute (SPANDATA .HTTP_REQUEST_METHOD , request .method )
87+ breadcrumb [SPANDATA .HTTP_REQUEST_METHOD ] = request .method
88+
89+ if sentry_sdk .traces .get_current_span () is not None :
90+ span = sentry_sdk .traces .start_span (
91+ name = description ,
92+ attributes = {
93+ "sentry.op" : OP .HTTP_CLIENT ,
94+ "sentry.origin" : Boto3Integration .origin ,
95+ SPANDATA .RPC_METHOD : f"{ service_id } /{ operation_name } " ,
96+ },
97+ )
98+ if parsed_url and should_send_default_pii ():
99+ span .set_attributes (
100+ {
101+ SPANDATA .URL_FULL : parsed_url .url ,
102+ SPANDATA .URL_QUERY : parsed_url .query ,
103+ SPANDATA .URL_FRAGMENT : parsed_url .fragment ,
104+ }
105+ )
106+
107+ if request .method is not None :
108+ span .set_attribute (SPANDATA .HTTP_REQUEST_METHOD , request .method )
89109 else :
90110 span = sentry_sdk .start_span (
91111 op = OP .HTTP_CLIENT ,
92112 name = description ,
93113 origin = Boto3Integration .origin ,
94114 )
95115
96- if request .url is not None :
97- with capture_internal_exceptions ():
98- parsed_url = parse_url (request .url , sanitize = False )
99- span .set_data ("aws.request.url" , parsed_url .url )
100- span .set_data (SPANDATA .HTTP_QUERY , parsed_url .query )
101- span .set_data (SPANDATA .HTTP_FRAGMENT , parsed_url .fragment )
116+ if parsed_url :
117+ span .set_data ("aws.request.url" , parsed_url .url )
118+ span .set_data (SPANDATA .HTTP_QUERY , parsed_url .query )
119+ span .set_data (SPANDATA .HTTP_FRAGMENT , parsed_url .fragment )
120+ breadcrumb .update (
121+ {
122+ "aws.request.url" : parsed_url .url ,
123+ SPANDATA .HTTP_QUERY : parsed_url .query ,
124+ SPANDATA .HTTP_FRAGMENT : parsed_url .fragment ,
125+ }
126+ )
102127
103128 span .set_tag ("aws.service_id" , service_id .hyphenize ())
104129 span .set_tag ("aws.operation_name" , operation_name )
105130 if request .method is not None :
106131 span .set_data (SPANDATA .HTTP_METHOD , request .method )
132+ breadcrumb [SPANDATA .HTTP_METHOD ] = request .method
107133
108- # We do it in order for subsequent http calls/retries be
109- # attached to this span.
110- span .__enter__ ()
134+ # We do it in order for subsequent http calls/retries be
135+ # attached to this span.
136+ span .__enter__ ()
111137
112- # request.context is an open-ended data-structure
113- # where we can add anything useful in request life cycle.
114- request .context ["_sentrysdk_span" ] = span
138+ add_http_breadcrumb (None , breadcrumb )
139+
140+ if span is not None :
141+ # request.context is an open-ended data-structure
142+ # where we can add anything useful in request life cycle.
143+ request .context ["_sentrysdk_span" ] = span
115144
116145
117146def _sentry_after_call (
@@ -122,6 +151,7 @@ def _sentry_after_call(
122151 # Span could be absent if the integration is disabled.
123152 if span is None :
124153 return
154+
125155 span .__exit__ (None , None , None )
126156
127157 body = parsed .get ("Body" )
@@ -186,4 +216,5 @@ def _sentry_after_call_error(
186216 # Span could be absent if the integration is disabled.
187217 if span is None :
188218 return
219+
189220 span .__exit__ (type (exception ), exception , None )
0 commit comments