#!/bin/env python # Copyright 2008, Google Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # logging_utils.py ''' Utility functions and objects for logging. ''' import logging import sys class StdoutStderrHandler(logging.Handler): ''' Subclass of logging.Handler which outputs to either stdout or stderr based on a threshold level. ''' def __init__(self, threshold=logging.WARNING, err=sys.stderr, out=sys.stdout): ''' Args: threshold: below this logging level messages are sent to stdout, otherwise they are sent to stderr err: a stream object that error messages are sent to, defaults to sys.stderr out: a stream object that non-error messages are sent to, defaults to sys.stdout ''' logging.Handler.__init__(self) self._err = logging.StreamHandler(err) self._out = logging.StreamHandler(out) self._threshold = threshold self._last_was_err = False def setLevel(self, lvl): logging.Handler.setLevel(self, lvl) self._err.setLevel(lvl) self._out.setLevel(lvl) def setFormatter(self, formatter): logging.Handler.setFormatter(self, formatter) self._err.setFormatter(formatter) self._out.setFormatter(formatter) def emit(self, record): if record.levelno < self._threshold: self._out.emit(record) self._last_was_err = False else: self._err.emit(record) self._last_was_err = False def flush(self): # preserve order on the flushing, the stalest stream gets flushed first if self._last_was_err: self._out.flush() self._err.flush() else: self._err.flush() self._out.flush() FORMAT = "%(asctime)s %(filename)s [%(levelname)s] %(message)s" DATEFMT = "%H:%M:%S" def config_root(level=logging.INFO, threshold=logging.WARNING, format=FORMAT, datefmt=DATEFMT): ''' Configure the root logger to use a StdoutStderrHandler and some default formatting. Args: level: messages below this level are ignored threshold: below this logging level messages are sent to stdout, otherwise they are sent to stderr format: format for log messages, see logger.Format datefmt: format for date in log messages ''' # to set the handler of the root logging object, we need to do setup # manually rather than using basicConfig root = logging.getLogger() root.setLevel(level) formatter = logging.Formatter(format, datefmt) handler = StdoutStderrHandler(threshold=threshold) handler.setLevel(level) handler.setFormatter(formatter) root.addHandler(handler)