Source code for vdat.libvdat.loggers

"""Deal with loggers"""
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import logging
import os

import six

import vdat.config as vdatconfig

# store all the loggers used by cure tasks so that they and the handlers are
# created only once
_cure_loggers = {}


[docs]def setup_main_logger(conf, name='logger'): """Setup the main vdat logger. Add to the logger called ``name`` a standard output and a file logger Parameters ---------- conf : :class:`~pyhetdex.tools.configuration.ConfigParser` instance configuration options, """ log = logging.getLogger(name) # force debug to make handlers decide log.setLevel(_to_log_level(conf.get('logging', 'logger_level'))) # create and add the handlers for section in ['logging.main.stdout', 'logging.main.file']: sconf = conf_to_dict(conf, section) # get the content of the section h = make_handler(sconf) log.addHandler(h)
[docs]def make_logger(logger, conf=None, section=None): """Create a handler using the instructions in the configuration ``section`` and add it to the ``logger``. It looks for the following options: * ``remove_old``: if True remove all the previous handlers before adding the new one, default ``False`` * ``remove_std``: if True remove all the previous :class:`~logging.StreamHandler``, default ``False`` * the ones used by :func:`make_handler` Parameters ---------- logger : :class:`~logging.Logger` instance logger conf : :class:`~pyhetdex.tools.configuration.ConfigParser` instance,\ optional configuration options, if ``None`` use the default ``VDAT`` configuration object section : string, default name of the section containing the information to build the logger; defaults to ``logging.name``, where ``name`` is the logger name """ if not conf: conf = vdatconfig.get_config('main') if not section: section = "logging.{}".format(logger.name) dconf = conf_to_dict(conf, section) # get the class to use to get rid of old handlers parent_class = None if dconf.get('remove_std', False): parent_class = logging.StreamHandler if dconf.get('remove_old', False): parent_class = logging.Handler # get rid of old handlers if parent_class: for h in logger.handlers: if isinstance(h, parent_class): logger.removeHandler(h) # set the logger level logger.setLevel(_to_log_level(dconf.get('logger_level'))) handler = make_handler(dconf) if handler: logger.addHandler(handler)
[docs]def astropy_handlers(conf): """Tweak the astropy logger removing the existing (stream) handlers and setting a new one if required Parameters ---------- conf : :class:`~pyhetdex.tools.configuration.ConfigParser` instance configuration options, """ from astropy import log as astropy_log make_logger(astropy_log, conf=conf, section='logging.astropy')
[docs]def ginga_handlers(conf): """Create/update the ginga logger removing existing handlers setting a new one if required Parameters ---------- conf : :class:`~pyhetdex.tools.configuration.ConfigParser` instance configuration options, """ make_logger(logging.getLogger('ginga'), conf=conf)
[docs]def cure_logger(name): """Get the logger with ``name``. The first time this function is called with a ``name``, the logger writing to a file in the log directory and called ``name.log`` is created Parameters ---------- name : string name of the logger Returns ------- :class:`logging.Logger` instance string name of the file where the logs are saved .. todo:: redo it when finishing the implementation of the command interpreter """ try: return _cure_loggers[name] except KeyError: # Create logger for the cure task conf = vdatconfig.get_config('main') log = logging.getLogger(name) # first clear all the handlers to avoid problems with handlers # inherited from parent loggers for h in log.handlers: log.removeHandler(h) if conf.getboolean('logging', 'cure_enabled'): log_dir = conf.get('logging', 'logdir') cure_name = name + conf.get("logging", "cure_ext") cure_level = _get_handler_level(conf, "logging", "cure_level") cure_logform = conf.get("logging", "cure_logform") hf = _file_handler(log_dir, cure_name, cure_level, cure_logform) log.addHandler(hf) full_name = os.path.join(log_dir, cure_name) else: full_name = "No cure file logging enabled" _cure_loggers[name] = [log, full_name] return _cure_loggers[name]
[docs]def setup_command_loggers(): """Set up the loggers for the commands connected or connectible to buttons""" main_config = vdatconfig.get_config('main') command_config = vdatconfig.get_config('commands') section = 'logging.commands' for k, v in six.iteritems(command_config): # loop through all the commands and create a logger accordingly try: exe_name = v['is_alias_of'] except KeyError: exe_name = k file_name = '{}{}'.format(exe_name, main_config.get(section, 'extension')) main_config.set(section, 'file_name', file_name) logger = logging.getLogger(name=k) make_logger(logger, conf=main_config, section=section)
[docs]def conf_to_dict(conf, section, from_parent=['logdir', 'logger_level']): """Extract the ``section`` and convert it to a dictionary. If ``from_parent`` is a non empty list, it will get the name of the parent section, e.g. if ``section=logging.main.stdout``, the parent section is ``logging``. Then if any element in ``from_parent`` doesn't exist in ``section``, it tries to get to from the parent section. If it doesn't exist there, it's skipped. Parameters ---------- conf : :class:`~pyhetdex.tools.configuration.ConfigParser` instance configuration options, section : string name of the section in the configuration where to find all the info Returns ------- dict """ dconf = dict(conf.items(section)) if from_parent: parent_section = section.split('.')[0] for fp in from_parent: if fp not in dconf and conf.has_option(parent_section, fp): dconf[fp] = conf.get(parent_section, fp) return dconf
[docs]def make_handler(conf): """Create a handler following the instructions contained in ``section`` It looks for the following options: * enabled: if False, doesn't create a logger and returns None; default False * file_name: if not found, a :class:`~logging.StreamHandler` is created, if exists, a :class:`~logging.FileHandler` is created * logdir: directory where the log file goes; default '.' * format: formatting of the message, default %(levelname)s: %(message)s * level: level of the handler, default INFO Parameters ---------- conf : dict configuration options for the handler; it can be created using :func:`conf_to_dict` Returns ------- None, if enabled is ``False`` :class:`~logging.StreamHandler`: if not file name is given :class:`~logging.FileHandler`: otherwise """ if not conf.get('enabled', False): return None file_name = conf.get('file_name', None) if file_name: logdir = conf.get('logdir', os.curdir) if not os.path.exists(logdir): os.mkdir(logdir) handler = logging.FileHandler(os.path.join(logdir, file_name)) else: handler = logging.StreamHandler() logform = conf.get('format', '%(levelname)s: %(message)s') handler.setFormatter(logging.Formatter(logform)) handler.setLevel(_to_log_level(conf.get("level", logging.INFO))) return handler
[docs]def _to_log_level(level, default=logging.INFO): """Try to convert ``level`` to int or to extract the level ``level`` from the logging module. If both fail, return the default. See `here <https://docs.python.org/3.4/library/logging.html?highlight=logging#logging-levels>`_ for more info about logging levels. Parameters ---------- level : string numerical or literal value of the level Returns ------- int handler level """ try: return int(level) except ValueError: # it's not an integer pass return getattr(logging, level, default)