Logging, StreamHandler and standard streams
I can't figure out how to log info-level messages to stdout, but everything else to stderr. I already read this http://docs.python.org/library/logging.html. Any suggestion?
The following script, log1.py
:
import logging, sys
class SingleLevelFilter(logging.Filter):
def __init__(self, passlevel, reject):
self.passlevel = passlevel
self.reject = reject
def filter(self, record):
if self.reject:
return (record.levelno != self.passlevel)
else:
return (record.levelno == self.passlevel)
h1 = logging.StreamHandler(sys.stdout)
f1 = SingleLevelFilter(logging.INFO, False)
h1.addFilter(f1)
rootLogger = logging.getLogger()
rootLogger.addHandler(h1)
h2 = logging.StreamHandler(sys.stderr)
f2 = SingleLevelFilter(logging.INFO, True)
h2.addFilter(f2)
rootLogger.addHandler(h2)
logger = logging.getLogger("my.logger")
logger.setLevel(logging.DEBUG)
logger.debug("A DEBUG message")
logger.info("An INFO message")
logger.warning("A WARNING message")
logger.error("An ERROR message")
logger.critical("A CRITICAL message")
when run, produces the following results.
C:\temp>log1.py A DEBUG message An INFO message A WARNING message An ERROR message A CRITICAL message
As you'd expect, since on a terminal sys.stdout
and sys.stderr
are the same. Now, let's redirect stdout to a file, tmp
:
C:\temp>log1.py >tmp A DEBUG message A WARNING message An ERROR message A CRITICAL message
So the INFO message has not been printed to the terminal - but the messages directed to sys.stderr
have been printed. Let's look at what's in tmp
:
C:\temp>type tmp An INFO message
So that approach appears to do what you want.
Generally, I think it makes sense to redirect messages lower than WARNING
to stdout, instead of only INFO
messages.
Based on Vinay Sajip's excellent answer, I came up with this:
class MaxLevelFilter(Filter):
'''Filters (lets through) all messages with level < LEVEL'''
def __init__(self, level):
self.level = level
def filter(self, record):
return record.levelno < self.level # "<" instead of "<=": since logger.setLevel is inclusive, this should be exclusive
MIN_LEVEL= DEBUG
#...
stdout_hdlr = StreamHandler(sys.stdout)
stderr_hdlr = StreamHandler(sys.stderr)
lower_than_warning= MaxLevelFilter(WARNING)
stdout_hdlr.addFilter( lower_than_warning ) #messages lower than WARNING go to stdout
stdout_hdlr.setLevel( MIN_LEVEL )
stderr_hdlr.setLevel( max(MIN_LEVEL, WARNING) ) #messages >= WARNING ( and >= STDOUT_LOG_LEVEL ) go to stderr
#...