summaryrefslogtreecommitdiff
path: root/nova/notifications
diff options
context:
space:
mode:
authorStephen Finucane <stephenfin@redhat.com>2020-06-04 12:13:56 +0100
committerStephen Finucane <stephenfin@redhat.com>2020-06-08 14:38:33 +0100
commit125df26bf9d6b4cfbfb68770fa47f2055f29b8dc (patch)
treea7d95a2be41f094edb796d426832dbd1317e9203 /nova/notifications
parent03b00ae02fede4ee7f347001f50baab1d79ffa0a (diff)
downloadnova-125df26bf9d6b4cfbfb68770fa47f2055f29b8dc.tar.gz
Use 'Exception.__traceback__' for versioned notifications
The 'inspect.trace()' function is expected to be called within the context of an exception handler. The 'from_exc_and_traceback' class method of the 'nova.notification.objects.exception.ExceptionPayload' class uses this to get information about a provided exception, however, there are cases where this is called from outside of an exception handler. In these cases, we see an 'IndexError' since we can't get the last frame of a non-existent stacktrace. The solution to this is to fallback to using the traceback embedded in the exception. This is a bit lossy when decorators are involved but for all other cases this will give us the same information. This also allows us to avoid passing a traceback argument to the function since we have it to hand already. Change-Id: I404ca316b1bf2a963106cd34e927934befbd9b12 Signed-off-by: Stephen Finucane <stephenfin@redhat.com> Closes-Bug: #1881455
Diffstat (limited to 'nova/notifications')
-rw-r--r--nova/notifications/objects/exception.py41
1 files changed, 30 insertions, 11 deletions
diff --git a/nova/notifications/objects/exception.py b/nova/notifications/objects/exception.py
index 6ebb927b27..5e50221130 100644
--- a/nova/notifications/objects/exception.py
+++ b/nova/notifications/objects/exception.py
@@ -9,9 +9,9 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-import inspect
-import six
+import inspect
+import traceback as tb
from nova.notifications.objects import base
from nova.objects import base as nova_base
@@ -41,19 +41,38 @@ class ExceptionPayload(base.NotificationPayloadBase):
self.traceback = traceback
@classmethod
- def from_exc_and_traceback(cls, fault, traceback):
- trace = inspect.trace()[-1]
+ def from_exception(cls, fault: Exception):
+ traceback = fault.__traceback__
+
+ # NOTE(stephenfin): inspect.trace() will only return something if we're
+ # inside the scope of an exception handler. If we are not, we fallback
+ # to extracting information from the traceback. This is lossy, since
+ # the stack stops at the exception handler, not the exception raise.
+ # Check the inspect docs for more information.
+ #
+ # https://docs.python.org/3/library/inspect.html#types-and-members
+ trace = inspect.trace()
+ if trace:
+ module = inspect.getmodule(trace[-1][0])
+ function_name = trace[-1][3]
+ else:
+ module = inspect.getmodule(traceback)
+ function_name = traceback.tb_frame.f_code.co_name
+
+ module_name = module.__name__ if module else 'unknown'
+
# TODO(gibi): apply strutils.mask_password on exception_message and
# consider emitting the exception_message only if the safe flag is
# true in the exception like in the REST API
- module = inspect.getmodule(trace[0])
- module_name = module.__name__ if module else 'unknown'
return cls(
- function_name=trace[3],
- module_name=module_name,
- exception=fault.__class__.__name__,
- exception_message=six.text_type(fault),
- traceback=traceback)
+ function_name=function_name,
+ module_name=module_name,
+ exception=fault.__class__.__name__,
+ exception_message=str(fault),
+ # NOTE(stephenfin): the first argument to format_exception is
+ # ignored since Python 3.5
+ traceback=','.join(tb.format_exception(None, fault, traceback)),
+ )
@base.notification_sample('compute-exception.json')