core (log): moved configuration to core.log_config; this avoid import issues with pyjamas.
This commit is contained in:
parent
daa4b0b30b
commit
ed2abd3971
1 changed files with 497 additions and 465 deletions
611
src/core/log.py
611
src/core/log.py
|
|
@ -23,9 +23,9 @@
|
|||
from sat.core.constants import Const as C
|
||||
from sat.core import exceptions
|
||||
|
||||
_backend = None
|
||||
backend = None
|
||||
_loggers = {}
|
||||
_handlers = {}
|
||||
handlers = {}
|
||||
|
||||
|
||||
class Filtered(Exception):
|
||||
|
|
@ -101,12 +101,11 @@ class Logger(object):
|
|||
except KeyError as e:
|
||||
if e.args[0] == 'profile':
|
||||
# XXX: %(profile)s use some magic with introspection, for debugging purpose only *DO NOT* use in production
|
||||
record['profile'] = _getProfile()
|
||||
record['profile'] = configure_cls[backend].getProfile()
|
||||
return self.fmt % record
|
||||
else:
|
||||
raise e
|
||||
|
||||
|
||||
def debug(self, msg):
|
||||
self.log(C.LOG_LVL_DEBUG, msg)
|
||||
|
||||
|
|
@ -123,23 +122,6 @@ class Logger(object):
|
|||
self.log(C.LOG_LVL_CRITICAL, msg)
|
||||
|
||||
|
||||
class TwistedLogger(Logger):
|
||||
colors = True
|
||||
force_colors = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TwistedLogger, self).__init__(*args, **kwargs)
|
||||
from twisted.python import log
|
||||
self.twisted_log = log
|
||||
|
||||
def out(self, message, level=None):
|
||||
"""Actually log the message
|
||||
|
||||
@param message: formatted message
|
||||
"""
|
||||
self.twisted_log.msg(message.encode('utf-8', 'ignore'), sat_logged=True, level=level)
|
||||
|
||||
|
||||
class FilterName(object):
|
||||
"""Filter on logger name according to a regex"""
|
||||
|
||||
|
|
@ -169,83 +151,13 @@ class FilterName(object):
|
|||
log_record.name = dict_record['name']
|
||||
return self.filter(log_record) == 1
|
||||
|
||||
def memoryGet(size=None):
|
||||
"""Return buffered logs
|
||||
|
||||
@param size: number of logs to return
|
||||
"""
|
||||
if not C.LOG_OPT_OUTPUT_MEMORY in _handlers:
|
||||
raise ValueError('memory output is not used')
|
||||
if _backend == C.LOG_BACKEND_STANDARD:
|
||||
mem_handler = _handlers[C.LOG_OPT_OUTPUT_MEMORY]
|
||||
return (log_msg for log_msg in mem_handler.buffer[size if size is None else -size:])
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def _getProfile():
|
||||
"""Try to find profile value using introspection"""
|
||||
import inspect
|
||||
stack = inspect.stack()
|
||||
current_path = stack[0][1]
|
||||
for frame_data in stack[:-1]:
|
||||
if frame_data[1] != current_path:
|
||||
if _backend == C.LOG_BACKEND_STANDARD and "/logging/__init__.py" in frame_data[1]:
|
||||
continue
|
||||
break
|
||||
|
||||
frame = frame_data[0]
|
||||
args = inspect.getargvalues(frame)
|
||||
try:
|
||||
profile = args.locals.get('profile') or args.locals['profile_key']
|
||||
except (TypeError, KeyError):
|
||||
try:
|
||||
try:
|
||||
profile = args.locals['self'].profile
|
||||
except AttributeError:
|
||||
try:
|
||||
profile = args.locals['self'].parent.profile
|
||||
except AttributeError:
|
||||
profile = args.locals['self'].host.profile # used in quick_frontend for single profile configuration
|
||||
except Exception:
|
||||
# we can't find profile, we return an empty value
|
||||
profile = ''
|
||||
return profile
|
||||
|
||||
def _ansiColors(level, message):
|
||||
"""Colorise message depending on level for terminals
|
||||
|
||||
@param level: one of C.LOG_LEVELS
|
||||
@param message: formatted message to log
|
||||
@return: message with ANSI escape codes for coloration
|
||||
"""
|
||||
if level == C.LOG_LVL_DEBUG:
|
||||
out = (C.ANSI_FG_CYAN, message, C.ANSI_RESET)
|
||||
elif level == C.LOG_LVL_WARNING:
|
||||
out = (C.ANSI_FG_YELLOW, message, C.ANSI_RESET)
|
||||
elif level == C.LOG_LVL_ERROR:
|
||||
out = (C.ANSI_FG_RED,
|
||||
C.ANSI_BLINK,
|
||||
r'/!\ ',
|
||||
C.ANSI_BLINK_OFF,
|
||||
message,
|
||||
C.ANSI_RESET)
|
||||
elif level == C.LOG_LVL_CRITICAL:
|
||||
out = (C.ANSI_BOLD,
|
||||
C.ANSI_FG_RED,
|
||||
'Guru Meditation ',
|
||||
C.ANSI_NORMAL_WEIGHT,
|
||||
message,
|
||||
C.ANSI_RESET)
|
||||
else:
|
||||
out = message
|
||||
return ''.join(out)
|
||||
|
||||
|
||||
class Configure(object):
|
||||
class ConfigureBase(object):
|
||||
LOGGER_CLASS = Logger
|
||||
|
||||
def __init__(self, level=None, fmt=None, output=None, logger=None, colors=False, force_colors=False):
|
||||
"""Configure backend
|
||||
"""Configure a backend
|
||||
|
||||
@param level: one of C.LOG_LEVELS
|
||||
@param fmt: format string, pretty much as in std logging. Accept the following keywords (maybe more depending on backend):
|
||||
- "message"
|
||||
|
|
@ -275,72 +187,6 @@ class Configure(object):
|
|||
def preTreatment(self):
|
||||
pass
|
||||
|
||||
def configureLevel(self, level):
|
||||
pass
|
||||
|
||||
def configureFormat(self, fmt):
|
||||
pass
|
||||
|
||||
def configureOutput(self, output):
|
||||
pass
|
||||
|
||||
def configureLogger(self, logger):
|
||||
pass
|
||||
|
||||
def configureColors(self, colors, force_colors):
|
||||
pass
|
||||
|
||||
def postTreatment(self):
|
||||
pass
|
||||
|
||||
def manageOutputs(self, outputs_raw):
|
||||
""" Parse output option in a backend agnostic way, and fill _handlers consequently
|
||||
|
||||
@param outputs_raw: output option as enterred in environment variable or in configuration
|
||||
"""
|
||||
if not outputs_raw:
|
||||
return
|
||||
outputs = outputs_raw.split(C.LOG_OPT_OUTPUT_SEP)
|
||||
global _handlers
|
||||
if len(outputs) == 1:
|
||||
_handlers[C.LOG_OPT_OUTPUT_FILE] = [outputs.pop()]
|
||||
|
||||
for output in outputs:
|
||||
if not output:
|
||||
continue
|
||||
if output[-1] == ')':
|
||||
# we have options
|
||||
opt_begin = output.rfind('(')
|
||||
options = output[opt_begin+1:-1]
|
||||
output = output[:opt_begin]
|
||||
else:
|
||||
options = None
|
||||
|
||||
if output not in (C.LOG_OPT_OUTPUT_DEFAULT, C.LOG_OPT_OUTPUT_FILE, C.LOG_OPT_OUTPUT_MEMORY):
|
||||
raise ValueError(u"Invalid output [%s]" % output)
|
||||
|
||||
if output == C.LOG_OPT_OUTPUT_DEFAULT:
|
||||
# no option for defaut handler
|
||||
_handlers[output] = None
|
||||
elif output == C.LOG_OPT_OUTPUT_FILE:
|
||||
if not options:
|
||||
ValueError("%(handler)s output need a path as option" % {'handler': output})
|
||||
_handlers.setdefault(output, []).append(options)
|
||||
options = None # option are parsed, we can empty them
|
||||
elif output == C.LOG_OPT_OUTPUT_MEMORY:
|
||||
# we have memory handler, option can be the len limit or None
|
||||
try:
|
||||
limit = int(options)
|
||||
options = None # option are parsed, we can empty them
|
||||
except (TypeError, ValueError):
|
||||
limit = C.LOG_OPT_OUTPUT_MEMORY_LIMIT
|
||||
_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})
|
||||
|
||||
|
||||
class ConfigureBasic(Configure):
|
||||
def configureLevel(self, level):
|
||||
if level is not None:
|
||||
# we deactivate methods below level
|
||||
|
|
@ -366,363 +212,144 @@ class ConfigureBasic(Configure):
|
|||
Logger.filter_name = FilterName(logger)
|
||||
|
||||
def configureColors(self, colors, force_colors):
|
||||
if colors:
|
||||
import sys
|
||||
if force_colors or sys.stdout.isatty(): # FIXME: isatty should be tested on each handler, not globaly
|
||||
# we need colors
|
||||
Logger.post_treat = lambda self, level, message: _ansiColors(level, message)
|
||||
elif force_colors:
|
||||
raise ValueError("force_colors can't be used if colors is False")
|
||||
|
||||
|
||||
class ConfigureTwisted(ConfigureBasic):
|
||||
LOGGER_CLASS = TwistedLogger
|
||||
|
||||
def changeObserver(self, observer, can_colors=False):
|
||||
"""Install a hook on observer to manage SàT specificities
|
||||
|
||||
@param observer: original observer to hook
|
||||
@param can_colors: True if observer can display ansi colors
|
||||
"""
|
||||
def observer_hook(event):
|
||||
"""redirect non SàT log to twisted_logger, and add colors when possible"""
|
||||
if 'sat_logged' in event: # we only want our own logs, other are managed by twistedObserver
|
||||
# we add colors if possible
|
||||
if (can_colors and self.LOGGER_CLASS.colors) or self.LOGGER_CLASS.force_colors:
|
||||
message = event.get('message', tuple())
|
||||
level = event.get('level', C.LOG_LVL_INFO)
|
||||
if message:
|
||||
event['message'] = (_ansiColors(level, ''.join(message)),) # must be a tuple
|
||||
observer(event) # we can now call the original observer
|
||||
|
||||
return observer_hook
|
||||
|
||||
def changeFileLogObserver(self, observer):
|
||||
"""Install SàT hook for FileLogObserver
|
||||
|
||||
if the output is a tty, we allow colors, else we don't
|
||||
@param observer: original observer to hook
|
||||
"""
|
||||
log_obs = observer.__self__
|
||||
log_file = log_obs.write.__self__
|
||||
try:
|
||||
can_colors = log_file.isatty()
|
||||
except AttributeError:
|
||||
can_colors = False
|
||||
return self.changeObserver(observer, can_colors=can_colors)
|
||||
|
||||
def installObserverHook(self, observer):
|
||||
"""Check observer type and install SàT hook when possible
|
||||
|
||||
@param observer: observer to hook
|
||||
@return: hooked observer or original one
|
||||
"""
|
||||
if hasattr(observer, '__self__'):
|
||||
ori = observer
|
||||
if isinstance(observer.__self__, self.log.FileLogObserver):
|
||||
observer = self.changeFileLogObserver(observer)
|
||||
elif isinstance(observer.__self__, self.log.DefaultObserver):
|
||||
observer = self.changeObserver(observer, can_colors=True)
|
||||
else:
|
||||
# we use print because log system is not fully initialized
|
||||
print("Unmanaged observer [%s]" % observer)
|
||||
return observer
|
||||
self.observers[ori] = observer
|
||||
return observer
|
||||
|
||||
def preTreatment(self):
|
||||
"""initialise needed attributes, and install observers hooks"""
|
||||
self.observers = {}
|
||||
from twisted.python import log
|
||||
self.log = log
|
||||
self.log_publisher = log.msg.__self__
|
||||
def addObserverObserver(self_logpub, other):
|
||||
"""Install hook so we know when a new observer is added"""
|
||||
other = self.installObserverHook(other)
|
||||
return self_logpub._originalAddObserver(other)
|
||||
def removeObserverObserver(self_logpub, ori):
|
||||
"""removeObserver hook fix
|
||||
|
||||
As we wrap the original observer, the original removeObserver may want to remove the original object instead of the wrapper, this method fix this
|
||||
"""
|
||||
if ori in self.observers:
|
||||
self_logpub._originalRemoveObserver(self.observers[ori])
|
||||
else:
|
||||
try:
|
||||
self_logpub._originalRemoveObserver(ori)
|
||||
except ValueError:
|
||||
try:
|
||||
ori in self.cleared_observers
|
||||
except AttributeError:
|
||||
raise ValueError("Unknown observer")
|
||||
|
||||
# we replace addObserver/removeObserver by our own
|
||||
log.LogPublisher._originalAddObserver = log.LogPublisher.addObserver
|
||||
log.LogPublisher._originalRemoveObserver = log.LogPublisher.removeObserver
|
||||
import types # see https://stackoverflow.com/a/4267590 (thx Chris Morgan/aaronasterling)
|
||||
log.addObserver = types.MethodType(addObserverObserver, self.log_publisher, log.LogPublisher)
|
||||
log.removeObserver = types.MethodType(removeObserverObserver, self.log_publisher, log.LogPublisher)
|
||||
|
||||
# we now change existing observers
|
||||
for idx, observer in enumerate(self.log_publisher.observers):
|
||||
self.log_publisher.observers[idx] = self.installObserverHook(observer)
|
||||
|
||||
|
||||
def configureLevel(self, level):
|
||||
self.LOGGER_CLASS.level = level
|
||||
super(ConfigureTwisted, self).configureLevel(level)
|
||||
|
||||
def configureOutput(self, output):
|
||||
import sys
|
||||
if output is None:
|
||||
output = C.LOG_OPT_OUTPUT_SEP + C.LOG_OPT_OUTPUT_DEFAULT
|
||||
self.manageOutputs(output)
|
||||
addObserver = self.log.addObserver
|
||||
|
||||
if C.LOG_OPT_OUTPUT_DEFAULT in _handlers:
|
||||
# default output is already managed, we just add output to stdout if we are in debug mode
|
||||
from twisted.internet import defer
|
||||
if defer.Deferred.debug:
|
||||
addObserver(self.log.FileLogObserver(sys.stdout).emit)
|
||||
else:
|
||||
# \\default is not in the output, so we remove current observers
|
||||
self.cleared_observers = self.log_publisher.observers
|
||||
self.observers.clear()
|
||||
del self.log_publisher.observers[:]
|
||||
# and we forbid twistd to add any observer
|
||||
self.log.addObserver = lambda other: None
|
||||
|
||||
if C.LOG_OPT_OUTPUT_FILE in _handlers:
|
||||
from twisted.python import logfile
|
||||
for path in _handlers[C.LOG_OPT_OUTPUT_FILE]:
|
||||
log_file = sys.stdout if path == '-' else logfile.LogFile.fromFullPath(path)
|
||||
addObserver(self.log.FileLogObserver(log_file).emit)
|
||||
|
||||
if C.LOG_OPT_OUTPUT_MEMORY in _handlers:
|
||||
raise NotImplementedError("Memory observer is not implemented in Twisted backend")
|
||||
|
||||
def configureColors(self, colors, force_colors):
|
||||
self.LOGGER_CLASS.colors = colors
|
||||
self.LOGGER_CLASS.force_colors = force_colors
|
||||
if force_colors and not colors:
|
||||
raise ValueError('colors must be True if force_colors is True')
|
||||
pass
|
||||
|
||||
def postTreatment(self):
|
||||
"""Install twistedObserver which manage non SàT logs"""
|
||||
def twistedObserver(event):
|
||||
"""Observer which redirect log message not produced by SàT to SàT logging system"""
|
||||
if not 'sat_logged' in event:
|
||||
# this log was not produced by SàT
|
||||
from twisted.python import log
|
||||
text = log.textFromEventDict(event)
|
||||
if text is None:
|
||||
return
|
||||
twisted_logger = getLogger(C.LOG_TWISTED_LOGGER)
|
||||
log_method = twisted_logger.error if event.get('isError', False) else twisted_logger.info
|
||||
log_method(text.decode('utf-8'))
|
||||
pass
|
||||
|
||||
self.log_publisher._originalAddObserver(twistedObserver)
|
||||
def manageOutputs(self, outputs_raw):
|
||||
""" Parse output option in a backend agnostic way, and fill handlers consequently
|
||||
|
||||
@param outputs_raw: output option as enterred in environment variable or in configuration
|
||||
"""
|
||||
if not outputs_raw:
|
||||
return
|
||||
outputs = outputs_raw.split(C.LOG_OPT_OUTPUT_SEP)
|
||||
global handlers
|
||||
if len(outputs) == 1:
|
||||
handlers[C.LOG_OPT_OUTPUT_FILE] = [outputs.pop()]
|
||||
|
||||
for output in outputs:
|
||||
if not output:
|
||||
continue
|
||||
if output[-1] == ')':
|
||||
# we have options
|
||||
opt_begin = output.rfind('(')
|
||||
options = output[opt_begin+1:-1]
|
||||
output = output[:opt_begin]
|
||||
else:
|
||||
options = None
|
||||
|
||||
if output not in (C.LOG_OPT_OUTPUT_DEFAULT, C.LOG_OPT_OUTPUT_FILE, C.LOG_OPT_OUTPUT_MEMORY):
|
||||
raise ValueError(u"Invalid output [%s]" % output)
|
||||
|
||||
if output == C.LOG_OPT_OUTPUT_DEFAULT:
|
||||
# no option for defaut handler
|
||||
handlers[output] = None
|
||||
elif output == C.LOG_OPT_OUTPUT_FILE:
|
||||
if not options:
|
||||
ValueError("%(handler)s output need a path as option" % {'handler': output})
|
||||
handlers.setdefault(output, []).append(options)
|
||||
options = None # option are parsed, we can empty them
|
||||
elif output == C.LOG_OPT_OUTPUT_MEMORY:
|
||||
# we have memory handler, option can be the len limit or None
|
||||
try:
|
||||
limit = int(options)
|
||||
options = None # option are parsed, we can empty them
|
||||
except (TypeError, ValueError):
|
||||
limit = C.LOG_OPT_OUTPUT_MEMORY_LIMIT
|
||||
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})
|
||||
|
||||
@staticmethod
|
||||
def memoryGet(size=None):
|
||||
"""Return buffered logs
|
||||
|
||||
@param size: number of logs to return
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def ansiColors(level, message):
|
||||
"""Colorise message depending on level for terminals
|
||||
|
||||
@param level: one of C.LOG_LEVELS
|
||||
@param message: formatted message to log
|
||||
@return: message with ANSI escape codes for coloration
|
||||
"""
|
||||
if level == C.LOG_LVL_DEBUG:
|
||||
out = (C.ANSI_FG_CYAN, message, C.ANSI_RESET)
|
||||
elif level == C.LOG_LVL_WARNING:
|
||||
out = (C.ANSI_FG_YELLOW, message, C.ANSI_RESET)
|
||||
elif level == C.LOG_LVL_ERROR:
|
||||
out = (C.ANSI_FG_RED,
|
||||
C.ANSI_BLINK,
|
||||
r'/!\ ',
|
||||
C.ANSI_BLINK_OFF,
|
||||
message,
|
||||
C.ANSI_RESET)
|
||||
elif level == C.LOG_LVL_CRITICAL:
|
||||
out = (C.ANSI_BOLD,
|
||||
C.ANSI_FG_RED,
|
||||
'Guru Meditation ',
|
||||
C.ANSI_NORMAL_WEIGHT,
|
||||
message,
|
||||
C.ANSI_RESET)
|
||||
else:
|
||||
out = message
|
||||
return ''.join(out)
|
||||
|
||||
@staticmethod
|
||||
def getProfile():
|
||||
"""Try to find profile value using introspection"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ConfigureCustom(ConfigureBasic):
|
||||
class ConfigureCustom(ConfigureBase):
|
||||
LOGGER_CLASS = None
|
||||
|
||||
def __init__(self, logger_class, *args, **kwargs):
|
||||
ConfigureCustom.LOGGER_CLASS = logger_class
|
||||
|
||||
|
||||
class ConfigureStandard(Configure):
|
||||
|
||||
def __init__(self, level=None, fmt=None, output=None, logger=None, colors=False, force_colors=False):
|
||||
if fmt is None:
|
||||
fmt = C.LOG_OPT_FORMAT[1]
|
||||
if output is None:
|
||||
output = C.LOG_OPT_OUTPUT[1]
|
||||
super(ConfigureStandard, self).__init__(level, fmt, output, logger, colors, force_colors)
|
||||
|
||||
def preTreatment(self):
|
||||
"""We use logging methods directly, instead of using Logger"""
|
||||
global getLogger
|
||||
global debug
|
||||
global info
|
||||
global warning
|
||||
global error
|
||||
global critical
|
||||
import logging
|
||||
getLogger = logging.getLogger
|
||||
debug = logging.debug
|
||||
info = logging.info
|
||||
warning = logging.warning
|
||||
error = logging.error
|
||||
critical = logging.critical
|
||||
|
||||
def configureLevel(self, level):
|
||||
if level is None:
|
||||
level = C.LOG_LVL_DEBUG
|
||||
self.level = level
|
||||
|
||||
def configureFormat(self, fmt):
|
||||
import logging
|
||||
|
||||
class SatFormatter(logging.Formatter):
|
||||
u"""Formatter which manage SàT specificities"""
|
||||
_format = fmt
|
||||
_with_profile = '%(profile)s' in fmt
|
||||
|
||||
def __init__(self, can_colors=False):
|
||||
super(SatFormatter, self).__init__(self._format)
|
||||
self.can_colors = can_colors
|
||||
|
||||
def format(self, record):
|
||||
if self._with_profile:
|
||||
record.profile = _getProfile()
|
||||
s = super(SatFormatter, self).format(record)
|
||||
if self.with_colors and (self.can_colors or self.force_colors):
|
||||
s = _ansiColors(record.levelname, s)
|
||||
return s
|
||||
|
||||
self.formatterClass = SatFormatter
|
||||
|
||||
def configureOutput(self, output):
|
||||
self.manageOutputs(output)
|
||||
|
||||
def configureLogger(self, logger):
|
||||
self.name_filter = FilterName(logger) if logger else None
|
||||
|
||||
def configureColors(self, colors, force_colors):
|
||||
self.formatterClass.with_colors = colors
|
||||
self.formatterClass.force_colors = force_colors
|
||||
if not colors and force_colors:
|
||||
raise ValueError("force_colors can't be used if colors is False")
|
||||
|
||||
def _addHandler(self, root_logger, hdlr, can_colors=False):
|
||||
hdlr.setFormatter(self.formatterClass(can_colors))
|
||||
root_logger.addHandler(hdlr)
|
||||
root_logger.setLevel(self.level)
|
||||
if self.name_filter is not None:
|
||||
hdlr.addFilter(self.name_filter)
|
||||
|
||||
def postTreatment(self):
|
||||
import logging
|
||||
root_logger = logging.getLogger()
|
||||
if len(root_logger.handlers) == 0:
|
||||
for handler, options in _handlers.items():
|
||||
if handler == C.LOG_OPT_OUTPUT_DEFAULT:
|
||||
hdlr = logging.StreamHandler()
|
||||
try:
|
||||
can_colors = hdlr.stream.isatty()
|
||||
except AttributeError:
|
||||
can_colors = False
|
||||
self._addHandler(root_logger, hdlr, can_colors=can_colors)
|
||||
elif handler == C.LOG_OPT_OUTPUT_MEMORY:
|
||||
from logging.handlers import BufferingHandler
|
||||
class SatMemoryHandler(BufferingHandler):
|
||||
def emit(self, record):
|
||||
super(SatMemoryHandler, self).emit(self.format(record))
|
||||
hdlr = SatMemoryHandler(options)
|
||||
_handlers[handler] = hdlr # we keep a reference to the handler to read the buffer later
|
||||
self._addHandler(root_logger, hdlr, can_colors=False)
|
||||
elif handler == C.LOG_OPT_OUTPUT_FILE:
|
||||
import os.path
|
||||
for path in options:
|
||||
hdlr = logging.FileHandler(os.path.expanduser(path))
|
||||
self._addHandler(root_logger, hdlr, can_colors=False)
|
||||
else:
|
||||
raise ValueError("Unknown handler type")
|
||||
else:
|
||||
root_logger.warning(u"Handlers already set on root logger")
|
||||
configure_cls = { None: ConfigureBase,
|
||||
C.LOG_BACKEND_CUSTOM: ConfigureCustom
|
||||
} # XXX: (key: backend, value: Configure subclass) must be filled when new backend are added
|
||||
|
||||
|
||||
def configure(backend=C.LOG_BACKEND_STANDARD, **options):
|
||||
def configure(backend_, **options):
|
||||
"""Configure logging behaviour
|
||||
@param backend: can be:
|
||||
C.LOG_BACKEND_STANDARD: use standard logging module
|
||||
C.LOG_BACKEND_TWISTED: use twisted logging module (with standard logging observer)
|
||||
C.LOG_BACKEND_BASIC: use a basic print based logging
|
||||
C.LOG_BACKEND_CUSTOM: use a given Logger subclass
|
||||
"""
|
||||
global _backend
|
||||
if _backend is not None:
|
||||
global backend
|
||||
if backend is not None:
|
||||
raise exceptions.InternalError("Logging can only be configured once")
|
||||
_backend = backend
|
||||
backend = backend_
|
||||
|
||||
if backend == C.LOG_BACKEND_BASIC:
|
||||
ConfigureBasic(**options)
|
||||
|
||||
elif backend == C.LOG_BACKEND_TWISTED:
|
||||
ConfigureTwisted(**options)
|
||||
|
||||
elif backend == C.LOG_BACKEND_STANDARD:
|
||||
ConfigureStandard(**options)
|
||||
|
||||
elif backend == C.LOG_BACKEND_CUSTOM:
|
||||
try:
|
||||
configure_class = configure_cls[backend]
|
||||
except KeyError:
|
||||
raise ValueError("unknown backend [%s]" % backend)
|
||||
if backend == C.LOG_BACKEND_CUSTOM:
|
||||
logger_class = options.pop('logger_class')
|
||||
ConfigureCustom(logger_class, **options)
|
||||
|
||||
configure_class(logger_class, **options)
|
||||
else:
|
||||
raise ValueError("unknown backend")
|
||||
configure_class(**options)
|
||||
|
||||
def _parseOptions(options):
|
||||
"""Parse string options as given in conf or environment variable, and return expected python value
|
||||
|
||||
@param options (dict): options with (key: name, value: string value)
|
||||
"""
|
||||
COLORS = C.LOG_OPT_COLORS[0]
|
||||
LEVEL = C.LOG_OPT_LEVEL[0]
|
||||
|
||||
if COLORS in options:
|
||||
if options[COLORS].lower() in ('1', 'true'):
|
||||
options[COLORS] = True
|
||||
elif options[COLORS] == 'force':
|
||||
options[COLORS] = True
|
||||
options['force_colors'] = True
|
||||
else:
|
||||
options[COLORS] = False
|
||||
if LEVEL in options:
|
||||
level = options[LEVEL].upper()
|
||||
if level not in C.LOG_LEVELS:
|
||||
level = C.LOG_LVL_INFO
|
||||
options[LEVEL] = level
|
||||
|
||||
def satConfigure(backend=C.LOG_BACKEND_STANDARD, const=None):
|
||||
"""Configure logging system for SàT, can be used by frontends
|
||||
|
||||
logs conf is read in SàT conf, then in environment variables. It must be done before Memory init
|
||||
@param backend: backend to use, it can be:
|
||||
- C.LOG_BACKEND_BASIC: print based backend
|
||||
- C.LOG_BACKEND_TWISTED: Twisted logging backend
|
||||
- C.LOG_BACKEND_STANDARD: standard logging backend
|
||||
@param const: Const class to use instead of sat.core.constants.Const (mainly used to change default values)
|
||||
"""
|
||||
if const is not None:
|
||||
global C
|
||||
C = const
|
||||
import ConfigParser
|
||||
import os
|
||||
log_conf = {}
|
||||
config = ConfigParser.SafeConfigParser()
|
||||
config.read(C.CONFIG_FILES)
|
||||
for opt_name, opt_default in C.LOG_OPTIONS():
|
||||
try:
|
||||
log_conf[opt_name] = os.environ[''.join((C.ENV_PREFIX, C.LOG_OPT_PREFIX.upper(), opt_name.upper()))]
|
||||
except KeyError:
|
||||
try:
|
||||
log_conf[opt_name] = config.get(C.LOG_OPT_SECTION, C.LOG_OPT_PREFIX + opt_name)
|
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
|
||||
log_conf[opt_name] = opt_default
|
||||
|
||||
_parseOptions(log_conf)
|
||||
configure(backend, **log_conf)
|
||||
def memoryGet(size=None):
|
||||
if not C.LOG_OPT_OUTPUT_MEMORY in handlers:
|
||||
raise ValueError('memory output is not used')
|
||||
return configure_cls[backend].memoryGet(size)
|
||||
|
||||
def getLogger(name=C.LOG_BASE_LOGGER):
|
||||
if _backend in (None, C.LOG_BACKEND_BASIC):
|
||||
logger_class = ConfigureBasic.LOGGER_CLASS
|
||||
elif _backend == C.LOG_BACKEND_TWISTED:
|
||||
logger_class = ConfigureTwisted.LOGGER_CLASS
|
||||
elif _backend == C.LOG_BACKEND_CUSTOM:
|
||||
logger_class = ConfigureCustom.LOGGER_CLASS
|
||||
else:
|
||||
raise ValueError("This method should not be called with backend [%s]" % _backend)
|
||||
try:
|
||||
logger_class = configure_cls[backend].LOGGER_CLASS
|
||||
except KeyError:
|
||||
raise ValueError("This method should not be called with backend [%s]" % backend)
|
||||
return _loggers.setdefault(name, logger_class(name))
|
||||
|
||||
_root_logger = getLogger()
|
||||
|
|
|
|||
Loading…
Reference in a new issue