Skip to content

Commit

Permalink
Merge pull request #7864 from drew2a/feature/disable_parse_core_output
Browse files Browse the repository at this point in the history
Sentry: Disable parse code output
  • Loading branch information
drew2a authored Jan 26, 2024
2 parents 404ae39 + 58ef177 commit 502d393
Show file tree
Hide file tree
Showing 4 changed files with 5 additions and 197 deletions.
14 changes: 1 addition & 13 deletions src/tribler/core/sentry_reporter/sentry_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
from tribler.core import version
from tribler.core.sentry_reporter.sentry_tools import (
get_first_item,
get_value,
parse_last_core_output
get_value
)

VALUE = 'value'
Expand Down Expand Up @@ -207,17 +206,6 @@ def send_event(self, event: Dict, tags: Optional[Dict[str, Any]] = None, info: O
if last_core_output:
info[LAST_CORE_OUTPUT] = last_core_output.split('\n') # split for better representation in the web view

# check is it necessary to parse the last core output
exceptions = event.get(EXCEPTION, {}).get(VALUES, [])
exception_types = (e.get(TYPE) for e in exceptions)
need_to_parse_core_output = any(t in self._types_that_requires_core_output_parse for t in exception_types)

if need_to_parse_core_output:
if last_core_exception := parse_last_core_output(last_core_output):
# create a core exception extracted from the last core output
core_exception = {TYPE: last_core_exception.type, VALUE: last_core_exception.message}
exceptions.append(core_exception)

event[CONTEXTS][REPORTER] = info
event[CONTEXTS][BROWSER] = {VERSION: tribler_version, NAME: TRIBLER}

Expand Down
22 changes: 0 additions & 22 deletions src/tribler/core/sentry_reporter/sentry_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@

from faker import Faker

# Find an exception in the string like: "OverflowError: bind(): port must be 0-65535"
_re_search_exception = re.compile(r'^([A-Za-z0-9_.]+):\s+(.+)')
_re_search_exception_exclusions = re.compile(r'(?:warning)', re.RegexFlag.IGNORECASE)

# Remove the substring like "Sentry is attempting to send 1 pending error messages"
_re_remove_sentry = re.compile(r'Sentry is attempting.*')

Expand All @@ -21,24 +17,6 @@ class LastCoreException:
message: str


def parse_last_core_output(text: str) -> Optional[LastCoreException]:
""" This function tries to find an Exception type and the Exception message in the raw core output
"""

def _clean_up(s: str):
return _re_remove_sentry.sub('', s).strip()

for line in reversed(text.split('\n')):
if m := _re_search_exception.match(line):
exception_type = m.group(1)
if _re_search_exception_exclusions.search(exception_type):
continue # find an exclusion

return LastCoreException(type=_clean_up(exception_type),
message=_clean_up(m.group(2)))
return None


def get_first_item(items, default=None):
return items[0] if items else default

Expand Down
75 changes: 2 additions & 73 deletions src/tribler/core/sentry_reporter/tests/test_sentry_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import pytest

from tribler.core.sentry_reporter.sentry_reporter import (
BROWSER, CONTEXTS, LAST_CORE_OUTPUT, NAME, OS_ENVIRON,
BROWSER, CONTEXTS, NAME, OS_ENVIRON,
REPORTER, STACKTRACE, SentryReporter,
SentryStrategy,
TAGS, TRIBLER, TYPE, VALUE, VERSION, this_sentry_strategy,
TAGS, TRIBLER, VERSION, this_sentry_strategy,
)
from tribler.core.sentry_reporter.sentry_scrubber import SentryScrubber
from tribler.core.utilities.patch_import import patch_import
Expand Down Expand Up @@ -270,74 +270,3 @@ def test_sentry_strategy(sentry_reporter):

assert sentry_reporter.thread_strategy.get() is None
assert sentry_reporter.global_strategy == SentryStrategy.SEND_ALLOWED_WITH_CONFIRMATION


def test_send_last_core_output(sentry_reporter):
# Test that the `send_event` function:
# 1. leaves only the last exception from the given Sentry error
# 2. removes stacktrace from the last exception from the given Sentry error
# 3. adds an exception extracted from the "last core output" to the Sentry event
event = {
'exception': {
'values': [
{
TYPE: 'CreationTraceback',
VALUE: '\n File "/Users/<user>/Projects/github.com/Tribler/tribler/src/run_tribler.py", ',
},
{
TYPE: 'CoreCrashedError',
VALUE: 'The Tribler core has unexpectedly finished with exit code 1 and status: 0.',
}
]
}
}

last_core_output = '''
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 1461, in create_server
sock.bind(sa)
OverflowError: bind(): port must be 0-65535.Sentry is attempting to send 1 pending error messages
Waiting up to 2 seconds
Press Ctrl-C to quit
'''

actual = sentry_reporter.send_event(event=event, last_core_output=last_core_output)
expected = deepcopy(DEFAULT_EVENT)

expected['exception'] = {
'values': [
{
TYPE: 'CreationTraceback',
VALUE: '\n File "/Users/<user>/Projects/github.com/Tribler/tribler/src/run_tribler.py", ',
},
{
TYPE: 'CoreCrashedError',
VALUE: 'The Tribler core has unexpectedly finished with exit code 1 and status: 0.',
},
{
TYPE: 'OverflowError',
VALUE: 'bind(): port must be 0-65535.'
}
]
}
expected[CONTEXTS][REPORTER][LAST_CORE_OUTPUT] = last_core_output.split('\n')

assert actual == expected


EXCEPTION_TYPES = [
# (exception_type, called)
('CoreCrashedError', True),
('AnyOtherError', False)
]


# The `parse_last_core_output` function is patched to have an indicator that the parse bloc is called
@patch('tribler.core.sentry_reporter.sentry_reporter.parse_last_core_output')
@pytest.mark.parametrize('exception_type, called', EXCEPTION_TYPES)
def test_send_last_core_output_types_that_requires_core_output_parse(patched_parse_last_core_output, exception_type,
called, sentry_reporter):
# Test that the `send_event` function parse the "last core output" only for the specific exception types
event = {'exception': {'values': [{TYPE: exception_type}]}}
sentry_reporter.send_event(event=event, last_core_output='any last core output')

assert patched_parse_last_core_output.called == called
91 changes: 2 additions & 89 deletions src/tribler/core/sentry_reporter/tests/test_sentry_tools.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import pytest

from tribler.core.sentry_reporter.sentry_tools import (
_re_search_exception, _re_search_exception_exclusions, delete_item,
delete_item,
distinct_by,
extract_dict,
format_version,
get_first_item,
get_last_item,
get_value,
modify_value,
obfuscate_string, parse_last_core_output,
)
obfuscate_string, )


def test_first():
Expand Down Expand Up @@ -109,93 +108,7 @@ def test_extract_dict():
('string', 'quality'),
]

EXCEPTION_STRINGS = [
(
'OverflowError: bind(): port must be 0-65535',
(
'OverflowError',
'bind(): port must be 0-65535'
),
),

(
"pony_orm.core.TransactionIntegrityError: MiscData['db_version'] cannot be stored. IntegrityError: UNIQUE",
(
'pony_orm.core.TransactionIntegrityError',
"MiscData['db_version'] cannot be stored. IntegrityError: UNIQUE"
),
),

# Strings thst should not be matched
('ERROR <exception_handler:100>', None),
('PyInstaller\loader\pyimod03_importers.py:495', None),
('foo:bar: baz', None),
('foo<bar>: baz', None),
]


@pytest.mark.parametrize('given, expected', OBFUSCATED_STRINGS)
def test_obfuscate_string(given, expected):
assert obfuscate_string(given) == expected


@pytest.mark.parametrize('given, expected', EXCEPTION_STRINGS)
def test_parse_last_core_output_re(given, expected):
# Test that `_re_search_exception` matches with the expected values from the `EXCEPTION_STRINGS`
if m := _re_search_exception.match(given):
exception_type, exception_text = expected

assert m.group(1) == exception_type
assert m.group(2) == exception_text
else: # no match
assert m == expected


EXCEPTION_EXCLUSIONS_STRINGS = [
'UserWarning',
]


@pytest.mark.parametrize('given', EXCEPTION_EXCLUSIONS_STRINGS)
def test_re_search_exception_exclusions(given):
# Test that `_re_search_exception_exclusions` matches with the expected values from the
# `EXCEPTION_EXCLUSIONS_STRINGS`
assert _re_search_exception_exclusions.search(given)


def test_parse_last_core_output():
# Test that `parse_last_core_output` correctly extract the last core exception from the real raw core output

last_core_output = '''
pony.orm.core.TransactionIntegrityError: Object MiscData['db_version'] cannot be stored in the database. IntegrityError
ERROR <exception_handler:100> CoreExceptionHandler.unhandled_error_observer(): Unhandled exception occurred! bind():
Traceback (most recent call last):
File "/Users/<user>/Projects/github.com/Tribler/tribler/src/tribler/core/components/component.py", line 61, in start
await self.run()
File "/Users/<user>/Projects/github.com/Tribler/tribler/src/tribler/core/components/restapi/restapi_component.py",
await rest_manager.start()
File "/Users/<user>/Projects/github.com/Tribler/tribler/src/tribler/core/components/restapi/rest/rest_manager.py",
await self.site.start()
File "/Users/<user>/Projects/github.com/Tribler/tribler/venv/lib/python3.8/site-packages/aiohttp/web_runner.py",
self._server = await loop.create_server(
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/
sock.bind(sa)
OverflowError: bind(): port must be 0-65535.Sentry is attempting to send 1 pending error messages
Waiting up to 2 seconds
Press Ctrl-C to quit
'''
last_core_exception = parse_last_core_output(last_core_output)
assert last_core_exception.type == 'OverflowError'
assert last_core_exception.message == 'bind(): port must be 0-65535.'


def test_parse_last_core_output_no_match():
# Test that `parse_last_core_output` returns None in the case there is no exceptions in the raw core output

last_core_exception = parse_last_core_output('last core output without exceptions')
assert not last_core_exception


def test_parse_last_core_output_exclusion():
last_core_exception = parse_last_core_output('UserWarning: You are using cryptography on a 32-bit Python on a...')
assert not last_core_exception

0 comments on commit 502d393

Please sign in to comment.