@@ -126,6 +126,13 @@ def response_hook(span, req, resp):
126
126
127
127
_response_propagation_setter = FuncSetter (falcon .Response .append_header )
128
128
129
+ if hasattr (falcon , "App" ):
130
+ # Falcon 3
131
+ _instrument_app = "App"
132
+ else :
133
+ # Falcon 2
134
+ _instrument_app = "API"
135
+
129
136
130
137
class FalconInstrumentor (BaseInstrumentor ):
131
138
# pylint: disable=protected-access,attribute-defined-outside-init
@@ -138,14 +145,16 @@ def instrumentation_dependencies(self) -> Collection[str]:
138
145
return _instruments
139
146
140
147
def _instrument (self , ** kwargs ):
141
- self ._original_falcon_api = falcon .API
142
- falcon .API = partial (_InstrumentedFalconAPI , ** kwargs )
148
+ self ._original_falcon_api = getattr (falcon , _instrument_app )
149
+ setattr (
150
+ falcon , _instrument_app , partial (_InstrumentedFalconAPI , ** kwargs )
151
+ )
143
152
144
153
def _uninstrument (self , ** kwargs ):
145
- falcon . API = self ._original_falcon_api
154
+ setattr ( falcon , _instrument_app , self ._original_falcon_api )
146
155
147
156
148
- class _InstrumentedFalconAPI (falcon . API ):
157
+ class _InstrumentedFalconAPI (getattr ( falcon , _instrument_app ) ):
149
158
def __init__ (self , * args , ** kwargs ):
150
159
# inject trace middleware
151
160
middlewares = kwargs .pop ("middleware" , [])
@@ -169,6 +178,15 @@ def __init__(self, *args, **kwargs):
169
178
self ._excluded_urls = get_excluded_urls ("FALCON" )
170
179
super ().__init__ (* args , ** kwargs )
171
180
181
+ def _handle_exception (
182
+ self , req , resp , ex , params
183
+ ): # pylint: disable=C0103
184
+ # Falcon 3 does not execute middleware within the context of the exception
185
+ # so we capture the exception here and save it into the env dict
186
+ _ , exc , _ = exc_info ()
187
+ req .env [_ENVIRON_EXC ] = exc
188
+ return super ()._handle_exception (req , resp , ex , params )
189
+
172
190
def __call__ (self , env , start_response ):
173
191
# pylint: disable=E1101
174
192
if self ._excluded_urls .url_disabled (env .get ("PATH_INFO" , "/" )):
@@ -193,7 +211,6 @@ def __call__(self, env, start_response):
193
211
env [_ENVIRON_ACTIVATION_KEY ] = activation
194
212
195
213
def _start_response (status , response_headers , * args , ** kwargs ):
196
- otel_wsgi .add_response_attributes (span , status , response_headers )
197
214
response = start_response (
198
215
status , response_headers , * args , ** kwargs
199
216
)
@@ -264,29 +281,32 @@ def process_response(
264
281
if resource is None :
265
282
status = "404"
266
283
reason = "NotFound"
267
-
268
- exc_type , exc , _ = exc_info ()
269
- if exc_type and not req_succeeded :
270
- if "HTTPNotFound" in exc_type .__name__ :
271
- status = "404"
272
- reason = "NotFound"
284
+ else :
285
+ if _ENVIRON_EXC in req .env :
286
+ exc = req .env [_ENVIRON_EXC ]
287
+ exc_type = type (exc )
273
288
else :
274
- status = "500"
275
- reason = "{}: {}" .format (exc_type .__name__ , exc )
289
+ exc_type , exc = None , None
290
+ if exc_type and not req_succeeded :
291
+ if "HTTPNotFound" in exc_type .__name__ :
292
+ status = "404"
293
+ reason = "NotFound"
294
+ else :
295
+ status = "500"
296
+ reason = "{}: {}" .format (exc_type .__name__ , exc )
276
297
277
298
status = status .split (" " )[0 ]
278
299
try :
279
300
status_code = int (status )
280
- except ValueError :
281
- pass
282
- finally :
283
301
span .set_attribute (SpanAttributes .HTTP_STATUS_CODE , status_code )
284
302
span .set_status (
285
303
Status (
286
304
status_code = http_status_to_status_code (status_code ),
287
305
description = reason ,
288
306
)
289
307
)
308
+ except ValueError :
309
+ pass
290
310
291
311
propagator = get_global_response_propagator ()
292
312
if propagator :
0 commit comments