Source code for logger

# -*- coding: utf-8 -*-

# This code is part of Amoco
# Copyright (C) 2006-2011 Axel Tillequin (bdcht3@gmail.com)
# published under GPLv2 license

"""
logger.py
=========

This module defines amoco logging facilities.
The ``Log`` class inherits from a standard :py:class:`logging.Logger`,
with minor additional features like a ``'VERBOSE'`` level introduced between
``'INFO'`` and ``'DEBUG'``
levels, and a progress method that can be useful for time consuming activities.
See below for details.

Most amoco modules start by creating their local ``logger`` object used to
provide various feedback.
Users can thus focus on messages from selected amoco modules by adjusting their
level independently, or use the ``set_quiet()``, ``set_debug()`` or
``set_log_all(level)`` functions to adjust all loggers at once.

Examples:

    Setting the mapper module to ``'VERBOSE'`` level::

        In [1]: import amoco
        In [2]: amoco.cas.mapper.logger.setlevel('VERBOSE')

    Setting all modules loggers to ``'ERROR'`` level::

        In [2]: amoco.logger.set_quiet()

Note:
All loggers can be configured to log both to *stderr* with selected level
and to a unique temporary file with ``'DEBUG'`` level. See configuration.
"""

import logging

VERBOSE = 15
logging.addLevelName(VERBOSE, "VERBOSE")
# logging.captureWarnings(True)

default_format = logging.Formatter("[%(levelname)-7s] %(name)-24s: %(message)s")

from amoco.config import conf

# setting logfile global. Static definition here, see below for traits observer.

logfile = None

def set_file_logging(filename,level):
    global logfile
    if not filename and conf.Log.tempfile:
        import tempfile
        filename = tempfile.mkstemp(".log", prefix="amoco-")[1]
    if filename:
        logfile = logging.FileHandler(filename, mode="w")
        logfile.setFormatter(default_format)
        logfile.setLevel(level)
    else:
        logfile = None

# By default, we log at INFO level in console, and VERBOSE in file:
set_file_logging(conf.Log.filename,VERBOSE)

[docs]class Log(logging.Logger): """ This class is intended to allow amoco activities to be logged simultaneously to the *stderr* output with an adjusted level and to a temporary file with full verbosity. All instanciated Log objects are tracked by the Log class attribute ``Log.loggers`` which maps their names with associated instances. The recommended way to create a Log object is to add, near the begining of amoco modules:: from amoco.logger import Log logger = Log(__name__) """ loggers = {} def __init__(self, name, handler=logging.StreamHandler()): super().__init__(name) handler.setFormatter(default_format) self.addHandler(handler) self.setLevel(conf.Log.level) if logfile: self.addHandler(logfile) self.register(name, self) def verbose(self, msg, *args, **kargs): return self.log(VERBOSE, msg, *args, **kargs) def progress(self, count, total=0, pfx=""): h = self.handlers[0] if h.level > VERBOSE: return term = h.stream if not term.isatty(): return if total > 0: barlen = 40 fillr = min((count + 1.0) / total, 1.0) done = int(round(barlen * fillr)) ratio = round(100.0 * fillr, 1) s = ("=" * done).ljust(barlen, "-") term.write("%s[%s] %s%%\r" % (pfx, s, ratio)) else: s = ("%s[%d]" % (pfx, count)).ljust(80, " ") term.write("%s\r" % s)
[docs] def setLevel(self, lvl): return super().setLevel(lvl)
@classmethod def register(cls, name, self): if name in self.loggers: raise KeyError else: cls.loggers[name] = self
[docs]def set_quiet(): """set all loggers to ``'ERROR'`` level """ set_log_all(logging.ERROR)
[docs]def set_debug(): """set all loggers to ``'DEBUG'`` level """ set_log_all(logging.DEBUG)
[docs]def set_log_all(level): """set all loggers to specified level Args: level (int): level value as an integer. """ for l in Log.loggers.values(): l.setLevel(level)
def set_log_module(name, level): if name in Log.loggers: Log.loggers[name].setLevel(level) def log_level_observed(change): level = change["new"] set_log_all(level) conf.Log.observe(log_level_observed, names=["level"])
[docs]def reset_log_file(filename, level=logging.DEBUG): """set DEBUG log file for all loggers. Args: filename (str): filename for the FileHandler added to all amoco loggers """ global logfile if logfile is not None: logfile.close() unset_log_file() set_file_logging(filename,level) for l in Log.loggers.values(): l.addHandler(logfile)
def unset_log_file(): global logfile if logfile: for l in Log.loggers.values(): l.removeHandler(logfile) logfile = None def log_tempfile_observed(change): if not conf.Log.filename: if change['new'] is True: if not logfile: reset_log_file("",VERBOSE) else: unset_log_file() conf.Log.observe(log_tempfile_observed, names=["tempfile"]) def log_filename_observed(change): reset_log_file(change["new"]) conf.Log.observe(log_filename_observed, names=["filename"])