예제 #1
0
    def unhandled_error_observer(self, loop, context):
        """
        This method is called when an unhandled error in Tribler is observed.
        It broadcasts the tribler_exception event.
        """
        try:
            SentryReporter.ignore_logger(self._logger.name)

            exception = context.get('exception')
            ignored_message = None
            try:
                ignored_message = IGNORED_ERRORS.get(
                    (exception.__class__, exception.errno),
                    IGNORED_ERRORS.get(exception.__class__))
            except (ValueError, AttributeError):
                pass
            if ignored_message is not None:
                self._logger.error(ignored_message if ignored_message != "" else context.get('message'))
                return
            text = str(exception or context.get('message'))
            # We already have a check for invalid infohash when adding a torrent, but if somehow we get this
            # error then we simply log and ignore it.
            if isinstance(exception, RuntimeError) and 'invalid info-hash' in text:
                self._logger.error("Invalid info-hash found")
                return
            text_long = text
            exc = context.get('exception')
            if exc:
                with StringIO() as buffer:
                    print_exception(type(exc), exc, exc.__traceback__, file=buffer)
                    text_long = text_long + "\n--LONG TEXT--\n" + buffer.getvalue()
            text_long = text_long + "\n--CONTEXT--\n" + str(context)
            self._logger.error("Unhandled exception occurred! %s", text_long, exc_info=None)

            sentry_event = SentryReporter.event_from_exception(exception)

            if not self.api_manager:
                return
            events = self.api_manager.get_endpoint('events')
            events.on_tribler_exception(text_long,
                                        sentry_event,
                                        self.config.get_core_error_reporting_requires_user_consent())

            state = self.api_manager.get_endpoint('state')
            state.on_tribler_exception(text_long, sentry_event)
        except Exception as ex:
            SentryReporter.capture_exception(ex)
            raise ex
예제 #2
0
class CoreExceptionHandler:
    """
    This class handles Python errors arising in the Core by catching them, adding necessary context,
    and sending them to the GUI through the events endpoint. It must be connected to the Asyncio loop.
    """

    def __init__(self):
        self.logger = logging.getLogger("CoreExceptionHandler")
        self.report_callback: Optional[Callable[[ReportedError], None]] = None
        self.unreported_error: Optional[ReportedError] = None
        self.sentry_reporter = SentryReporter()

    @staticmethod
    def _get_long_text_from(exception: Exception):
        with StringIO() as buffer:
            print_exception(type(exception), exception, exception.__traceback__, file=buffer)
            return buffer.getvalue()

    @staticmethod
    def _is_ignored(exception: Exception):
        exception_class = exception.__class__
        error_number = exception.errno if hasattr(exception, 'errno') else None

        if (exception_class, error_number) in IGNORED_ERRORS_BY_CODE:
            return True

        if exception_class not in IGNORED_ERRORS_BY_REGEX:
            return False

        pattern = IGNORED_ERRORS_BY_REGEX[exception_class]
        return re.search(pattern, str(exception)) is not None

    def _create_exception_from(self, message: str):
        text = f'Received error without exception: {message}'
        self.logger.warning(text)
        return Exception(text)

    def unhandled_error_observer(self, _, context):
        """
        This method is called when an unhandled error in Tribler is observed.
        It broadcasts the tribler_exception event.
        """
        try:
            self.sentry_reporter.ignore_logger(self.logger.name)

            context = context.copy()
            should_stop = context.pop('should_stop', True)
            message = context.pop('message', 'no message')
            exception = context.pop('exception', None) or self._create_exception_from(message)
            # Exception
            text = str(exception)
            if isinstance(exception, ComponentStartupException):
                should_stop = exception.component.tribler_should_stop_on_component_error
                exception = exception.__cause__

            if self._is_ignored(exception):
                self.logger.warning(exception)
                return

            long_text = self._get_long_text_from(exception)
            self.logger.error(f"Unhandled exception occurred! {exception}\n{long_text}")

            reported_error = ReportedError(
                type=exception.__class__.__name__,
                text=text,
                long_text=long_text,
                context=str(context),
                event=self.sentry_reporter.event_from_exception(exception) or {},
                should_stop=should_stop
            )
            if self.report_callback:
                self.report_callback(reported_error)  # pylint: disable=not-callable
            else:
                if not self.unreported_error:
                    # We only remember the first unreported error,
                    # as that was probably the root cause for # the crash
                    self.unreported_error = reported_error

        except Exception as ex:
            self.sentry_reporter.capture_exception(ex)
            raise ex
예제 #3
0
def test_capture_exception(mocked_capture_exception: Mock,
                           sentry_reporter: SentryReporter):
    # test that `capture_exception` passes an exception to `sentry_sdk`
    exception = Exception('test')
    sentry_reporter.capture_exception(exception)
    mocked_capture_exception.assert_called_with(exception)