blob: b2d853ab52dcdd4d6e4dabca38c4d49725dc3a3e [file] [log] [blame]
# Copyright 2021 The ChromiumOS Authors
# 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)
# prepend lines written to the log file with timestamps
ts = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
lines_ts = []
for line in message.strip().splitlines():
lines_ts.append(f"[{ts}] {line}\n")
message_ts = "".join(lines_ts)
self.log.write(message_ts)
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