def test_notify_error(self): ab = Airbrake(project_id=1234, api_key='fake') try: raise TypeError except Exception as e: exc_info = sys.exc_info() error = Error(exc_info=exc_info) user = {"id": "12345", "name": "root", "email": "*****@*****.**"} notice = ab.build_notice(error, user=user) exception_str = type(e).__name__ exception_type = type(e).__name__ expected_payload = self.get_expected_payload( exception_str, exception_type) data = { 'type': exc_info[1].__class__.__name__, 'backtrace': format_backtrace(exc_info[2]), 'message': pytb_lastline(exc_info) } expected_payload['errors'] = [data] expected_payload['context']['user'] = user expected_payload['context']['severity'] = ErrorLevels.DEFAULT_LEVEL self.assertEqual(expected_payload, notice.payload) with mock.patch('requests.post') as requests_post: ab.notify(error) data = json.loads(requests_post.call_args[1]['data']) self.assertEqual(expected_payload, data)
def test_notice_error_system_exit(self): try: sys.exit() except SystemExit: # SystemExit will be passed up the chain with self.assertRaisesRegexp(TypeError, "unsupported 'exc_info' type"): exc_info = sys.exc_info() Error(exc_info=exc_info) self.fail("Should have failed to create Error")
def build_error(exc_info=None, message=None, filename=None, line=None, function=None, errtype=None, **params): """Build an error instance with the current exception.""" if not utils.is_exc_info_tuple(exc_info) or not exc_info: exc_info = sys.exc_info() raw_frames = traceback.extract_tb(exc_info[2]) exc_frame = raw_frames[0] return Error(exc_info=exc_info, filename=filename or str(exc_frame[0]), line=line or str(exc_frame[1]), function=function or str(exc_frame[2]), message=message or str(exc_frame[3]), errtype=errtype or "ERROR:%s" % str(exc_frame[0]), **params)
def test_create_notice_error(self): try: raise TypeError except: exc_info = sys.exc_info() error = Error(exc_info=exc_info, severity=ErrorLevels.WARNING) notice = Notice(error) data = { 'type': exc_info[1].__class__.__name__, 'backtrace': format_backtrace(exc_info[2]), 'message': pytb_lastline(exc_info), 'severity': ErrorLevels.WARNING } expected_payload = {'errors': [data]} self.assertEqual(expected_payload, notice.payload)
def test_create_notice_error(self): try: raise TypeError except TypeError: exc_info = sys.exc_info() error = Error(exc_info=exc_info) notice = Notice(error) data = { 'type': exc_info[1].__class__.__name__, 'backtrace': format_backtrace(exc_info[2]), 'message': pytb_lastline(exc_info) } expected_payload = { 'errors': [data], 'context': { 'severity': ErrorLevels.DEFAULT_LEVEL }, } self.assertEqual(expected_payload, notice.payload)
def test_capture(self): ab = Airbrake(project_id=1234, api_key='fake') try: raise ValueError("oh nos") except Exception: with mock.patch('requests.post') as requests_post: ab.capture() exc_info = sys.exc_info() raw_frames = traceback.extract_tb(exc_info[2]) exc_frame = raw_frames[0] exception_str = exc_frame[3] exception_type = "ERROR:%s" % exc_frame[0] expected_payload = self.get_expected_payload( exception_str, exception_type) data = { 'type': exc_info[1].__class__.__name__, 'backtrace': format_backtrace(exc_info[2]), 'message': str(exc_frame[3]) } expected_payload['errors'] = [data] expected_payload['context']['severity'] =\ ErrorLevels.DEFAULT_LEVEL err = Error(exc_info=exc_info, filename=str(exc_frame[0]), line=str(exc_frame[1]), function=str(exc_frame[2]), message=str(exc_frame[3]), errtype="ERROR:%s" % str(exc_frame[0])) notice = ab.build_notice(err) self.assertEqual(expected_payload, notice.payload) data = json.loads(requests_post.call_args[1]['data']) self.assertEqual(expected_payload, data)
def log(self, exc_info=None, message=None, filename=None, line=None, function=None, errtype=None, **params): """Acknowledge an error and post it to airbrake.io. :param exc_info: Exception tuple to use for formatting request. If None, sys.exc_info() will be read to get exception info. To prevent the reading of sys.exc_info(), set exc_info to False. :param message: Message accompanying error. :param filename: Name of file where error occurred. :param line: Line number in file where error occurred. :param function: Function name where error occurred. :param errtype: Type of error which occurred. :param params: Payload field "params" which may contain any other context related to the exception(s). Exception info willl be read from sys.exc_info() if it is not supplied. To prevent this behavior, pass exc_info=False. """ if not utils.is_exc_info_tuple(exc_info): # compatibility, allows exc_info not to be a exc tuple errmessage = None if isinstance(exc_info, Exception): errmessage = utils.pytb_lastline(exc_info) elif exc_info: try: errmessage = json.dumps(exc_info) except (ValueError, TypeError): errmessage = str(exc_info) if errmessage: # this way, if exc_info kwarg is passed to logger method, # its value will be available in params params['exc_info'] = errmessage if message and errmessage and message != errmessage: message = "%s | %s" % (message, errmessage) elif errmessage: message = errmessage # read the global exception stack, but # be sure not to read the same exception twice. # This can happen for a number of reasons: # - the exception context has not been # cleared or garbage collected # - the airbrake logging handler is being used # to log exceptions and error messages in the same # thread, but that thread is not actually logging # some exceptions, or is not clearing the exception # context via sys.exc_clear() # the alternative is to clear the exception stack every time, # which this module *should not* take the liberty to do # # to prevent this function from reading the global # exception context *at all*, pass exc_info=False if exc_info is False: exc_info = None, None, None else: exc_info = sys.exc_info() # dont ship the same exception instance twice if exc_info[1] in self._exc_queue: exc_info = None, None, None self._exc_queue.put(exc_info[1]) severity = params.get("severity", None) error = Error(exc_info=exc_info, message=message, filename=filename, line=line, function=function, errtype=errtype, severity=severity) environment = params.pop('environment', {}) session = params.pop('session', {}) notice = self.build_notice(error, params, session, environment) return self.notify(notice)
def uncaught_handler(self, exception_class, exception, trace): """Catch uncaught exceptions and send to airbrake.""" exc_info = (exception_class, exception, trace) error = Error(exc_info=exc_info) self.notify(error) self.excepthook(*exc_info)