core (logs): log color location can now be specified with %(color_start)s and %(color_end)s

This commit is contained in:
Goffi 2016-04-18 18:33:59 +02:00
commit 304b1eb34b
2 changed files with 40 additions and 19 deletions

View file

@ -19,6 +19,7 @@
"""High level logging functions"""
# XXX: this module use standard logging module when possible, but as SàT can work in different cases where logging is not the best choice (twisted, pyjamas, etc), it is necessary to have a dedicated module. Additional feature like environment variables and colors are also managed.
# TODO: change formatting from "%s" style to "{}" when moved to Python 3
from sat.core.constants import Const as C
from sat.core import exceptions
@ -26,6 +27,8 @@ from sat.core import exceptions
backend = None
_loggers = {}
handlers = {}
COLOR_START = '%(color_start)s'
COLOR_END = '%(color_end)s'
class Filtered(Exception):
@ -154,6 +157,7 @@ class FilterName(object):
class ConfigureBase(object):
LOGGER_CLASS = Logger
_color_location = False # True if color location is specified in fmt (with COLOR_START)
def __init__(self, level=None, fmt=None, output=None, logger=None, colors=False, force_colors=False, backend_data=None):
"""Configure a backend
@ -201,6 +205,11 @@ class ConfigureBase(object):
if fmt is not None:
if fmt != '%(message)s': # %(message)s is the same as None
Logger.fmt = fmt
if COLOR_START in fmt:
ConfigureBase._color_location = True
if fmt.find(COLOR_END,fmt.rfind(COLOR_START))<0:
# color_start not followed by an end, we add it
Logger.fmt += COLOR_END
def configureOutput(self, output):
if output is not None:
@ -249,7 +258,7 @@ class ConfigureBase(object):
handlers[output] = None
elif output == C.LOG_OPT_OUTPUT_FILE:
if not options:
ValueError("%(handler)s output need a path as option" % {'handler': output})
ValueError("{handler} output need a path as option" .format(handle=output))
handlers.setdefault(output, []).append(options)
options = None # option are parsed, we can empty them
elif output == C.LOG_OPT_OUTPUT_MEMORY:
@ -262,7 +271,7 @@ class ConfigureBase(object):
handlers[output] = limit
if options: # we should not have unparsed options
raise ValueError(u"options [%(options)s] are not supported for %(handler)s output" % {'options': options, 'handler': output})
raise ValueError(u"options [{options}] are not supported for {handler} output".format(options=options, handler=output))
@staticmethod
def memoryGet(size=None):
@ -272,8 +281,8 @@ class ConfigureBase(object):
"""
raise NotImplementedError
@staticmethod
def ansiColors(level, message):
@classmethod
def ansiColors(cls, level, message):
"""Colorise message depending on level for terminals
@param level: one of C.LOG_LEVELS
@ -281,26 +290,28 @@ class ConfigureBase(object):
@return: message with ANSI escape codes for coloration
"""
if level == C.LOG_LVL_DEBUG:
out = (C.ANSI_FG_CYAN, message, C.ANSI_RESET)
start = (C.ANSI_FG_CYAN,)
elif level == C.LOG_LVL_INFO:
start = ()
elif level == C.LOG_LVL_WARNING:
out = (C.ANSI_FG_YELLOW, message, C.ANSI_RESET)
start = (C.ANSI_FG_YELLOW,)
elif level == C.LOG_LVL_ERROR:
out = (C.ANSI_FG_RED,
start = (C.ANSI_FG_RED,
C.ANSI_BLINK,
r'/!\ ',
C.ANSI_BLINK_OFF,
message,
C.ANSI_RESET)
C.ANSI_BLINK_OFF)
elif level == C.LOG_LVL_CRITICAL:
out = (C.ANSI_BOLD,
start = (C.ANSI_BOLD,
C.ANSI_FG_RED,
'Guru Meditation ',
C.ANSI_NORMAL_WEIGHT,
message,
C.ANSI_RESET)
C.ANSI_NORMAL_WEIGHT)
else:
out = message
return ''.join(out)
start = ()
if cls._color_location:
return message % {'color_start': ''.join(start),
'color_end': C.ANSI_RESET}
else:
return '%s%s%s' % (''.join(start), message, C.ANSI_RESET)
@staticmethod
def getProfile():
@ -334,7 +345,7 @@ def configure(backend_, **options):
try:
configure_class = configure_cls[backend]
except KeyError:
raise ValueError("unknown backend [%s]" % backend)
raise ValueError("unknown backend [{}]".format(backend))
if backend == C.LOG_BACKEND_CUSTOM:
logger_class = options.pop('logger_class')
configure_class(logger_class, **options)
@ -350,7 +361,7 @@ def getLogger(name=C.LOG_BASE_LOGGER):
try:
logger_class = configure_cls[backend].LOGGER_CLASS
except KeyError:
raise ValueError("This method should not be called with backend [%s]" % backend)
raise ValueError("This method should not be called with backend [{}]".format(backend))
return _loggers.setdefault(name, logger_class(name))
_root_logger = getLogger()

View file

@ -258,6 +258,7 @@ class ConfigureStandard(ConfigureBasic):
self.level = level
def configureFormat(self, fmt):
super(ConfigureStandard, self).configureFormat(fmt)
import logging
class SatFormatter(logging.Formatter):
@ -272,8 +273,17 @@ class ConfigureStandard(ConfigureBasic):
def format(self, record):
if self._with_profile:
record.profile = ConfigureStandard.getProfile()
do_color = self.with_colors and (self.can_colors or self.force_colors)
if ConfigureStandard._color_location:
# we copy raw formatting strings for color_*
# as formatting is handled in ansiColors in this case
if do_color:
record.color_start = log.COLOR_START
record.color_end = log.COLOR_END
else:
record.color_start = record.color_end = ''
s = super(SatFormatter, self).format(record)
if self.with_colors and (self.can_colors or self.force_colors):
if do_color:
s = ConfigureStandard.ansiColors(record.levelname, s)
return s