114 lines
3.9 KiB
Python
114 lines
3.9 KiB
Python
"""Nicer log formatting with colours.
|
|
|
|
Code copied from Tornado, Apache licensed.
|
|
"""
|
|
# Copyright 2012 Facebook
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import logging
|
|
import sys
|
|
|
|
try:
|
|
import curses
|
|
except ImportError:
|
|
curses = None
|
|
|
|
|
|
def _stderr_supports_color():
|
|
color = False
|
|
if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
|
|
try:
|
|
curses.setupterm()
|
|
if curses.tigetnum("colors") > 0:
|
|
color = True
|
|
except Exception:
|
|
pass
|
|
return color
|
|
|
|
|
|
class LogFormatter(logging.Formatter):
|
|
"""Log formatter with colour support
|
|
"""
|
|
DEFAULT_COLORS = {
|
|
logging.INFO: 2, # Green
|
|
logging.WARNING: 3, # Yellow
|
|
logging.ERROR: 1, # Red
|
|
logging.CRITICAL: 1,
|
|
}
|
|
|
|
def __init__(self, color=True, datefmt=None):
|
|
r"""
|
|
:arg bool color: Enables color support.
|
|
:arg string fmt: Log message format.
|
|
It will be applied to the attributes dict of log records. The
|
|
text between ``%(color)s`` and ``%(end_color)s`` will be colored
|
|
depending on the level if color support is on.
|
|
:arg dict colors: color mappings from logging level to terminal color
|
|
code
|
|
:arg string datefmt: Datetime format.
|
|
Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``.
|
|
.. versionchanged:: 3.2
|
|
Added ``fmt`` and ``datefmt`` arguments.
|
|
"""
|
|
logging.Formatter.__init__(self, datefmt=datefmt)
|
|
self._colors = {}
|
|
if color and _stderr_supports_color():
|
|
# The curses module has some str/bytes confusion in
|
|
# python3. Until version 3.2.3, most methods return
|
|
# bytes, but only accept strings. In addition, we want to
|
|
# output these strings with the logging module, which
|
|
# works with unicode strings. The explicit calls to
|
|
# unicode() below are harmless in python2 but will do the
|
|
# right conversion in python 3.
|
|
fg_color = (curses.tigetstr("setaf") or
|
|
curses.tigetstr("setf") or "")
|
|
|
|
for levelno, code in self.DEFAULT_COLORS.items():
|
|
self._colors[levelno] = str(
|
|
curses.tparm(fg_color, code), "ascii")
|
|
self._normal = str(curses.tigetstr("sgr0"), "ascii")
|
|
|
|
scr = curses.initscr()
|
|
self.termwidth = scr.getmaxyx()[1]
|
|
curses.endwin()
|
|
else:
|
|
self._normal = ''
|
|
# Default width is usually 80, but too wide is
|
|
# worse than too narrow
|
|
self.termwidth = 70
|
|
|
|
def formatMessage(self, record):
|
|
mlen = len(record.message)
|
|
right_text = '{initial}-{name}'.format(initial=record.levelname[0],
|
|
name=record.name)
|
|
if mlen + len(right_text) < self.termwidth:
|
|
space = ' ' * (self.termwidth - (mlen + len(right_text)))
|
|
else:
|
|
space = ' '
|
|
|
|
if record.levelno in self._colors:
|
|
start_color = self._colors[record.levelno]
|
|
end_color = self._normal
|
|
else:
|
|
start_color = end_color = ''
|
|
|
|
return record.message + space + start_color + right_text + end_color
|
|
|
|
|
|
def enable_colourful_output(level=logging.INFO):
|
|
handler = logging.StreamHandler()
|
|
handler.setFormatter(LogFormatter())
|
|
logging.root.addHandler(handler)
|
|
logging.root.setLevel(level)
|