Skip to content

Instantly share code, notes, and snippets.

@ipmb
Last active September 27, 2020 16:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ipmb/a355ee6b2bbc0a4af41e975c725c97ca to your computer and use it in GitHub Desktop.
Save ipmb/a355ee6b2bbc0a4af41e975c725c97ca to your computer and use it in GitHub Desktop.
from __future__ import unicode_literals
import logging
# built-in attributes on LogRecord. Used to determine what is passed in `extras`
RESERVED_ATTRS = (
'args', 'asctime', 'created', 'exc_info', 'exc_text', 'filename',
'funcName', 'levelname', 'levelno', 'lineno', 'module',
'msecs', 'message', 'msg', 'name', 'pathname', 'process',
'processName', 'relativeCreated', 'stack_info', 'thread', 'threadName')
# from Fabric to colorize output
def _color(code):
def inner(text, bold=False):
c = code
if bold:
c = "1;%s" % c
return "\033[%sm%s\033[0m" % (c, text)
return inner
red = _color('31')
green = _color('32')
yellow = _color('33')
blue = _color('34')
white = _color('37')
# from python-json-logger
def _find_extras(record):
extras = {}
for key, value in record.__dict__.items():
# this allows to have numeric keys
starts_under = not (hasattr(key, "startswith") and key.startswith('_'))
if key not in RESERVED_ATTRS and starts_under:
extras[key] = value
return extras
def _color_log_level(text, levelno):
if levelno >= logging.ERROR:
return red(text)
elif levelno == logging.WARNING:
return yellow(text)
elif levelno <= logging.DEBUG:
return blue(text)
return white(text)
class ReadableFormatter(logging.Formatter):
"""
This is meant for local development where "human" readable logs are
important and "machine" readability is not.
Usage in logging.config.dictConfig::
'formatters': {
'readable': {
'class': 'readable_log_formatter.ReadableFormatter',
},
},
'handlers': {
'default': {
'class': 'logging.StreamHandler',
'formatter': 'readable',
},
}
"""
separator = '$'
def __init__(self, *args, **kwargs):
# force format
fmt = ('%(levelname)8s{} %(name)s in %(funcName)s (line:%(lineno)s)\n'
'%(message)s%(readableExtras)s'.format(self.separator))
# replace fmt if supplied as arg instead kwargs
if args and 'fmt' not in kwargs:
args = (fmt,) + args[1:]
else:
kwargs['fmt'] = fmt
super(ReadableFormatter, self).__init__(*args, **kwargs)
def format(self, record):
# extract extras from record
record.readableExtras = '\n'.join(['{}: {}'.format(white(k), v)
for k, v
in _find_extras(record).items()])
if record.readableExtras:
record.readableExtras = '\n' + record.readableExtras
# Color output
out = super(ReadableFormatter, self).format(record)
if record.exc_text:
out = out.replace(record.exc_text, green(record.exc_text))
lines = out.splitlines(True)
level, code_info = lines[0].split(self.separator, 1)
name, details = code_info.lstrip().split(' ', 1)
first_line = '{} {} {}'.format(_color_log_level(level, record.levelno),
white(name, bold=True),
white(details))
lines = [first_line] + lines[1:]
# indent after first line
return (' ' * 9).join(lines)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment