Skip to content

Instantly share code, notes, and snippets.

@rh0dium
Created January 26, 2013 16:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rh0dium/4643083 to your computer and use it in GitHub Desktop.
Save rh0dium/4643083 to your computer and use it in GitHub Desktop.
Adds a colorizing stream handler..
# -*- coding: utf-8 -*-
"""logging.py: Django www"""
import ctypes
import logging
import platform
import os
__author__ = 'Steven Klass'
__date__ = '2/24/12 10:20 PM'
__credits__ = ['Vinay Sajip', 'Steven Klass']
class LevelLTEInfo(logging.Filter):
"""Just filter everything <= INFO"""
def filter(self, record):
"""Determine if the specified record is to be logged.
:param record: record
"""
return record.levelno <= logging.INFO
class ManagementExclude(logging.Filter):
"""Filter out management commands"""
def filter(self, record):
"""Determine if the specified record is to be logged.
:param record: record
"""
return record.name.split(".")[-1] != "management"
class ColorizingStreamHandler(logging.StreamHandler):
"""A handler class which writes colored logging records, appropriately formatted, to a stream.
Note that this class does not close the stream, as sys.stdout or sys.stderr may be used.
"""
color_map = {
'black': 0,
'red': 1,
'green': 2,
'yellow': 3,
'blue': 4,
'magenta': 5,
'cyan': 6,
'white': 7,
}
#levels to (background, foreground, bold/intense)
if os.name == 'nt':
level_map = {
logging.DEBUG: (None, 'blue', True),
logging.INFO: (None, 'white', False),
logging.WARNING: (None, 'yellow', True),
logging.ERROR: (None, 'red', True),
logging.CRITICAL: ('red', 'white', True),
}
else:
level_map = {
logging.DEBUG: (None, 'cyan', False),
logging.INFO: (None, 'black', False),
logging.WARNING: (None, 'yellow', False),
logging.ERROR: (None, 'red', False),
logging.CRITICAL: ('red', 'white', True),
}
if os.getenv('DJANGO_COLORS') == 'dark':
level_map[logging.INFO] = (None, 'white', False)
csi = '\x1b['
reset = '\x1b[0m'
@property
def is_tty(self):
"""Are we a tty"""
if (hasattr(self.stream, "isatty") and self.stream.isatty()) \
or os.environ.get('TERM') == 'ANSI' or os.environ.get('PYCHARM_HOSTED'):
if platform.system() == 'Windows' and not os.environ.get('TERM') == 'ANSI':
return False
else:
return True
else:
return False
def emit(self, record):
"""Emit a record.
:param record: The record element.
"""
try:
message = self.format(record)
stream = self.stream
if not self.is_tty:
stream.write(message)
else:
self.output_colorized(message)
stream.write(getattr(self, 'terminator', '\n'))
self.flush()
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
if os.name != 'nt':
def output_colorized(self, message):
"""Output color
:param message: message to be colored.
"""
self.stream.write(message)
else:
import re
ansi_esc = re.compile(r'\x1b\[((?:\d+)(?:;(?:\d+))*)m')
nt_color_map = {
0: 0x00, # black
1: 0x04, # red
2: 0x02, # green
3: 0x06, # yellow
4: 0x01, # blue
5: 0x05, # magenta
6: 0x03, # cyan
7: 0x07, # white
}
def output_colorized(self, message):
"""Colorize the ouput
:param message: The message to be colored.
"""
parts = self.ansi_esc.split(message)
write = self.stream.write
h = None
fd = getattr(self.stream, 'fileno', None)
if fd is not None:
fd = fd()
if fd in (1, 2): # stdout or stderr
h = ctypes.windll.kernel32.GetStdHandle(-10 - fd)
while parts:
text = parts.pop(0)
if text:
write(text)
if parts:
params = parts.pop(0)
if h is not None:
params = [int(p) for p in params.split(';')]
color = 0
for p in params:
if 40 <= p <= 47:
color |= self.nt_color_map[p - 40] << 4
elif 30 <= p <= 37:
color |= self.nt_color_map[p - 30]
elif p == 1:
color |= 0x08 # foreground intensity on
elif p == 0: # reset to default color
color = 0x07
else:
pass # error condition ignored
ctypes.windll.kernel32.SetConsoleTextAttribute(h, color)
def colorize(self, message, record):
"""Colorize the output.
:param record: The record
:param message: The message
"""
if record.levelno in self.level_map:
bg, fg, bold = self.level_map[record.levelno]
params = []
if bg in self.color_map:
params.append(str(self.color_map[bg] + 40))
if fg in self.color_map:
params.append(str(self.color_map[fg] + 30))
if bold:
params.append('1')
if params:
message = ''.join((self.csi, ';'.join(params),
'm', message, self.reset))
return message
def format(self, record):
"""Format this output
:param record: The record
"""
message = logging.StreamHandler.format(self, record)
if self.is_tty:
# Don't colorize any traceback
parts = message.split('\n', 1)
parts[0] = self.colorize(parts[0], record)
message = '\n'.join(parts)
return message
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment