15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2011 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)''' Utility functions and objects for logging.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)'''
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class StdoutStderrHandler(logging.Handler):
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ''' Subclass of logging.Handler which outputs to either stdout or stderr
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  based on a threshold level.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, threshold=logging.WARNING, err=sys.stderr, out=sys.stdout):
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ''' Args:
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          threshold: below this logging level messages are sent to stdout,
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            otherwise they are sent to stderr
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          err: a stream object that error messages are sent to, defaults to
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            sys.stderr
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          out: a stream object that non-error messages are sent to, defaults to
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            sys.stdout
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    logging.Handler.__init__(self)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._err = logging.StreamHandler(err)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._out = logging.StreamHandler(out)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._threshold = threshold
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._last_was_err = False
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def setLevel(self, lvl):
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    logging.Handler.setLevel(self, lvl)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._err.setLevel(lvl)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._out.setLevel(lvl)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def setFormatter(self, formatter):
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    logging.Handler.setFormatter(self, formatter)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._err.setFormatter(formatter)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._out.setFormatter(formatter)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def emit(self, record):
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if record.levelno < self._threshold:
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._out.emit(record)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._last_was_err = False
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._err.emit(record)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._last_was_err = False
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def flush(self):
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # preserve order on the flushing, the stalest stream gets flushed first
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._last_was_err:
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._out.flush()
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._err.flush()
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._err.flush()
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._out.flush()
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FORMAT = "%(asctime)s %(filename)s [%(levelname)s] %(message)s"
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DATEFMT = "%H:%M:%S"
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def config_root(level=logging.INFO, threshold=logging.WARNING, format=FORMAT,
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         datefmt=DATEFMT):
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ''' Configure the root logger to use a StdoutStderrHandler and some default
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  formatting.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      level: messages below this level are ignored
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      threshold: below this logging level messages are sent to stdout,
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        otherwise they are sent to stderr
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      format: format for log messages, see logger.Format
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      datefmt: format for date in log messages
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # to set the handler of the root logging object, we need to do setup
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # manually rather than using basicConfig
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  root = logging.getLogger()
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  root.setLevel(level)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  formatter = logging.Formatter(format, datefmt)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  handler = StdoutStderrHandler(threshold=threshold)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  handler.setLevel(level)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  handler.setFormatter(formatter)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  root.addHandler(handler)
83