Bug 1750153 - Prevent unwanted errors from showing up in ./mach show-log after closing less before the entire log file has been piped to it r=firefox-build-system-reviewers,mhentges

The main thread is blocking (for an unknown reason) without piping the entirety of the log
file to `less` (unless the user manually scrolls to almost the bottom). Since the piping
cannot always complete without a user terminating, we must hide the errors that arise
from using the `stdin` stream for the `less` process after the process is terminated.

Differential Revision: https://phabricator.services.mozilla.com/D135979
This commit is contained in:
ahochheiden 2022-01-20 19:34:45 +00:00
parent 5b1ec638ce
commit bea764be73

View File

@ -17,6 +17,7 @@ import subprocess
import sys
import tempfile
import time
import errno
import mozbuild.settings # noqa need @SettingsProvider hook to execute
import mozpack.path as mozpath
@ -361,31 +362,60 @@ def show_log(command_context, log_file=None):
["less"], stdin=subprocess.PIPE, env=env, encoding="UTF-8"
)
# Create a new logger handler with the stream being the stdin of our 'less'
# process so that we can pipe the logger output into 'less'
less_handler = logging.StreamHandler(stream=less.stdin)
less_handler.setFormatter(
command_context.log_manager.terminal_handler.formatter
)
less_handler.setLevel(command_context.log_manager.terminal_handler.level)
try:
# Create a new logger handler with the stream being the stdin of our 'less'
# process so that we can pipe the logger output into 'less'
less_handler = logging.StreamHandler(stream=less.stdin)
less_handler.setFormatter(
command_context.log_manager.terminal_handler.formatter
)
less_handler.setLevel(command_context.log_manager.terminal_handler.level)
# replace the existing terminal handler with the new one for 'less' while
# still keeping the original one to set back later
original_handler = command_context.log_manager.replace_terminal_handler(
less_handler
)
# replace the existing terminal handler with the new one for 'less' while
# still keeping the original one to set back later
original_handler = command_context.log_manager.replace_terminal_handler(
less_handler
)
# Parses the log file line by line and streams (to less.stdin) the relevant records we want
handle_log_file(command_context, log_file)
# Save this value so we can set it back to the original value later
original_logging_raise_exceptions = logging.raiseExceptions
# Set the handler (and contained stream) back to the original one
command_context.log_manager.replace_terminal_handler(original_handler)
# We need to explicitly disable raising exceptions inside logging so
# that we can catch them here ourselves to ignore the ones we want
logging.raiseExceptions = False
# At this point we've piped the entire log file to 'less', so we can close the input stream
less.stdin.close()
# Wait for the user to manually terminate `less`
less.wait()
# Parses the log file line by line and streams
# (to less.stdin) the relevant records we want
handle_log_file(command_context, log_file)
# At this point we've piped the entire log file to
# 'less', so we can close the input stream
less.stdin.close()
# Wait for the user to manually terminate `less`
less.wait()
except OSError as os_error:
# (POSIX) errno.EPIPE: BrokenPipeError: [Errno 32] Broken pipe
# (Windows) errno.EINVAL: OSError: [Errno 22] Invalid argument
if os_error.errno == errno.EPIPE or os_error.errno == errno.EINVAL:
# If the user manually terminates 'less' before the entire log file
# is piped (without scrolling close enough to the bottom) we will get
# one of these errors (depends on the OS) because the logger will still
# attempt to stream to the now invalid less.stdin. To prevent a bunch
# of errors being shown after a user terminates 'less', we just catch
# the first of those exceptions here, and stop parsing the log file.
pass
else:
raise
except Exception:
raise
finally:
# Ensure these values are changed back to the originals, regardless of outcome
command_context.log_manager.replace_terminal_handler(original_handler)
logging.raiseExceptions = original_logging_raise_exceptions
else:
# Not in a terminal context, so just handle the log file with the
# default stream without piping it to a pager (less)
handle_log_file(command_context, log_file)