1
0
Fork 0
mirror of https://github.com/yweber/lodel2.git synced 2025-11-01 03:59:02 +01:00
lodel2_mirror/lodel/logger.py
Yann fc7c74660b Debuging logger for multicontext environment
Before we were calling the logging.getLogger function without argument. The result was
that all contexts were sharing the same logger (and every message were logged X time with
X the number of loaded context).
Now getLogger is called with the context id as argument, each context has its own logger.
2016-11-16 11:50:57 +01:00

139 lines
5.6 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#-*- coding: utf-8 -*-
import copy
import logging, logging.handlers
import os.path
# Variables & constants definitions
default_format = '%(asctime)-15s %(levelname)s %(_pathname)s:%(_lineno)s:%(_funcName)s() : %(message)s'
simple_format = '%(asctime)-15s %(levelname)s : %(message)s'
SECURITY_LOGLEVEL = 35
logging.addLevelName(SECURITY_LOGLEVEL, 'SECURITY')
handlers = dict() # Handlers list (generated from settings)
##@brief Stores sent messages until module is able to be initialized
msg_buffer = []
# Fetching logger for current context
from lodel.context import LodelContext
logger = logging.getLogger(LodelContext.get_name())
##@brief Module initialisation from settings
#@return True if inited else False
def __init_from_settings():
from lodel.context import LodelContext
try:
LodelContext.expose_modules(globals(), {
'lodel.settings': ['Settings']})
except Exception:
return False
LodelContext.expose_modules(globals(), {
'lodel.settings.settings': [('Settings', 'Lodel2Settings')]})
if not Lodel2Settings.started():
return False
# capture warning disabled, because the custom format raises error (unable
# to give the _ preffixed arguments to logger resulting into a KeyError
# exception )
#logging.captureWarnings(True) # Log warnings
logger.setLevel(logging.DEBUG)
for name in Settings.logging._fields:
add_handler(name, getattr(Settings.logging, name))
return True
##@brief Add an handler, identified by a name, to a given logger
#
# logging_opt is a dict with logger option. Allowed keys are :
# - filename : take a filepath as value and cause the use of a logging.handlers.RotatingFileHandler
# - level : the minimum logging level for a logger, takes values [ 'DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL' ]
# - format : DONT USE THIS OPTION (or if you use it be sure to includes %(_pathname)s %(_lineno)s %(_funcName)s format variables in format string
# - context : boolean, if True include the context (module:lineno:function_name) in the log format
# @todo Move the logging_opt documentation somewhere related with settings
#
# @param name str : The handler name
# @param logging_opt dict : dict containing options ( see above )
def add_handler(name, logging_opt):
logger = logging.getLogger(LodelContext.get_name())
if name in handlers:
raise KeyError("A handler named '%s' allready exists")
logging_opt = logging_opt._asdict()
if 'filename' in logging_opt and logging_opt['filename'] is not None:
maxBytes = (1024 * 10) if 'maxbytes' not in logging_opt else logging_opt['maxbytes']
backupCount = 10 if 'backupcount' not in logging_opt else logging_opt['backupcount']
handler = logging.handlers.RotatingFileHandler(
logging_opt['filename'],
maxBytes = maxBytes,
backupCount = backupCount,
encoding = 'utf-8')
else:
handler = logging.StreamHandler()
if 'level' in logging_opt:
handler.setLevel(getattr(logging, logging_opt['level'].upper()))
if 'format' in logging_opt:
formatter = logging.Formatter(logging_opt['format'])
else:
if 'context' in logging_opt and not logging_opt['context']:
formatter = logging.Formatter(simple_format)
else:
formatter = logging.Formatter(default_format)
handler.setFormatter(formatter)
handlers[name] = handler
logger.addHandler(handler)
##@brief Remove an handler generated from configuration (runtime logger configuration)
# @param name str : handler name
def remove_handler(name):
if name in handlers:
logger.removeHandler(handlers[name])
# else: can we do anything ?
##@brief Utility function that disable unconditionnaly handlers that implies console output
# @note In fact, this function disables handlers generated from settings wich are instances of logging.StreamHandler
def remove_console_handlers():
for name, handler in handlers.items():
if isinstance(handler, logging.StreamHandler):
remove_handler(name)
#####################
# Utility functions #
#####################
##@brief Generic logging function
# @param lvl int : Log severity
# @param msg str : log message
# @param *args : additional positionnal arguments
# @param **kwargs : additional named arguments
def log(lvl, msg, *args, **kwargs):
if len(handlers) == 0: #late initialisation
if not __init_from_settings():
s_kwargs = copy.copy(kwargs)
s_kwargs.update({'lvl': lvl, 'msg':msg})
msg_buffer.append((s_kwargs, args))
return
else:
for s_kwargs, args in msg_buffer:
log(*args, **s_kwargs)
from lodel.context import LodelContext
if LodelContext.multisite():
msg = "CTX(%s) %s" % (LodelContext.get_name(), msg)
caller = logger.findCaller() # Opti warning : small overhead
extra = {
'_pathname': os.path.abspath(caller[0]),
'_lineno': caller[1],
'_funcName': caller[2],
}
logger.log(lvl, msg, extra = extra, *args, **kwargs)
def debug(msg, *args, **kwargs): log(logging.DEBUG, msg, *args, **kwargs)
def info(msg, *args, **kwargs): log(logging.INFO, msg, *args, **kwargs)
def warning(msg, *args, **kwargs): log(logging.WARNING, msg, *args, **kwargs)
def security(msg, *args, **kwargs): log(SECURITY_LOGLEVEL, msg, *args, **kwargs)
def error(msg, *args, **kwargs): log(logging.ERROR, msg, *args, **kwargs)
def critical(msg, *args, **kwargs): log(logging.CRITICAL, msg, *args, **kwargs)