Source code for finam.tools.log_helper

"""Logging helpers."""
# pylint: disable=E1101
import logging
import sys
from contextlib import AbstractContextManager

from finam.interfaces import Loggable

from . import wurlitzer


[docs] def is_loggable(obj): """ Check if given object is loggable. Parameters ---------- obj : object Object to check for loggability Returns ------- bool Loggability of the object. """ return isinstance(obj, Loggable)
[docs] class LogWriter: """ Log writer. Parameters ---------- logger : string, None or logging.Logger instance, optional Logger name for this writer. Will be the root logger by default. level : integer, optional Logging level, by default logging.INFO """ def __init__(self, logger=None, level=logging.INFO): self.logger = logger.name if isinstance(logger, logging.Logger) else logger self.level = level
[docs] def write(self, msg): """ Write a given message to the logger. Parameters ---------- msg : string Message to log. """ logger = logging.getLogger(self.logger) if msg != "\n": logger.log(self.level, msg)
[docs] class LogStdOutStdErr(AbstractContextManager): """ Context manager to redirect stdout and stderr to a logger. Parameters ---------- logger : string, None or logging.Logger instance, optional Logger name for this writer. Will be the root logger by default. level_stdout : integer, optional Logging level for stdout, by default logging.INFO level_stderr : integer, optional Logging level for stderr, by default logging.WARN """ def __init__( self, logger=None, level_stdout=logging.INFO, level_stderr=logging.WARN ): self._stdout_target = LogWriter(logger=logger, level=level_stdout) self._stderr_target = LogWriter(logger=logger, level=level_stderr) self._old_stdout = getattr(sys, "stdout") self._old_stderr = getattr(sys, "stderr") def __enter__(self): setattr(sys, "stdout", self._stdout_target) setattr(sys, "stderr", self._stderr_target) def __exit__(self, *args, **kwargs): setattr(sys, "stdout", self._old_stdout) setattr(sys, "stderr", self._old_stderr)
[docs] class LogCStdOutStdErr(AbstractContextManager): """ Context manager to redirect low-level C stdout and stderr to a logger. Parameters ---------- logger : string, None or logging.Logger instance, optional Logger name for this writer. Will be the root logger by default. level_stdout : integer, optional Logging level for stdout, by default logging.INFO level_stderr : integer, optional Logging level for stderr, by default logging.WARN """ def __init__( self, logger=None, level_stdout=logging.INFO, level_stderr=logging.WARN ): self.logger = logger.name if isinstance(logger, logging.Logger) else logger self.level_stdout = level_stdout self.level_stderr = level_stderr self.stdout = None self.stderr = None self.pipes = wurlitzer.pipes() def __enter__(self): self.stdout, self.stderr = self.pipes.__enter__() def __exit__(self, *args, **kwargs): self.pipes.__exit__(*args, **kwargs) logger = logging.getLogger(self.logger) for line in self.stdout.read().splitlines(): logger.log(self.level_stdout, line) for line in self.stderr.read().splitlines(): logger.log(self.level_stderr, line)
[docs] class ErrorLogger(AbstractContextManager): """ Context manager to log Exceptions. Parameters ---------- logger : string, None or logging.Logger instance, optional Logger name to use. Will be the root logger by default. do_log : Bool, optional Whether to really log errors. Will be true by default. """ def __init__(self, logger=None, do_log=True): self.logger = logger.name if isinstance(logger, logging.Logger) else logger self.do_log = do_log def __exit__(self, exc_type, exc_value, traceback): if exc_value is not None and self.do_log: logging.getLogger(self.logger).exception(exc_value)
[docs] def add_logging_level(name, num, method=None): """ Adds a logging level to the :mod:`logging` module. Examples -------- .. code-block:: Python add_logging_level("TRACE", logging.DEBUG - 5) Parameters ---------- name : str The name of the new logging level. num : int The numeric severity of the new logging level. method : str, optional The method name for the new logging level. Defaults to lowercase of ``name``. """ if not method: method = name.lower() if hasattr(logging, name): raise AttributeError(f"{name} already defined in logging module") if hasattr(logging.getLoggerClass(), name): raise AttributeError(f"{name} already defined in logger class") def log_for_level(self, message, *args, **kwargs): if self.isEnabledFor(num): # pylint: disable=protected-access self._log(num, message, args, **kwargs) def log_to_root(message, *args, **kwargs): logging.log(num, message, *args, **kwargs) logging.addLevelName(num, name) setattr(logging, name, num) setattr(logging.getLoggerClass(), method, log_for_level) setattr(logging, method, log_to_root)