Source code for modelparameters.logger

# Copyright (C) 2005-2012 Martin Sandve Alnaes and Anders Logg
#
# This file is part of ModelParameters.
#
# ModelParameters is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ModelParameters is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ModelParameters. If not, see <http://www.gnu.org/licenses/>.
# Modified by Johan Hake, 2009-2012.
import logging
import sys
import types

__all__ = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "Logger"]

# Import default log levels
from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL


# Base class for ModelParameters exceptions
class ModelParametersException(Exception):
    "Base class for ModelParameters exceptions"
    pass


# This is used to override emit() in StreamHandler for printing without newline
def emit(self, record):
    message = self.format(record)
    format_string = "%s" if getattr(record, "continued", False) else "%s\n"
    self.stream.write(format_string % message)
    self.flush()


# Colors
RED = "\033[1;37;31m%s\033[0m"
BLUE = "\033[1;37;34m%s\033[0m"
GREEN = "\033[1;37;32m%s\033[0m"


# Logger class
[docs]class Logger: def __init__(self, name): "Create logger instance." self._name = name # Set up handler h = logging.StreamHandler(sys.stdout) sys.stdout.flush() h.setLevel(WARNING) # h.setFormatter(logging.Formatter("%(levelname)s, %(name)s, # %(pathname)s, line %(lineno)s, in %(module)s\n %(message)s\n")) # Override emit() in handler for indentation h.emit = types.MethodType(emit, h) # , h.__class__) self._handler = h # Set up logger self._log = logging.getLogger(name) assert len(self._log.handlers) == 0 self._log.addHandler(h) self._log.setLevel(DEBUG) self._logfiles = {} # Set initial indentation level self._indent_level = 0 # Set flag for raising errors self._raise_error = True # Setup stack with default logging level self._level_stack = [DEBUG] # Set prefix self._prefix = "" # Set default Exception self._DefaultException = ModelParametersException
[docs] def set_default_exception(self, exception): if not issubclass(exception, Exception): raise TypeError("Expected a subclass of Exception") self._DefaultException = exception
[docs] def add_logfile(self, filename=None, mode="a"): if filename is None: filename = f"{self._name}.log" if filename in self._logfiles: self.warning(f"Trying to add logfile {filename} multiple times.") return h = logging.FileHandler(filename, mode) h.emit = types.MethodType(emit, h, h.__class__) h.setLevel(self._log.level) self._log.addHandler(h) self._logfiles[filename] = h return h
[docs] def remove_logfile(self, filename): if filename in self._logfiles: h = self._logfiles.pop(filename) self._log.removeHandler(h)
[docs] def set_raise_error(self, value): self._raise_error = bool(value)
[docs] def get_logfile_handler(self, filename): return self._logfiles[filename]
[docs] def log(self, level, *message): "Write a log message on given log level" text = self._format_raw(*message) if len(text) >= 3 and text[-3:] == "...": self._log.log(level, self._format(*message), extra={"continued": True}) else: self._log.log(level, self._format(*message))
[docs] def debug(self, *message): "Write debug message." self.log(DEBUG, *message)
[docs] def info(self, *message): "Write info message." self.log(INFO, *message)
[docs] def info_red(self, *message): "Write info message in red." self.log(INFO, RED % self._format_raw(*message))
[docs] def info_green(self, *message): "Write info message in green." self.log(INFO, GREEN % self._format_raw(*message))
[docs] def info_blue(self, *message): "Write info message in blue." self.log(INFO, BLUE % self._format_raw(*message))
[docs] def warning(self, *message): "Write warning message." self.log(WARNING, self._format_raw(*message))
[docs] def error(self, *message, **kwargs): "Write error message and raise an exception." self.log(ERROR, self._format_raw(*message)) raise_error = kwargs.get("raise_error", self._raise_error) Exception = kwargs.get("exception", self._DefaultException) if raise_error: raise Exception(self._format_raw(*message))
[docs] def type_error(self, *message, **kwargs): "Write error message and raise a type error exception." kwargs["exception"] = TypeError self.error(*message, **kwargs)
[docs] def value_error(self, *message, **kwargs): "Write error message and raise a value error exception." kwargs["exception"] = ValueError self.error(*message, **kwargs)
[docs] def begin_log(self, *message): "Begin task: write message and increase indentation level." self.info(*message) # self.info("-"*len(self._format_raw(*message))) self.add_log_indent()
[docs] def end_log(self): "End task: write a newline and decrease indentation level." self.info("") self.add_log_indent(-1)
[docs] def push_log_level(self, level): "Push a log level on the level stack." self._level_stack.append(level) self.set_log_level(level)
[docs] def pop_log_level(self): """ Pop log level from the level stack, reverting to before the last push_level. """ self._level_stack.pop() level = self._level_stack[-1] self.set_log_level(level)
[docs] def suppress_logging(self): "Suppress all logging" self.set_log_level(CRITICAL + 1)
[docs] def set_log_level(self, level): "Set log level." self._level_stack[-1] = level self._log.setLevel(level) self._handler.setLevel(level) for h in list(self._logfiles.values()): h.setLevel(level)
[docs] def get_log_level(self): "Get log level." return self._level_stack[-1]
[docs] def set_log_indent(self, level): "Set indentation level." self._indent_level = level
[docs] def add_log_indent(self, increment=1): "Add to indentation level." self._indent_level += increment
[docs] def get_log_handler(self): "Get handler for logging." return self._handler
[docs] def flush_logger(self): "Flush the log handler" for h in self._log.handlers: h.flush()
[docs] def set_log_handler(self, handler): """ Replace handler for logging. To add additional handlers instead of replacing the existing, use log.get_logger().addHandler(myhandler). See the logging module for more details. """ self._log.removeHandler(self._handler) self._log.addHandler(handler) self._handler = handler handler.emit = types.MethodType(emit, self._handler, self._handler.__class__)
[docs] def get_logger(self): "Return message logger." return self._log
[docs] def set_log_prefix(self, prefix): "Set prefix for log messages." self._prefix = prefix
[docs] def wrap_log_message(self, message, symbol="*"): message = f"{symbol} {message} {symbol}" wrapping = symbol * len(message) return "\n".join(["", wrapping, message, wrapping, ""])
def _format(self, *message): "Format message including indentation." indent = self._prefix + 2 * self._indent_level * " " message = message[0] if len(message) == 1 else message[0] % message[1:] return "\n".join([indent + line for line in message.split("\n")]) def _format_raw(self, *message): "Format message without indentation." message = message[0] if len(message) == 1 else message[0] % message[1:] return message
# --- Set up global log functions --- _logger = Logger("ModelParameters") def set_default_exception(exception): _logger.set_default_exception(exception) def add_logfile(filename=None, mode="a"): _logger.add_logfile(filename, mode) def remove_logfile(filename): _logger.remove_logfile(filename) def set_raise_error(value): _logger.set_raise_error(value) def get_logfile_handler(filename): _logger.get_log_handler(filename) def log(level, *message): _logger.log(level, *message)
[docs]def debug(*message): "Write debug message." _logger.debug(*message)
[docs]def info(*message): "Write info message." _logger.info(*message)
def info_red(*message): "Write info message in red." _logger.info_red(*message) def info_green(*message): "Write info message in green." _logger.info_green(*message) def info_blue(*message): "Write info message in blue." _logger.info_blue(*message)
[docs]def warning(*message): "Write warning message." _logger.warning(*message)
[docs]def error(*message, **kwargs): "Write error message and raise an exception." _logger.error(*message, **kwargs)
[docs]def type_error(*message, **kwargs): "Write error message and raise a type error exception." _logger.type_error(*message, **kwargs)
[docs]def value_error(*message, **kwargs): "Write error message and raise a value error exception." _logger.value_error(*message, **kwargs)
def begin_log(*message): "Begin task: write message and increase indentation level." _logger.begin_log(*message) def end_log(): "End task: write a newline and decrease indentation level." _logger.end_log() def push_log_level(level): "Push a log level on the level stack." _logger.push_log_level(level) def pop_log_level(): """ Pop log level from the level stack, reverting to before the last push_level. """ _logger.pop_log_level() def suppress_logging(): "Suppress all logging" _logger.suppress_logging() def set_log_level(level): "Set log level." _logger.set_log_level(level) def get_log_level(): "Get log level." _logger.get_log_level() def set_log_indent(level): "Set indentation level." _logger.set_log_indent(level) def add_log_indent(increment=1): "Add to indentation level." _logger.add_log_indent(increment) def get_log_handler(): "Get handler for logging." _logger.get_log_handler() def flush_logger(): "Flush the log handler" _logger.flush_logger() def set_log_handler(handler): """ Replace handler for logging. To add additional handlers instead of replacing the existing, use log.get_logger().addHandler(myhandler). See the logging module for more details. """ _logger.set_log_handler(handler) def get_logger(): "Return message logger." _logger.get_logger() def set_log_prefix(prefix): "Set prefix for log messages." _logger.set_log_prefix(prefix) def wrap_log_message(message, symbol="*"): _logger.wrap_log_message(message, symbol)