Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sentry: Disable parse code output #7864

Merged
merged 1 commit into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading