How to specify levels in python logging module?
Question:
I need two handlers. One for file logging and another one for stream logging in the console. I need to specify levels for each handler. Notice that my levels are going to be something like the following ones.
Stream Handler –> INFO
File Handler –> WARNING, ERROR, CRITICAL
Here is my code.
# Create a custom logger
logger = logging.getLogger('DBMQ')
logger.setLevel(logging.DEBUG)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
logger.addHandler(stream_handler)
file_handler = logging.FileHandler('./data/file.log')
file_handler.setLevel(logging.WARNING)
logger.addHandler(file_handler)
Now, it has to operate like this:
logger.debug('hey') # Nothing should happen
logger.info('hey') # only stream handler should log this message
logger.warning('hey') # only file handler should log this message but the stream does too
logger.error('hey') # only file handler should log this message but the stream does too
logger.critical('hey') # only file handler should log this message but the stream does too
I need to disallow the stream handler from logging the warning
, error
, and critical
logging levels. Is there any way to filter this handler?
Answers:
You could create your own Handler:
logger = logging.getLogger('DBMQ')
logger.setLevel(logging.DEBUG)
class MyStreamHandler(logging.StreamHandler):
def emit(self, record):
if record.levelno == self.level:
super().emit(record)
stream_handler = MyStreamHandler()
stream_handler.setLevel(logging.INFO)
logger.addHandler(stream_handler)
file_handler = logging.FileHandler('./data/file.log')
file_handler.setLevel(logging.WARNING)
logger.addHandler(file_handler)
The "level" system does not work to as disable/enable single "levels".
https://docs.python.org/3/library/logging.html#logging-levels
The "level" is a single integer value. If the message’s level is over logger/handler level, it will be showed. Just that simple.
If you need another logic the only way is implement you own handlers.
I liked Lufftre’s answer here, but I don’t like the idea of creating a whole class just for this, so I’m basically patching the StreamHandler’s emit function into a lambda function, like so:
chs = logging.StreamHandler()
chs.setLevel(logging.INFO)
chs.semit = chs.emit # You have to "clone" original emit, as there is no super()
chs.emit = lambda record: chs.semit(record) if record.levelno == chs.level else None
logger.addHandler(chs)
I think it looks cleaner. If you want multiple handlers working in this way, then you can first define a function that is this lambda and then patch the emit
with that function object.
# Set up new emit
nemit = lambda record, sh: sh.semit(record) if record.levelno == sh.level else None
# Create first handler
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
ch.semit = ch.emit
ch.emit = lambda record: nemit(record, ch)
# Create second handler
chs = logging.StreamHandler()
chs.setLevel(logging.INFO)
chs.semit = chs.emit
chs.emit = lambda record: nemit(record, chs)
logger.addHandler(ch)
logger.addHandler(chs)
I need two handlers. One for file logging and another one for stream logging in the console. I need to specify levels for each handler. Notice that my levels are going to be something like the following ones.
Stream Handler –> INFO
File Handler –> WARNING, ERROR, CRITICAL
Here is my code.
# Create a custom logger
logger = logging.getLogger('DBMQ')
logger.setLevel(logging.DEBUG)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
logger.addHandler(stream_handler)
file_handler = logging.FileHandler('./data/file.log')
file_handler.setLevel(logging.WARNING)
logger.addHandler(file_handler)
Now, it has to operate like this:
logger.debug('hey') # Nothing should happen
logger.info('hey') # only stream handler should log this message
logger.warning('hey') # only file handler should log this message but the stream does too
logger.error('hey') # only file handler should log this message but the stream does too
logger.critical('hey') # only file handler should log this message but the stream does too
I need to disallow the stream handler from logging the warning
, error
, and critical
logging levels. Is there any way to filter this handler?
You could create your own Handler:
logger = logging.getLogger('DBMQ')
logger.setLevel(logging.DEBUG)
class MyStreamHandler(logging.StreamHandler):
def emit(self, record):
if record.levelno == self.level:
super().emit(record)
stream_handler = MyStreamHandler()
stream_handler.setLevel(logging.INFO)
logger.addHandler(stream_handler)
file_handler = logging.FileHandler('./data/file.log')
file_handler.setLevel(logging.WARNING)
logger.addHandler(file_handler)
The "level" system does not work to as disable/enable single "levels".
https://docs.python.org/3/library/logging.html#logging-levels
The "level" is a single integer value. If the message’s level is over logger/handler level, it will be showed. Just that simple.
If you need another logic the only way is implement you own handlers.
I liked Lufftre’s answer here, but I don’t like the idea of creating a whole class just for this, so I’m basically patching the StreamHandler’s emit function into a lambda function, like so:
chs = logging.StreamHandler()
chs.setLevel(logging.INFO)
chs.semit = chs.emit # You have to "clone" original emit, as there is no super()
chs.emit = lambda record: chs.semit(record) if record.levelno == chs.level else None
logger.addHandler(chs)
I think it looks cleaner. If you want multiple handlers working in this way, then you can first define a function that is this lambda and then patch the emit
with that function object.
# Set up new emit
nemit = lambda record, sh: sh.semit(record) if record.levelno == sh.level else None
# Create first handler
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
ch.semit = ch.emit
ch.emit = lambda record: nemit(record, ch)
# Create second handler
chs = logging.StreamHandler()
chs.setLevel(logging.INFO)
chs.semit = chs.emit
chs.emit = lambda record: nemit(record, chs)
logger.addHandler(ch)
logger.addHandler(chs)