Created
January 26, 2013 16:14
-
-
Save rh0dium/4643083 to your computer and use it in GitHub Desktop.
Adds a colorizing stream handler..
This file contains hidden or 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
# -*- 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