Source code for logquacious.log_context

import functools
import logging

from . import utils
from .context_templates import ContextTemplates
from .backport_configurable_stacklevel import PatchedLoggerMixin


__all__ = ['LogContext']


[docs]class LogContext: """Manager for context managers/decorators used for logging. Attributes: debug: Decorator/context-manager with level `logging.DEBUG`. info: Decorator/context-manager with level `logging.INFO`. warning: Decorator/context-manager with level `logging.WARNING`. error: Decorator/context-manager with level `logging.ERROR`. fatal: Decorator/context-manager with level `logging.CRITICAL`. """ def __init__(self, logger, templates=None): templates = ContextTemplates.resolve(templates) self.logger = utils.get_logger(logger) self.debug = _ContextLoggerFactory(logger, logging.DEBUG, templates) self.info = _ContextLoggerFactory(logger, logging.INFO, templates) self.warning = _ContextLoggerFactory(logger, logging.WARNING, templates) # noqa: E501 self.error = _ContextLoggerFactory(logger, logging.ERROR, templates) self.fatal = _ContextLoggerFactory(logger, logging.CRITICAL, templates)
class _BaseContextLogger(PatchedLoggerMixin): context_type = None def __init__(self, templates, logger, log_level=logging.INFO, label=None): super(_BaseContextLogger, self).__init__() self.logger = utils.get_logger(logger) self.log_level = log_level self.label = label level_name = logging.getLevelName(log_level) start_key = '{}.start.{}'.format(self.context_type, level_name) self.start_template = templates.get(start_key) finish_key = '{}.finish.{}'.format(self.context_type, level_name) self.finish_template = templates.get(finish_key) def log(self, msg, *args, **kwargs): # Stacklevel 3: # 1: This function # 2: Decorated function for `FunctionContextLogger` # or __enter__/__exit__ of `ContextLogger`. # 3: Function that was decorated or the original call of the context. kwargs.setdefault('stacklevel', 3) with self.temp_monkey_patched_logger(): self.logger.log(self.log_level, msg, *args, **kwargs) class ContextLogger(_BaseContextLogger): context_type = 'context' def __init__(self, templates, logger, log_level=logging.INFO, label=None): super(ContextLogger, self).__init__(templates, logger, log_level=log_level, label=label) def __enter__(self): if self.start_template: self.log(self.start_template.format(label=self.label)) def __exit__(self, *args, **kwds): if self.finish_template: self.log(self.finish_template.format(label=self.label)) class FunctionContextLogger(_BaseContextLogger): context_type = 'function' def __init__(self, templates, logger, log_level=logging.INFO, label=None, show_args=False, show_kwargs=False): super(FunctionContextLogger, self).__init__( templates, logger, log_level=log_level, label=label ) self._format_function_args = functools.partial( utils.format_function_args, show_args=show_args, show_kwargs=show_kwargs, ) def __call__(self, func): self.label = func.__name__ @functools.wraps(func) def decorated_func(*args, **kwargs): arg_string = self._format_function_args(args, kwargs) log_kwargs = {'label': self.label, 'arguments': arg_string} if self.start_template: self.log(self.start_template.format(**log_kwargs)) output = func(*args, **kwargs) if self.finish_template: self.log(self.finish_template.format(**log_kwargs)) return output return decorated_func class _ContextLoggerFactory: """Factory returning a `ContextLogger` for a specific logging level. Note that each use as a context manager or decorator creates a new instance of `ContextLogger` or `FunctionContextLogger`. """ def __init__(self, logger, log_level, templates): self.logger = utils.get_logger(logger) self.log_level = log_level self.templates = templates def __call__(self, func_or_label=None, show_args=False, show_kwargs=False): if func_or_label is None or callable(func_or_label): decorator = FunctionContextLogger( templates=self.templates, logger=self.logger, log_level=self.log_level, show_args=show_args, show_kwargs=show_kwargs, ) if func_or_label is None: return decorator # Decorator called without arguments so argument is function. return decorator(func_or_label) return ContextLogger( templates=self.templates, logger=self.logger, log_level=self.log_level, label=func_or_label, )