Modifying logging message format based on message logging level in Python3
For some weird reasons, the solutions of @JS and @Evpok were raising some errors (I am using Python 3.7 and that might be why).
This solution worked for me:
class CustomFormatter(logging.Formatter):
"""Logging Formatter to add colors and count warning / errors"""
FORMATS = {
logging.ERROR: "ERROR: %(msg)s",
logging.WARNING: "WARNING: %(msg)s",
logging.DEBUG: "DBG: %(module)s: %(lineno)d: %(msg)s",
"DEFAULT": "%(msg)s",
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
formatter = logging.Formatter(log_fmt)
return formatter.format(record)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger_ch = logging.StreamHandler()
logger_ch.setLevel(logging.INFO)
logger_ch.setFormatter(CustomFormatter())
logger.addHandler(logger_ch)
Cross-posting of another answer. It doesn't work because of the new (3.2+, 3.4 as of now) implementation of logging.Formatter
which now relies on formatting styles. This relies on the '{'
style format, but it can be adapted. Could be refined to be more general and allow selection of formatting style and custom messages as arguments to __init__
, too.
class SpecialFormatter(logging.Formatter):
FORMATS = {logging.DEBUG : logging._STYLES['{']("{module} DEBUG: {lineno}: {message}"),
logging.ERROR : logging._STYLES['{']("{module} ERROR: {message}"),
logging.INFO : logging._STYLES['{']("{module}: {message}"),
'DEFAULT' : logging._STYLES['{']("{module}: {message}")}
def format(self, record):
# Ugly. Should be better
self._style = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
return logging.Formatter.format(self, record)
hdlr = logging.StreamHandler(sys.stderr)
hdlr.setFormatter(SpecialFormatter())
logging.root.addHandler(hdlr)
logging.root.setLevel(logging.INFO)
I prefer this because it's shorter, simpler and does not require strings like 'ERROR' to be hard coded. No need to reset ._fmt
, because else:
can handle that just fine.
Also, using "%(msg)s"
doesn't work with lazy logging!
class Formatter(logging.Formatter):
def format(self, record):
if record.levelno == logging.INFO:
self._style._fmt = "%(message)s"
else:
self._style._fmt = "%(levelname)s: %(message)s"
return super().format(record)
Usage example:
import logging
logger = logging.getLogger()
handler = logging.StreamHandler()
handler.setFormatter(Formatter())
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
logger.debug('foo')
logger.info('bar %d', 4)
DEBUG: foo
bar 4
In case you want to have the levelname colored:
class Formatter(logging.Formatter):
def format(self, record):
if record.levelno == logging.INFO:
self._style._fmt = "%(message)s"
else:
color = {
logging.WARNING: 33,
logging.ERROR: 31,
logging.FATAL: 31,
logging.DEBUG: 36
}.get(record.levelno, 0)
self._style._fmt = f"\033[{color}m%(levelname)s\033[0m: %(message)s"
return super().format(record)
see https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit for color numbers
With a bit of digging, I was able to modify the Python 2 solution to work with Python 3. In Python2, it was necessary to temporarily overwrite Formatter._fmt
. In Python3, support for multiple format string types requires us to temporarily overwrite Formatter._style._fmt
instead.
# Custom formatter
class MyFormatter(logging.Formatter):
err_fmt = "ERROR: %(msg)s"
dbg_fmt = "DBG: %(module)s: %(lineno)d: %(msg)s"
info_fmt = "%(msg)s"
def __init__(self):
super().__init__(fmt="%(levelno)d: %(msg)s", datefmt=None, style='%')
def format(self, record):
# Save the original format configured by the user
# when the logger formatter was instantiated
format_orig = self._style._fmt
# Replace the original format with one customized by logging level
if record.levelno == logging.DEBUG:
self._style._fmt = MyFormatter.dbg_fmt
elif record.levelno == logging.INFO:
self._style._fmt = MyFormatter.info_fmt
elif record.levelno == logging.ERROR:
self._style._fmt = MyFormatter.err_fmt
# Call the original formatter class to do the grunt work
result = logging.Formatter.format(self, record)
# Restore the original format configured by the user
self._style._fmt = format_orig
return result
And here is Halloleo's example of how to use the above in your script (from the Python2 version of this question):
fmt = MyFormatter()
hdlr = logging.StreamHandler(sys.stdout)
hdlr.setFormatter(fmt)
logging.root.addHandler(hdlr)
logging.root.setLevel(logging.DEBUG)