# -*- coding: utf-8 -*-
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# Copyright 2013, Hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright 2018, Pol Canelles <canellestudi@gmail.com>
import io
import logging
import os
import sys
import traceback
# If you want to debug cohen,
# set the below variable to: logging.DEBUG
DEFAULT_COHEN_LOG_LEVEL = logging.WARN
LOG_FORMAT = ('[%(levelname)-18s][$BOLD%(name)-15s$RESET] '
'%(message)s ($BOLD%(filename)s$RESET:%(lineno)d)')
ENV_VAR_NAME = 'COHEN_DEBUG'
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
# The background is set with 40 plus the number of the color,
# and the foreground with 30
# These are the sequences need to get colored output
RESET_SEQ = '\033[0m'
COLOR_SEQ = '\033[1;%dm'
BOLD_SEQ = '\033[1m'
COLORS = {
'WARNING': YELLOW,
'INFO': WHITE,
'DEBUG': BLUE,
'CRITICAL': YELLOW,
'ERROR': RED
}
# This is taken from std.-module logging, see Logger.findCaller below.
# _srcfile is used when walking the stack to check when we've got the first
# caller stack frame.
#
if hasattr(sys, 'frozen'): # support for py2exe
_srcfile = f'coherence{os.sep}log{__file__[-4:]}'
elif __file__[-4:].lower() in ['.pyc', '.pyo']:
_srcfile = __file__[:-4] + '.py'
else:
_srcfile = __file__
_srcfile = os.path.normcase(_srcfile)
_srcfiles = (_srcfile, logging._srcfile)
loggers = {}
[docs]def get_main_log_level():
global loggers
if ENV_VAR_NAME in os.environ:
return os.environ[ENV_VAR_NAME]
log_root = loggers.get('coherence', None)
if log_root is None:
log_level = DEFAULT_COHEN_LOG_LEVEL
else:
log_level = log_root.level
return log_level
[docs]class ColoredLogger(logging.Logger):
FORMAT = LOG_FORMAT
COLOR_FORMAT = formatter_message(FORMAT, True)
def __init__(self, name):
logging.Logger.__init__(self, name, get_main_log_level())
color_formatter = ColoredFormatter(self.COLOR_FORMAT)
console = logging.StreamHandler()
console.setFormatter(color_formatter)
# print(self.handlers)
if console not in self.handlers:
self.addHandler(console)
# print(self.handlers)
return
[docs] def findCaller(self, stack_info=False, use_color=True):
'''
Find the stack frame of the caller so that we can note the source
file name, line number and function name.
'''
f = logging.currentframe()
# On some versions of IronPython, currentframe() returns None if
# IronPython isn't run with -X:Frames.
if f is not None:
f = f.f_back
rv = '(unknown file)', 0, '(unknown function)', None
while hasattr(f, 'f_code'):
co = f.f_code
filename = os.path.normcase(co.co_filename)
if filename in _srcfiles:
f = f.f_back
continue
sinfo = None
if stack_info:
sio = io.StringIO()
sio.write('Stack (most recent call last):\n')
traceback.print_stack(f, file=sio)
sinfo = sio.getvalue()
if sinfo[-1] == '\n':
sinfo = sinfo[:-1]
sio.close()
rv = (co.co_filename, f.f_lineno, co.co_name, sinfo)
break
return rv
logging.setLoggerClass(ColoredLogger)
[docs]class LogAble(object):
'''
Base class for objects that want to be able to log messages with
different level of severity. The levels are, in order from least
to most: log, debug, info, warning, error.
'''
logCategory = 'default'
'''Implementors can provide a category to log their messages under.'''
_Loggable__logger = None
FORMAT = LOG_FORMAT
COLOR_FORMAT = formatter_message(FORMAT, True)
def __init__(self):
global loggers
if loggers.get(self.logCategory):
self._logger = loggers.get(self.logCategory)
self._logger.propagate = False
else:
self._logger = logging.getLogger(self.logCategory)
self._logger.propagate = False
loggers[self.logCategory] = self._logger
self.debug(f'Added logger with logCategory: {self.logCategory}')
return
[docs] def log(self, message, *args, **kwargs):
self._logger.log(message, *args, **kwargs)
[docs] def warning(self, message, *args, **kwargs):
self._logger.warning(message, *args, **kwargs)
[docs] def info(self, message, *args, **kwargs):
self._logger.info(message, *args, **kwargs)
[docs] def critical(self, message, *args, **kwargs):
self._logger.critical(message, *args, **kwargs)
[docs] def debug(self, message, *args, **kwargs):
self._logger.debug(message, *args, **kwargs)
[docs] def error(self, message, *args, **kwargs):
self._logger.error(message, *args, **kwargs)
[docs] def exception(self, message, *args, **kwargs):
self._logger.exception(message, *args, **kwargs)
fatal = critical
warn = warning
msg = info
[docs]def get_logger(log_category):
global loggers
log = loggers.get(log_category, None)
if log is None:
log = logging.getLogger(log_category)
log.setLevel(get_main_log_level())
log.propagate = False
return log
[docs]def init(logfilename=None, loglevel=logging.WARN):
global loggers
if loggers.get('coherence'):
log = loggers.get('coherence')
log.setLevel(loglevel)
return log
else:
logging.addLevelName(100, 'NONE')
logging.basicConfig(
filename=logfilename,
level=loglevel,
format=LOG_FORMAT)
logger = logging.getLogger()
logger.setLevel(loglevel)
logger.propagate = False
loggers['coherence'] = logger
logger.debug(f'Added logger with logCategory: {"coherence"}')