Skip to content

Commit

Permalink
Merge pull request #5772 from drew2a/refactoring_SentryReporter
Browse files Browse the repository at this point in the history
Refactoring SentryReporter
  • Loading branch information
drew2a committed Dec 2, 2020
2 parents 6572ec9 + 9534444 commit f254bbb
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class SentryReporter:
SentryReporter.strategy.set(SentryReporter.Strategy.SEND_SUPPRESSED)
```
SentryReporter is thread-safe.
"""
"""

class Strategy(Enum):
SEND_ALLOWED = auto()
Expand All @@ -66,7 +66,7 @@ class Strategy(Enum):

@staticmethod
def init(sentry_url='', scrubber=None, strategy=Strategy.SEND_ALLOWED_WITH_CONFIRMATION):
""" Initialization.
"""Initialization.
This method should be called in each process that uses SentryReporter.
Expand Down Expand Up @@ -212,9 +212,31 @@ def capture_exception(exception):
SentryReporter._logger.info(f"Capture exception: {exception}")
sentry_sdk.capture_exception(exception)

@staticmethod
def event_from_exception(exception):
"""This function format the exception by passing it through sentry
Args:
exception: an exception that will be passed to `sentry_sdk.capture_exception(exception)`
Returns:
the event that has been saved in `_before_send` method
"""
SentryReporter._logger.info(f"Event from exception: {exception}")

if not exception:
return exception

saved_strategy = SentryReporter.strategy.get()
try:
SentryReporter.strategy.set(SentryReporter.Strategy.SEND_SUPPRESSED)
sentry_sdk.capture_exception(exception)
return SentryReporter.last_event
finally:
SentryReporter.strategy.set(saved_strategy)

@staticmethod
def set_user(user_id):
""" Set the user to identify the event on a Sentry server
"""Set the user to identify the event on a Sentry server
The algorithm is the following:
1. Calculate hash from `user_id`.
Expand Down Expand Up @@ -261,8 +283,7 @@ def _before_send(event, hint):
return event

strategy = SentryReporter.strategy.get()
SentryReporter._logger.info(f"Before send event: {event}")
SentryReporter._logger.info(f"Strategy: {strategy}")
SentryReporter._logger.info(f"Before send strategy: {strategy}")

exc_info = get_value(hint, 'exc_info')
error_type = get_first_item(exc_info)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def test_before_send(reporter):
reporter.init('', scrubber)

# pylint: disable=protected-access
SentryReporter.last_event = None

assert reporter._before_send({}, {}) == {}
assert reporter._before_send(None, {}) is None
Expand Down Expand Up @@ -135,3 +136,11 @@ def test_before_send(reporter):

SentryReporter.get_confirmation = lambda e: True
assert reporter._before_send({'a': 'b'}, None) == {'a': 'b'}


def test_event_from_exception(reporter):
assert not reporter.event_from_exception(None)

# sentry sdk is not initialised, so None will be returned
SentryReporter.last_event = None
assert not reporter.event_from_exception(Exception('test'))
12 changes: 5 additions & 7 deletions src/tribler-core/tribler_core/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,7 @@ 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.
"""
sentry_saved_strategy = SentryReporter.strategy.get()
try:
SentryReporter.strategy.set(SentryReporter.Strategy.SEND_SUPPRESSED)
SentryReporter.ignore_logger(self._logger.name)

exception = context.get('exception')
Expand Down Expand Up @@ -249,19 +247,19 @@ def unhandled_error_observer(self, loop, context):
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)
SentryReporter.capture_exception(exception)
sentry_event = SentryReporter.last_event

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)

state = self.api_manager.get_endpoint('state')
state.on_tribler_exception(text_long, sentry_event)
finally:
SentryReporter.strategy.set(sentry_saved_strategy)
except Exception as ex:
SentryReporter.capture_exception(ex)
raise ex

def get_tribler_statistics(self):
"""Return a dictionary with general Tribler statistics."""
Expand Down
18 changes: 9 additions & 9 deletions src/tribler-gui/tribler_gui/dialogs/feedbackdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,23 @@
from tribler_gui.event_request_manager import received_events
from tribler_gui.tribler_action_menu import TriblerActionMenu
from tribler_gui.tribler_request_manager import (
TriblerNetworkRequest, performed_requests as tribler_performed_requests, tribler_urlencode, )
TriblerNetworkRequest,
performed_requests as tribler_performed_requests,
tribler_urlencode,
)
from tribler_gui.utilities import get_ui_file_path


class FeedbackDialog(QDialog):
def __init__(self, parent, exception_text, tribler_version, start_time, # pylint: disable=R0914
backend_sentry_event=None):
def __init__(self, parent, exception_text, tribler_version, start_time, sentry_event=None): # pylint: disable=R0914
QDialog.__init__(self, parent)

uic.loadUi(get_ui_file_path('feedback_dialog.ui'), self)

self.setWindowTitle("Unexpected error")
self.selected_item_index = 0
self.tribler_version = tribler_version
self.backend_sentry_event = backend_sentry_event
self.sentry_event = sentry_event

# Qt 5.2 does not have the setPlaceholderText property
if hasattr(self.comments_text_edit, "setPlaceholderText"):
Expand Down Expand Up @@ -69,8 +71,8 @@ def add_item_to_info_widget(key, value):
for request, status_code in sorted(tribler_performed_requests, key=lambda rq: rq[0].time)[-30:]:
add_item_to_info_widget(
'request_%d' % request_ind,
'%s %s %s (time: %s, code: %s)' % (request.url, request.method,
request.raw_data, request.time, status_code),
'%s %s %s (time: %s, code: %s)'
% (request.url, request.method, request.raw_data, request.time, status_code),
)
request_ind += 1

Expand Down Expand Up @@ -150,9 +152,7 @@ def on_send_clicked(self):
"stack": stack,
}

# if no backend_sentry_event, then try to restore GUI exception
sentry_event = self.backend_sentry_event or SentryReporter.last_event
SentryReporter.send_event(sentry_event, post_data, sys_info_dict)
SentryReporter.send_event(self.sentry_event, post_data, sys_info_dict)

TriblerNetworkRequest(endpoint, self.on_report_sent, raw_data=tribler_urlencode(post_data), method='POST')

Expand Down
42 changes: 22 additions & 20 deletions src/tribler-gui/tribler_gui/tribler_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ class TriblerWindow(QMainWindow):
received_search_completions = pyqtSignal(object)

def on_exception(self, *exc_info):
SentryReporter.strategy.set(SentryReporter.Strategy.SEND_SUPPRESSED)

if self.exception_handler_called:
# We only show one feedback dialog, even when there are two consecutive exceptions.
return
Expand All @@ -105,13 +103,17 @@ def on_exception(self, *exc_info):
info_type, info_error, _ = exc_info
exception_text = "".join(traceback.format_exception(*exc_info))

backend_sentry_event = None
sentry_event = None
for arg in info_error.args:
key = 'sentry_event'
if isinstance(arg, dict) and key in arg:
backend_sentry_event = arg[key]
sentry_event = arg[key]
break

if not sentry_event:
# then the exception occured in GUI
sentry_event = SentryReporter.event_from_exception(info_error)

logging.error(exception_text)

self.tribler_crashed.emit(exception_text)
Expand All @@ -134,7 +136,7 @@ def on_exception(self, *exc_info):
if info_type is CoreConnectTimeoutError:
exception_text = exception_text + self.core_manager.core_traceback

dialog = FeedbackDialog(self, exception_text, self.tribler_version, self.start_time, backend_sentry_event)
dialog = FeedbackDialog(self, exception_text, self.tribler_version, self.start_time, sentry_event)
dialog.show()

def __init__(self, core_args=None, core_env=None, api_port=None, api_key=None):
Expand Down Expand Up @@ -522,22 +524,22 @@ def update_recent_download_locations(self, destination):
self.gui_settings.setValue("recent_download_locations", ','.join(recent_locations))

def perform_start_download_request(
self,
uri,
anon_download,
safe_seeding,
destination,
selected_files,
total_files=0,
add_to_channel=False,
callback=None,
self,
uri,
anon_download,
safe_seeding,
destination,
selected_files,
total_files=0,
add_to_channel=False,
callback=None,
):
# Check if destination directory is writable
is_writable, error = is_dir_writable(destination)
if not is_writable:
gui_error_message = (
"Insufficient write permissions to <i>%s</i> directory. Please add proper "
"write permissions on the directory and add the torrent again. %s" % (destination, error)
"Insufficient write permissions to <i>%s</i> directory. Please add proper "
"write permissions on the directory and add the torrent again. %s" % (destination, error)
)
ConfirmationDialog.show_message(self.window(), "Download error <i>%s</i>" % uri, gui_error_message, "OK")
return
Expand Down Expand Up @@ -662,10 +664,10 @@ def on_top_search_button_click(self):
query = self.top_search_bar.text()

if (
self.last_search_query
and self.last_search_time
and self.last_search_query == self.top_search_bar.text()
and current_ts - self.last_search_time < 1
self.last_search_query
and self.last_search_time
and self.last_search_query == self.top_search_bar.text()
and current_ts - self.last_search_time < 1
):
logging.info("Same search query already sent within 500ms so dropping this one")
return
Expand Down

0 comments on commit f254bbb

Please sign in to comment.