-
Notifications
You must be signed in to change notification settings - Fork 453
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
Fix/stop core on components startup exception #7021
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
import os | ||
import re | ||
import sys | ||
from collections import deque | ||
from pathlib import Path | ||
from typing import Optional | ||
|
||
|
@@ -16,6 +17,9 @@ | |
from tribler.gui.utilities import connect | ||
|
||
|
||
CORE_OUTPUT_DEQUE_LENGTH = 10 | ||
|
||
|
||
class CoreManager(QObject): | ||
""" | ||
The CoreManager is responsible for managing the Tribler core (starting/stopping). When we are running the GUI tests, | ||
|
@@ -47,8 +51,8 @@ def __init__(self, root_state_dir: Path, api_port: int, api_key: str, app_manage | |
self.should_quit_app_on_core_finished = False | ||
|
||
self.use_existing_core = True | ||
self.last_core_stdout_output: str = '' | ||
self.last_core_stderr_output: str = '' | ||
self.last_core_stdout_output: deque = deque(maxlen=CORE_OUTPUT_DEQUE_LENGTH) | ||
self.last_core_stderr_output: deque = deque(maxlen=CORE_OUTPUT_DEQUE_LENGTH) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The last write to stderr does not necessarily contain the actual exception traceback, so we store several writes. |
||
|
||
connect(self.events_manager.core_connected, self.on_core_connected) | ||
|
||
|
@@ -125,10 +129,11 @@ def on_core_stdout_read_ready(self): | |
return | ||
|
||
raw_output = bytes(self.core_process.readAllStandardOutput()) | ||
self.last_core_stdout_output = self.decode_raw_core_output(raw_output).strip() | ||
output = self.decode_raw_core_output(raw_output).strip() | ||
self.last_core_stdout_output.append(output) | ||
|
||
try: | ||
print(self.last_core_stdout_output) # print core output # noqa: T001 | ||
print(output) # print core output # noqa: T001 | ||
except OSError: | ||
# Possible reason - cannot write to stdout as it was already closed during the application shutdown | ||
pass | ||
|
@@ -139,10 +144,11 @@ def on_core_stderr_read_ready(self): | |
return | ||
|
||
raw_output = bytes(self.core_process.readAllStandardError()) | ||
self.last_core_stderr_output = self.decode_raw_core_output(raw_output).strip() | ||
output = self.decode_raw_core_output(raw_output).strip() | ||
self.last_core_stderr_output.append(output) | ||
|
||
try: | ||
print(self.last_core_stderr_output, file=sys.stderr) # print core output # noqa: T001 | ||
print(output, file=sys.stderr) # print core output # noqa: T001 | ||
except OSError: | ||
# Possible reason - cannot write to stdout as it was already closed during the application shutdown | ||
pass | ||
|
@@ -196,18 +202,24 @@ def kill_core_process_and_remove_the_lock_file(self): | |
process_checker = ProcessChecker(self.root_state_dir) | ||
process_checker.remove_lock() | ||
|
||
def get_last_core_output(self, quoted=True): | ||
output = ''.join(self.last_core_stderr_output) or ''.join(self.last_core_stdout_output) | ||
if quoted: | ||
output = re.sub(r'^', '> ', output, flags=re.MULTILINE) | ||
return output | ||
|
||
@staticmethod | ||
def format_error_message(exit_code: int, exit_status: int, last_core_output: str) -> str: | ||
def format_error_message(exit_code: int, exit_status: int) -> str: | ||
message = f"The Tribler core has unexpectedly finished with exit code {exit_code} and status: {exit_status}." | ||
try: | ||
string_error = os.strerror(exit_code) | ||
except ValueError: | ||
# On platforms where strerror() returns NULL when given an unknown error number, ValueError is raised. | ||
string_error = 'unknown error number' | ||
if exit_code == 1: | ||
string_error = "Application error" | ||
else: | ||
try: | ||
string_error = os.strerror(exit_code) | ||
except ValueError: | ||
# On platforms where strerror() returns NULL when given an unknown error number, ValueError is raised. | ||
string_error = 'unknown error number' | ||
message += f'\n\nError message: {string_error}' | ||
|
||
quoted_output = re.sub(r'^', '> ', last_core_output, flags=re.MULTILINE) | ||
message += f"\n\nLast core output:\n{quoted_output}" | ||
Comment on lines
-209
to
-210
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This PR moves the rendering of the last core output to |
||
return message | ||
|
||
def on_core_finished(self, exit_code, exit_status): | ||
|
@@ -218,8 +230,7 @@ def on_core_finished(self, exit_code, exit_status): | |
if self.should_quit_app_on_core_finished: | ||
self.app_manager.quit_application() | ||
else: | ||
output = self.last_core_stderr_output or self.last_core_stdout_output | ||
error_message = self.format_error_message(exit_code, exit_status, output) | ||
error_message = self.format_error_message(exit_code, exit_status) | ||
self._logger.warning(error_message) | ||
|
||
if not self.app_manager.quitting_app: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid spamming logs with multiple copies of the same error traceback
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT. As an alternative option, you can use
SentryReporter.ignore_logger()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I understand it,
SentryReporter.ignore_logger()
affects what we send to Sentry, but does not affect our log files.