tschm
tschm

Reputation: 2955

Multiple streamhandlers

I am trying to beef up the logging in my Python scripts and I am grateful if could share best practices with me. For now I have created this little script (I should say that I run Python 3.4)

import logging
import io
import sys

def Streamhandler(stream, level, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"):
    ch = logging.StreamHandler(stream)
    ch.setLevel(level)
    formatter = logging.Formatter(format)
    ch.setFormatter(formatter)
    return ch


# get the root logger
logger = logging.getLogger()

stream = io.StringIO()
logger.addHandler(Streamhandler(stream, logging.WARN))

stream_error = io.StringIO()
logger.addHandler(Streamhandler(stream_error, logging.ERROR))

logger.addHandler(Streamhandler(stream=sys.stdout, level=logging.DEBUG))

print(logger)
for h in logger.handlers:
    print(h)
    print(h.level)

# 'application' code   # goes to the root logger!
logging.debug('debug message')
logging.info('info message')
logging.warning('warn message')
logging.error('error message')
logging.critical('critical message')

print(stream.getvalue())
print(stream_error.getvalue())

I have three handlers, 2 of them write into a io.StringIO (this seems to work). I need this to simplify testing but also to send logs via a HTTP email service. And then there is a Streamhandler for the console. However, logging.debug and logging.info messages are ignored on the console here despite setting the level explicitly low enough?!

Upvotes: 1

Views: 2057

Answers (2)

bruno desthuilliers
bruno desthuilliers

Reputation: 77892

First, you didn't set the level on the logger itself:

logger.setLevel(logging.DEBUG)

Also, you define a logger but do your calls on logging - which will call on the root logger. Not that it will make any difference in your case since you didn't specify a name for your logger, so logging.getLogger() returns the root logger.

wrt/ "best practices", it really depends on how "complex" your scripts are and of course on your logging needs.

For self-contained simple scripts with simple use cases (single known environment, no concurrent execution, simple logging to a file or stderr etc), a simple call to logging.basicConfig() and direct calls to logging.whatever() are usually good enough.

For anything more complex, it's better to use a distinct config file - either in ini format or as Python dict (using logging.dictConfig), split your script into distinct module(s) or package(s) each defining it's own named logger (with logger = logging.getLogger(__name__)) and only keep your script itself as the "runner" for your code, ie: configure logging, import modules, parse command line args and call the main function - preferably in a try/except block as to properly log any unhandled exception before crashing.

Upvotes: 2

Finwood
Finwood

Reputation: 3981

A logger has a threshold level too, you need to set it to DEBUG first:

logger.setLevel(logging.DEBUG)

Upvotes: 1

Related Questions