Created
July 23, 2011 21:10
-
-
Save natedub/1101888 to your computer and use it in GitHub Desktop.
Colorizing and formatting Python logging console output for better readability
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This colorizing :class:`logging.Formatter` implementation adds ANSI color | |
sequences to make logging output more readable. | |
SQLAlchemy queryies are treated specially: Pygments formats the query | |
string and the bind params. Python tracebacks are also formatted by | |
Pygments. Package names vary their colors as well, see the MY_APP_NAME | |
setting. | |
If you're configuring logging via a INI file, add this formatter like so: | |
[formatters] | |
keys = ..., colorized | |
[handler_console] | |
class = StreamHandler | |
args = (sys.stderr,) | |
level = NOTSET | |
formatter = colorized | |
[formatter_colorized] | |
class = colorformatter.AnsiColorFormatter | |
NOTE: Requires Pygments and Fabric, both available on PyPi. | |
Enjoy! | |
.. author:: Nathan Wright <[email protected]> | |
""" | |
# | |
# Copyright 2011 Nathan Wright <[email protected]> | |
# | |
import logging | |
import re | |
import sys | |
from fabric.colors import blue | |
from fabric.colors import cyan | |
from fabric.colors import green | |
from fabric.colors import magenta | |
from fabric.colors import red | |
from fabric.colors import yellow | |
from pygments import highlight | |
from pygments.formatters import TerminalFormatter | |
from pygments.lexers import MySqlLexer | |
from pygments.lexers import PythonLexer | |
from pygments.lexers import PythonTracebackLexer | |
mysql_lexer = MySqlLexer() | |
python_lexer = PythonLexer() | |
python_traceback_lexer = PythonTracebackLexer() | |
terminal_formatter = TerminalFormatter() | |
re_color_codes = re.compile(r'\033\[(\d;)?\d+m') | |
MY_APP_NAME = 'myapp.' | |
LEVELS = { | |
'WARNING': red(' WARN', bold=True), | |
'INFO': blue(' INFO'), | |
'DEBUG': blue('DEBUG'), | |
'CRITICAL': magenta(' CRIT'), | |
'ERROR': red('ERROR'), | |
} | |
class AnsiColorFormatter(logging.Formatter): | |
def __init__(self, msgfmt=None, datefmt=None): | |
logging.Formatter.__init__(self, None, '%H:%M:%S') | |
def format(self, record): | |
""" | |
Format the specified record as text. | |
The record's attribute dictionary is used as the operand to a | |
string formatting operation which yields the returned string. | |
Before formatting the dictionary, a couple of preparatory steps | |
are carried out. The message attribute of the record is computed | |
using LogRecord.getMessage(). If the formatting string contains | |
"%(asctime)", formatTime() is called to format the event time. | |
If there is exception information, it is formatted using | |
formatException() and appended to the message. | |
""" | |
message = record.getMessage() | |
asctime = self.formatTime(record, self.datefmt) | |
name = record.name | |
if name == 'sqlalchemy.engine.base.Engine': | |
name = green('sqlalchemy') | |
if record.levelname == 'INFO': | |
if message.startswith('('): | |
lexer = python_lexer | |
else: | |
lexer = mysql_lexer | |
message = highlight(message, lexer, terminal_formatter).rstrip() | |
elif name.startswith(MY_APP_NAME): | |
name = yellow(name) | |
else: | |
name = cyan(name) | |
s = '%(timestamp)s %(threadName)s %(levelname)s %(name)s ' % { | |
'timestamp': green('%s,%03d' % (asctime, record.msecs), bold=True), | |
'levelname': LEVELS[record.levelname], | |
'name': name, | |
'threadName': yellow(record.threadName, bold=True) | |
} | |
if "\n" in message: | |
indent_length = len(re_color_codes.sub('', s)) | |
message = message.replace("\n", "\n" + ' ' * indent_length) | |
s += message | |
if record.exc_info: | |
# Cache the traceback text to avoid converting it multiple times | |
# (it's constant anyway) | |
if not record.exc_text: | |
record.exc_text = self.formatException(record.exc_info) | |
if record.exc_text: | |
if s[-1:] != "\n": | |
s = s + "\n" | |
try: | |
exc_text = record.exc_text | |
except UnicodeError: | |
# Sometimes filenames have non-ASCII chars, which can lead | |
# to errors when s is Unicode and record.exc_text is str | |
# See issue 8924 | |
exc_text = record.exc_text.decode(sys.getfilesystemencoding()) | |
s += highlight(exc_text, python_traceback_lexer, terminal_formatter) | |
return s |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Instead of relying on fabric for colors (why using a deployment library for coloring?) you could use fabulous instead.