scripts/release/log.py
changeset 1846 ac30e04bcbba
parent 1834 0589bf1395c5
child 1888 ef350db7f753
--- a/scripts/release/log.py	Fri Mar 13 23:12:40 2009 +0000
+++ b/scripts/release/log.py	Fri Mar 13 23:12:43 2009 +0000
@@ -26,10 +26,31 @@
     ]
 
 import logging
+import sys
 
 import util
 
 
+# A logging level that is even lower than DEBUG. We use this level to
+# log to file only, when we need to replay something output by
+# bypassing the logging system.
+_TERMINAL_ECHO = 5
+
+
+# Pull in the functions from the logging module, for ease of use
+debug = logging.debug
+info = logging.info
+error = logging.error
+raw = debug  # Used for logging raw output like subprocess stdout data.
+
+
+# Save the actual out/err streams before init() replaces them. Users
+# of the logging system can use these files if they need to bypass the
+# logging system for some reason.
+stdout = sys.stdout
+stderr = sys.stderr
+
+
 class _ColorizingFormatter(logging.Formatter):
   """A logging formatter that colorizes based on log levels."""
 
@@ -51,6 +72,35 @@
     return util.decolorize(logging.Formatter.format(self, record))
 
 
+class FileLikeLogger(object):
+  """A file-like object that logs anything written to it."""
+
+  def __init__(self):
+    self._buffer = []
+
+  def _getBuffer(self):
+    data, self._buffer = ''.join(self._buffer), []
+    return data
+
+  def close(self):
+    self.flush()
+
+  def flush(self):
+    raw(self._getBuffer())
+
+  def write(self, str):
+    lines = str.split('\n')
+    self.writelines(lines[0:-1])
+    self._buffer.append(lines[-1])
+
+  def writelines(self, lines):
+    if not lines:
+      return
+    lines[0] = self._getBuffer() + lines[0]
+    for line in lines:
+      raw(line)
+
+
 def init(logfile=None):
   """Initialize the logging subsystem.
 
@@ -59,7 +109,7 @@
   """
 
   root = logging.getLogger('')
-  root.setLevel(logging.DEBUG)
+  root.setLevel(_TERMINAL_ECHO)
 
   console = logging.StreamHandler()
   console.setLevel(logging.DEBUG)
@@ -68,11 +118,27 @@
 
   if logfile:
     transcript = logging.FileHandler(logfile, 'w')
-    transcript.setLevel(logging.DEBUG)
+    transcript.setLevel(_TERMINAL_ECHO)
     transcript.setFormatter(_DecolorizingFormatter())
     root.addHandler(transcript)
 
+  # Redirect sys.stdout and sys.stderr to logging streams. This will
+  # force everything that is output in this process, even through
+  # 'print', to go to both the console and the transcript (if active).
+  sys.stdout = FileLikeLogger()
+  sys.stderr = FileLikeLogger()
 
-debug = logging.debug
-info = logging.info
-error = logging.error
+
+def terminal_echo(text, *args, **kwargs):
+  """Echo a message written manually to the terminal.
+
+  This function should be used when you want to echo into the logging
+  system something that you manually wrote to the real
+  stdout/stderr.
+
+  For example, when asking the user for input, you would output the
+  raw prompt to the terminal manually, read the user's response, and
+  then echo both the prompt and the answer back into the logging
+  system for recording.
+  """
+  logging.log(_TERMINAL_ECHO, text, *args, **kwargs)