| # Copyright 2021 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| # import checking doesn't always work |
| # pylint: disable=import-error |
| |
| """Console that logs output of each command to a file in log/""" |
| |
| import atexit |
| import code |
| from datetime import datetime |
| import os |
| import readline |
| import rlcompleter |
| import sys |
| |
| import sh |
| |
| # Python's default history path |
| histfile = os.path.expanduser('~/.python_history') |
| |
| def save_history(): |
| """appends command history to the history file""" |
| readline.set_history_length(10000) |
| readline.write_history_file(histfile) |
| |
| class Logger: |
| """Splits stdout into stdout and a file""" |
| |
| def __init__(self): |
| sh.mkdir('-p', 'log/triage/') |
| ts = datetime.now().strftime('%d-%m-%Y_%H-%M-%S') |
| filename = ts + '.log' |
| if os.path.exists('log/latest'): |
| sh.rm('log/latest') |
| sh.ln('-s', filename, 'log/latest') |
| self.terminal = sys.stdout |
| self.log = open('log/' + filename, 'w') |
| |
| def write(self, message): |
| """Forwards write() call to self.terminal and self.log""" |
| |
| self.terminal.write(message) |
| self.log.write(message) |
| |
| def flush(self): |
| """Forwards flush() call to self.terminal and self.log""" |
| |
| self.terminal.flush() |
| self.log.flush() |
| |
| class LoggingConsole(code.InteractiveConsole): |
| """code.InteractiveConsole that logs console output""" |
| |
| def __init__(self, local=None): |
| code.InteractiveConsole.__init__(self, local) |
| self.logger = Logger() |
| try: |
| readline.read_history_file(histfile) |
| atexit.register(save_history) |
| except FileNotFoundError: |
| pass |
| readline.set_completer(rlcompleter.Completer(local).complete) |
| readline.parse_and_bind('tab: complete') |
| |
| def push(self, line): |
| """temporarily subsistute a Logger() instance before forwarding the push() call |
| |
| This is necessary because the InteractiveConsole will refuse to support history |
| if sys.stdout isn't the default value. |
| """ |
| |
| sys.stdout = self.logger |
| super().push(line) |
| sys.stdout = self.logger.terminal |