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_notify_context(self): with mock.patch('requests.post') as requests_post: version = platform.python_version() plat = platform.platform() environment = u"testing123" root_directory = u"/home/app/" ab = Airbrake(project_id=1234, api_key='fake', environment=environment, root_directory=root_directory) ab.log("this is a test") expected_context = { u'notifier': { u'name': u'airbrake-python', u'version': __version__, u'url': __url__ }, u'os': plat, u'hostname': socket.gethostname(), u'language': u'Python/%s' % version, u'environment': environment, u'rootDirectory': root_directory, u'severity': ErrorLevels.DEFAULT_LEVEL, } data = json.loads(requests_post.call_args[1]['data']) actual_context = data["context"] self.assertEqual(expected_context, actual_context)
def test_log_with_severity(self): with mock.patch('requests.post') as requests_post: ab = Airbrake(project_id=1234, api_key='fake') ab.log("this is a test", severity=ErrorLevels.CRITICAL) data = json.loads(requests_post.call_args[1]['data']) actual_context = data["context"] self.assertEqual(actual_context[u'severity'], ErrorLevels.CRITICAL)
class AirbrakeHandler(logging.Handler): """A handler class which ships logs to airbrake.io. Requires one: * `project_id` AND `api_key` * an instance of airbrake.Airbrake """ def __init__(self, airbrake=None, level=logging.ERROR, project_id=None, api_key=None, environment=None, base_url=None): """Initialize the Airbrake handler with a default logging level. Default level of logs handled by this class are >= ERROR, which includes ERROR and CRITICAL. To change this behavior supply a different argument for 'level'. """ logging.Handler.__init__(self, level=level) if isinstance(airbrake, Airbrake): self.airbrake = airbrake else: self.airbrake = Airbrake(project_id, api_key, environment, base_url) def emit(self, record): """Log the record airbrake.io style. To prevent method calls which invoke this handler from using the global exception context in sys.exc_info(), exc_info must be passed as False. E.g. To prevent AirbrakeHandler from reading the global exception context, (which it may do to find a traceback and error type), make logger method calls like this: LOG.error("Bad math.", exc_info=False) Otherwise, provide exception context directly, though the following contrived example would be a strange way to use the handler: exc_info = sys.exc_info() ... LOG.error("Bad math.", exc_info=exc_info) """ try: airbrakeerror = airbrake_error_from_logrecord(record) self.airbrake.log(**airbrakeerror) except (KeyboardInterrupt, SystemExit): raise except: # pylint: disable=bare-except self.handleError(record)
def test_deploy_revision(self): with mock.patch('requests.post') as requests_post: ab = Airbrake(project_id=1234, api_key='fake', environment='test') ab.deploy('test', 'user1', 'https://github.com/airbrake/airbrake', None, 'v2.0') data = json.loads(requests_post.call_args[1]['data']) version = airbrake.utils.get_local_git_revision() self.assertEqual(version, data['revision'])
def test_notice_severity(self): ab = Airbrake(project_id=1234, api_key='fake') notice = ab.build_notice(ValueError("This is a test"), severity=ErrorLevels.CRITICAL) self.assertEqual(ErrorLevels.CRITICAL, notice.payload['context']['severity']) with mock.patch('requests.post') as requests_post: ab.notify(notice) data = json.loads(requests_post.call_args[1]['data']) error_level = data['context']['severity'] self.assertEqual(ErrorLevels.CRITICAL, error_level)
def test_notify_str(self): ab = Airbrake(project_id=1234, api_key='fake') exception_str = "This is a test" exception_type = 'Error' notice = ab.build_notice(exception_str) expected_payload = self.get_expected_payload(exception_str, exception_type) self.assertEqual(expected_payload, notice.payload) with mock.patch('requests.post') as requests_post: ab.notify(exception_str) data = json.loads(requests_post.call_args[1]['data']) self.assertEqual(expected_payload, data)
def test_deploy_payload(self): with mock.patch('requests.post') as requests_post: ab = Airbrake(project_id=1234, api_key='fake', environment='test') ab.deploy('test', 'user1', 'https://github.com/airbrake/airbrake', '38748467ea579e7ae64f7815452307c9d05e05c5', 'v2.0') expected_call_args = mock.call( 'https://airbrake.io/api/v4/projects/1234/deploys', data='{"environment": "test",' ' "repository": "https://github.com/airbrake/airbrake",' ' "revision": "38748467ea579e7ae64f7815452307c9d05e05c5",' ' "username": "******",' ' "version": "v2.0"}', headers={'Content-Type': 'application/json'}, params={'key': 'fake'}, timeout=Airbrake.AIRBRAKE_TIMEOUT_DEFAULT) self.assertEqual(expected_call_args, requests_post.call_args)
def __init__(self, airbrake=None, level=logging.ERROR, project_id=None, api_key=None, **config): """Initialize the Airbrake handler with a default logging level. Default level of logs handled by this class are >= ERROR, which includes ERROR and CRITICAL. To change this behavior supply a different argument for 'level'. """ logging.Handler.__init__(self, level=level) if isinstance(airbrake, Airbrake): self.airbrake = airbrake else: self.airbrake = Airbrake(project_id, api_key, **config)
def test_blacklist(self): ab = Airbrake(project_id=1234, api_key='fake', blacklist_keys=["filter_me_i_dare_you", "blacklist_me"]) expected_params = { "filter_me_i_dare_you": "[Filtered]", "blacklist_me": "[Filtered]", "page": 1, "order": "desc" } self.check_filter(ab, expected_params)
def check_timeout(self, timeout=None, expected_timeout=None): ab = Airbrake(project_id=1234, api_key='fake', environment='test', timeout=timeout) if not timeout: ab = Airbrake(project_id=1234, api_key='fake', environment='test') with mock.patch('requests.post') as requests_post: ab.deploy('test', 'user1') timeout = requests_post.call_args[1]['timeout'] self.assertEqual(expected_timeout, timeout) with mock.patch('requests.post') as requests_post: notice = ab.build_notice("This is a test") ab.notify(notice) timeout = requests_post.call_args[1]['timeout'] self.assertEqual(expected_timeout, timeout)
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 __init__(self, airbrake=None, level=logging.ERROR, project_id=None, api_key=None, environment=None): """Initialize the Airbrake handler with a default logging level. Default level of logs handled by this class are >= ERROR, which includes ERROR and CRITICAL. To change this behavior supply a different argument for 'level'. """ logging.Handler.__init__(self, level=level) if isinstance(airbrake, Airbrake): self.airbrake = airbrake else: self.airbrake = Airbrake(project_id, api_key, environment)
def test_uncaught_exception(self): ab = Airbrake(project_id=1234, api_key='fake') self.preserved_syshook = False def early_exit_syshook(*exc_info): self.preserved_syshook = True return ab.excepthook = early_exit_syshook with mock.patch('requests.post') as requests_post: try: raise Exception("raise to sys level") except Exception: # nose wraps exceptions, so manually call the exception as # if it was uncaught. exc_info = sys.exc_info() sys.excepthook(*exc_info) data = json.loads(requests_post.call_args[1]['data']) error_level = data['context']['severity'] self.assertEqual(ErrorLevels.ERROR, error_level) self.assertTrue(self.preserved_syshook)
def test_capture_decorator(self): ab = Airbrake(project_id=1234, api_key='fake') with mock.patch('requests.post') as requests_post: @capture(ab) def faulty_func(msg): raise ValueError(msg) msg = "I am a banana" with self.assertRaises(ValueError) as cm: faulty_func(msg) self.assertEqual(msg, str(cm.exception)) data = json.loads(requests_post.call_args[1]['data']) err_data = data['errors'][0] self.assertEqual(err_data['backtrace'][0]['function'], 'faulty_func') filename = err_data['backtrace'][0]['file'] self.assertTrue(filename.endswith("tests/test_notifier.py"))
from airbrake.notifier import Airbrake manager = Airbrake(project_id=144031, api_key="5a2fb879e83b40479b90284263193376") try: # Raising an exception. 1 / 0 except ZeroDivisionError as e: # Sends a 'division by zero' exception to Airbrake. manager.notify(e) except: # Sends all other exceptions to Airbrake. manager.capture()
#!/usr/bin/env python3 # coding=utf-8 import serial import time import struct import requests import json from airbrake.notifier import Airbrake with open('config.json') as f: CONFIG = json.load(f) ab = Airbrake(host=CONFIG["ERRBIT_HOST"], api_key=CONFIG["ERRBIT_PROJECT_KEY"], project_id=CONFIG["ERRBIT_PROJECT_ID"]) class BTPOWER: setAddrBytes = [0xB4, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x1E] readVoltageBytes = [0xB0, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x1A] readCurrentBytes = [0XB1, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x1B] readPowerBytes = [0XB2, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x1C] readRegPowerBytes = [0XB3, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x1D] def __init__(self, com=CONFIG["USB_SERIAL"], timeout=10.0): self.ser = serial.Serial(port=com, baudrate=9600, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS,
class AirbrakeNotifier: MAX_NOTIFICATIONS = 50 airbrake_notifier = Airbrake(project_id=config['airbrake_project_id'], api_key=config['airbrake_api_key']) blacklisted_log_group_names = set( config['airbrake_blacklisted_log_group_names']) blacklisted_log_message_strings_regex = re.compile('|'.join( config["airbrake_blacklisted_log_message_strings"])) whitelisted_log_message_terms_regex_string = "|".join( config['airbrake_whitelisted_log_message_terms']) whitelisted_log_message_terms_regexp = re.compile( whitelisted_log_message_terms_regex_string, re.IGNORECASE) def __init__(self): self._report = dict() self._total_errors = 0 self._airbrake_rate_limited = False def report(self): results = [] for log_group, subcounts in self._report.items(): for message_type, count in subcounts.items(): results += [(log_group, message_type, count)] return results def notify_on_stream(self, log_event_stream): for log_event in log_event_stream: self.notify(log_event) yield log_event def notify(self, log_event): message = log_event['@message'] log_group = log_event['@log_group'] log_stream = log_event['@log_stream'] error_str = None if AirbrakeNotifier._is_message_appropriate_for_airbrake(message, log_group) and \ not AirbrakeNotifier._contains_blacklisted_string(message): error_str = "'{0} {1} '@log_stream': {2}".format( log_group, message, log_stream) try: if not self._airbrake_rate_limited and self._total_errors < AirbrakeNotifier.MAX_NOTIFICATIONS: AirbrakeNotifier.airbrake_notifier.notify(error_str) except Exception as e: message = str(e) if message.startswith('420 Client Error'): self._airbrake_rate_limited = True else: logger.error( "Airbrake notification failed! {}".format(message)) self._observe(log_group, error_str) def _observe(self, log_group, error_str): if log_group not in self._report: self._report[log_group] = {'errors': 0, 'total': 0} if error_str: self._report[log_group]['errors'] += 1 self._total_errors += 1 self._report[log_group]['total'] += 1 @staticmethod def _is_message_appropriate_for_airbrake(message, log_group): if log_group not in AirbrakeNotifier.blacklisted_log_group_names and \ AirbrakeNotifier.whitelisted_log_message_terms_regexp.search(message): return True return False @staticmethod def _contains_blacklisted_string(message): if AirbrakeNotifier.blacklisted_log_message_strings_regex.search( message): return True return False