Blame dnf/logging.py

Packit Service 21c75c
# logging.py
Packit Service 21c75c
# DNF Logging Subsystem.
Packit Service 21c75c
#
Packit Service 21c75c
# Copyright (C) 2013-2016 Red Hat, Inc.
Packit Service 21c75c
#
Packit Service 21c75c
# This copyrighted material is made available to anyone wishing to use,
Packit Service 21c75c
# modify, copy, or redistribute it subject to the terms and conditions of
Packit Service 21c75c
# the GNU General Public License v.2, or (at your option) any later version.
Packit Service 21c75c
# This program is distributed in the hope that it will be useful, but WITHOUT
Packit Service 21c75c
# ANY WARRANTY expressed or implied, including the implied warranties of
Packit Service 21c75c
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
Packit Service 21c75c
# Public License for more details.  You should have received a copy of the
Packit Service 21c75c
# GNU General Public License along with this program; if not, write to the
Packit Service 21c75c
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 21c75c
# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
Packit Service 21c75c
# source code or documentation are not subject to the GNU General Public
Packit Service 21c75c
# License and may only be used or replicated with the express permission of
Packit Service 21c75c
# Red Hat, Inc.
Packit Service 21c75c
#
Packit Service 21c75c
Packit Service 21c75c
from __future__ import absolute_import
Packit Service 21c75c
from __future__ import unicode_literals
Packit Service 21c75c
import dnf.exceptions
Packit Service 21c75c
import dnf.const
Packit Service 21c75c
import dnf.lock
Packit Service 21c75c
import dnf.util
Packit Service 21c75c
import libdnf.repo
Packit Service 21c75c
import logging
Packit Service 21c75c
import logging.handlers
Packit Service 21c75c
import os
Packit Service 21c75c
import sys
Packit Service 21c75c
import time
Packit Service 21c75c
import warnings
Packit Service 21c75c
import gzip
Packit Service 21c75c
Packit Service 21c75c
# :api loggers are: 'dnf', 'dnf.plugin', 'dnf.rpm'
Packit Service 21c75c
Packit Service 21c75c
SUPERCRITICAL = 100 # do not use this for logging
Packit Service 21c75c
CRITICAL = logging.CRITICAL
Packit Service 21c75c
ERROR = logging.ERROR
Packit Service 21c75c
WARNING = logging.WARNING
Packit Service 21c75c
INFO = logging.INFO
Packit Service 21c75c
DEBUG = logging.DEBUG
Packit Service 21c75c
DDEBUG = 8  # used by anaconda (pyanaconda/payload/dnfpayload.py)
Packit Service 21c75c
SUBDEBUG = 6
Packit Service 21c75c
TRACE = 4
Packit Service 21c75c
ALL = 2
Packit Service 21c75c
Packit Service 21c75c
def only_once(func):
Packit Service 21c75c
    """Method decorator turning the method into noop on second or later calls."""
Packit Service 21c75c
    def noop(*_args, **_kwargs):
Packit Service 21c75c
        pass
Packit Service 21c75c
    def swan_song(self, *args, **kwargs):
Packit Service 21c75c
        func(self, *args, **kwargs)
Packit Service 21c75c
        setattr(self, func.__name__, noop)
Packit Service 21c75c
    return swan_song
Packit Service 21c75c
Packit Service 21c75c
class _MaxLevelFilter(object):
Packit Service 21c75c
    def __init__(self, max_level):
Packit Service 21c75c
        self.max_level = max_level
Packit Service 21c75c
Packit Service 21c75c
    def filter(self, record):
Packit Service 21c75c
        if record.levelno >= self.max_level:
Packit Service 21c75c
            return 0
Packit Service 21c75c
        return 1
Packit Service 21c75c
Packit Service 21c75c
_VERBOSE_VAL_MAPPING = {
Packit Service 21c75c
    0 : SUPERCRITICAL,
Packit Service 21c75c
    1 : logging.INFO,
Packit Service 21c75c
    2 : logging.INFO, # the default
Packit Service 21c75c
    3 : logging.DEBUG,
Packit Service 21c75c
    4 : logging.DEBUG,
Packit Service 21c75c
    5 : logging.DEBUG,
Packit Service 21c75c
    6 : logging.DEBUG, # verbose value
Packit Service 21c75c
    7 : DDEBUG,
Packit Service 21c75c
    8 : SUBDEBUG,
Packit Service 21c75c
    9 : TRACE,
Packit Service 21c75c
    10: ALL,   # more verbous librepo and hawkey
Packit Service 21c75c
    }
Packit Service 21c75c
Packit Service 21c75c
def _cfg_verbose_val2level(cfg_errval):
Packit Service 21c75c
    assert 0 <= cfg_errval <= 10
Packit Service 21c75c
    return _VERBOSE_VAL_MAPPING.get(cfg_errval, TRACE)
Packit Service 21c75c
Packit Service 21c75c
Packit Service 21c75c
# Both the DNF default and the verbose default are WARNING. Note that ERROR has
Packit Service 21c75c
# no specific level.
Packit Service 21c75c
_ERR_VAL_MAPPING = {
Packit Service 21c75c
    0: SUPERCRITICAL,
Packit Service 21c75c
    1: logging.CRITICAL,
Packit Service 21c75c
    2: logging.ERROR
Packit Service 21c75c
    }
Packit Service 21c75c
Packit Service 21c75c
def _cfg_err_val2level(cfg_errval):
Packit Service 21c75c
    assert 0 <= cfg_errval <= 10
Packit Service 21c75c
    return _ERR_VAL_MAPPING.get(cfg_errval, logging.WARNING)
Packit Service 21c75c
Packit Service 21c75c
Packit Service 21c75c
def compression_namer(name):
Packit Service 21c75c
    return name + ".gz"
Packit Service 21c75c
Packit Service 21c75c
Packit Service 21c75c
CHUNK_SIZE = 128 * 1024 # 128 KB
Packit Service 21c75c
Packit Service 21c75c
Packit Service 21c75c
def compression_rotator(source, dest):
Packit Service 21c75c
    with open(source, "rb") as sf:
Packit Service 21c75c
        with gzip.open(dest, 'wb') as wf:
Packit Service 21c75c
            while True:
Packit Service 21c75c
                data = sf.read(CHUNK_SIZE)
Packit Service 21c75c
                if not data:
Packit Service 21c75c
                    break
Packit Service 21c75c
                wf.write(data)
Packit Service 21c75c
    os.remove(source)
Packit Service 21c75c
Packit Service 21c75c
Packit Service 21c75c
class MultiprocessRotatingFileHandler(logging.handlers.RotatingFileHandler):
Packit Service 21c75c
    def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False):
Packit Service 21c75c
        super(MultiprocessRotatingFileHandler, self).__init__(
Packit Service 21c75c
            filename, mode, maxBytes, backupCount, encoding, delay)
Packit Service 21c75c
        self.rotate_lock = dnf.lock.build_log_lock("/var/log/", True)
Packit Service 21c75c
Packit Service 21c75c
    def emit(self, record):
Packit Service 21c75c
        while True:
Packit Service 21c75c
            try:
Packit Service 21c75c
                if self.shouldRollover(record):
Packit Service 21c75c
                    with self.rotate_lock:
Packit Service 21c75c
                        self.doRollover()
Packit Service 21c75c
                logging.FileHandler.emit(self, record)
Packit Service 21c75c
                return
Packit Service 21c75c
            except (dnf.exceptions.ProcessLockError, dnf.exceptions.ThreadLockError):
Packit Service 21c75c
                time.sleep(0.01)
Packit Service 21c75c
            except Exception:
Packit Service 21c75c
                self.handleError(record)
Packit Service 21c75c
                return
Packit Service 21c75c
Packit Service 21c75c
Packit Service 21c75c
def _create_filehandler(logfile, log_size, log_rotate, log_compress):
Packit Service 21c75c
    if not os.path.exists(logfile):
Packit Service 21c75c
        dnf.util.ensure_dir(os.path.dirname(logfile))
Packit Service 21c75c
        dnf.util.touch(logfile)
Packit Service 21c75c
        # By default, make logfiles readable by the user (so the reporting ABRT
Packit Service 21c75c
        # user can attach root logfiles).
Packit Service 21c75c
        os.chmod(logfile, 0o644)
Packit Service 21c75c
    handler = MultiprocessRotatingFileHandler(logfile, maxBytes=log_size, backupCount=log_rotate)
Packit Service 21c75c
    formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s",
Packit Service 21c75c
                                  "%Y-%m-%dT%H:%M:%S%z")
Packit Service 21c75c
    formatter.converter = time.localtime
Packit Service 21c75c
    handler.setFormatter(formatter)
Packit Service 21c75c
    if log_compress:
Packit Service 21c75c
        handler.rotator = compression_rotator
Packit Service 21c75c
        handler.namer = compression_namer
Packit Service 21c75c
    return handler
Packit Service 21c75c
Packit Service 21c75c
def _paint_mark(logger):
Packit Service 21c75c
    logger.log(INFO, dnf.const.LOG_MARKER)
Packit Service 21c75c
Packit Service 21c75c
Packit Service 21c75c
class Logging(object):
Packit Service 21c75c
    def __init__(self):
Packit Service 21c75c
        self.stdout_handler = self.stderr_handler = None
Packit Service 21c75c
        logging.addLevelName(DDEBUG, "DDEBUG")
Packit Service 21c75c
        logging.addLevelName(SUBDEBUG, "SUBDEBUG")
Packit Service 21c75c
        logging.addLevelName(TRACE, "TRACE")
Packit Service 21c75c
        logging.addLevelName(ALL, "ALL")
Packit Service 21c75c
        logging.captureWarnings(True)
Packit Service 21c75c
        logging.raiseExceptions = False
Packit Service 21c75c
Packit Service 21c75c
    @only_once
Packit Service 21c75c
    def _presetup(self):
Packit Service 21c75c
        logger_dnf = logging.getLogger("dnf")
Packit Service 21c75c
        logger_dnf.setLevel(TRACE)
Packit Service 21c75c
Packit Service 21c75c
        # setup stdout
Packit Service 21c75c
        stdout = logging.StreamHandler(sys.stdout)
Packit Service 21c75c
        stdout.setLevel(INFO)
Packit Service 21c75c
        stdout.addFilter(_MaxLevelFilter(logging.WARNING))
Packit Service 21c75c
        logger_dnf.addHandler(stdout)
Packit Service 21c75c
        self.stdout_handler = stdout
Packit Service 21c75c
Packit Service 21c75c
        # setup stderr
Packit Service 21c75c
        stderr = logging.StreamHandler(sys.stderr)
Packit Service 21c75c
        stderr.setLevel(WARNING)
Packit Service 21c75c
        logger_dnf.addHandler(stderr)
Packit Service 21c75c
        self.stderr_handler = stderr
Packit Service 21c75c
Packit Service 21c75c
    @only_once
Packit Service 21c75c
    def _setup_file_loggers(self, logfile_level, logdir, log_size, log_rotate, log_compress):
Packit Service 21c75c
        logger_dnf = logging.getLogger("dnf")
Packit Service 21c75c
        logger_dnf.setLevel(TRACE)
Packit Service 21c75c
Packit Service 21c75c
        # setup file logger
Packit Service 21c75c
        logfile = os.path.join(logdir, dnf.const.LOG)
Packit Service 21c75c
        handler = _create_filehandler(logfile, log_size, log_rotate, log_compress)
Packit Service 21c75c
        handler.setLevel(logfile_level)
Packit Service 21c75c
        logger_dnf.addHandler(handler)
Packit Service 21c75c
Packit Service 21c75c
        # setup Python warnings
Packit Service 21c75c
        logger_warnings = logging.getLogger("py.warnings")
Packit Service 21c75c
        logger_warnings.addHandler(handler)
Packit Service 21c75c
Packit Service 21c75c
        logger_librepo = logging.getLogger("librepo")
Packit Service 21c75c
        logger_librepo.setLevel(TRACE)
Packit Service 21c75c
        logfile = os.path.join(logdir, dnf.const.LOG_LIBREPO)
Packit Service 21c75c
        handler = _create_filehandler(logfile, log_size, log_rotate, log_compress)
Packit Service 21c75c
        logger_librepo.addHandler(handler)
Packit Service 21c75c
        libdnf.repo.LibrepoLog.addHandler(logfile, logfile_level <= ALL)
Packit Service 21c75c
Packit Service 21c75c
        # setup RPM callbacks logger
Packit Service 21c75c
        logger_rpm = logging.getLogger("dnf.rpm")
Packit Service 21c75c
        logger_rpm.propagate = False
Packit Service 21c75c
        logger_rpm.setLevel(SUBDEBUG)
Packit Service 21c75c
        logfile = os.path.join(logdir, dnf.const.LOG_RPM)
Packit Service 21c75c
        handler = _create_filehandler(logfile, log_size, log_rotate, log_compress)
Packit Service 21c75c
        logger_rpm.addHandler(handler)
Packit Service 21c75c
Packit Service 21c75c
    @only_once
Packit Service 21c75c
    def _setup(self, verbose_level, error_level, logfile_level, logdir, log_size, log_rotate, log_compress):
Packit Service 21c75c
        self._presetup()
Packit Service 21c75c
Packit Service 21c75c
        self._setup_file_loggers(logfile_level, logdir, log_size, log_rotate, log_compress)
Packit Service 21c75c
Packit Service 21c75c
        logger_warnings = logging.getLogger("py.warnings")
Packit Service 21c75c
        logger_warnings.addHandler(self.stderr_handler)
Packit Service 21c75c
Packit Service 21c75c
        # setup RPM callbacks logger
Packit Service 21c75c
        logger_rpm = logging.getLogger("dnf.rpm")
Packit Service 21c75c
        logger_rpm.addHandler(self.stdout_handler)
Packit Service 21c75c
        logger_rpm.addHandler(self.stderr_handler)
Packit Service 21c75c
Packit Service 21c75c
        logger_dnf = logging.getLogger("dnf")
Packit Service 21c75c
        # temporarily turn off stdout/stderr handlers:
Packit Service 21c75c
        self.stdout_handler.setLevel(WARNING)
Packit Service 21c75c
        self.stderr_handler.setLevel(WARNING)
Packit Service 21c75c
        _paint_mark(logger_dnf)
Packit Service 21c75c
        _paint_mark(logger_rpm)
Packit Service 21c75c
        # bring std handlers to the preferred level
Packit Service 21c75c
        self.stdout_handler.setLevel(verbose_level)
Packit Service 21c75c
        self.stderr_handler.setLevel(error_level)
Packit Service 21c75c
Packit Service 21c75c
    def _setup_from_dnf_conf(self, conf, file_loggers_only=False):
Packit Service 21c75c
        verbose_level_r = _cfg_verbose_val2level(conf.debuglevel)
Packit Service 21c75c
        error_level_r = _cfg_err_val2level(conf.errorlevel)
Packit Service 21c75c
        logfile_level_r = _cfg_verbose_val2level(conf.logfilelevel)
Packit Service 21c75c
        logdir = conf.logdir
Packit Service 21c75c
        log_size = conf.log_size
Packit Service 21c75c
        log_rotate = conf.log_rotate
Packit Service 21c75c
        log_compress = conf.log_compress
Packit Service 21c75c
        if file_loggers_only:
Packit Service 21c75c
            return self._setup_file_loggers(logfile_level_r, logdir, log_size, log_rotate, log_compress)
Packit Service 21c75c
        else:
Packit Service 21c75c
            return self._setup(
Packit Service 21c75c
                verbose_level_r, error_level_r, logfile_level_r, logdir, log_size, log_rotate, log_compress)
Packit Service 21c75c
Packit Service 21c75c
Packit Service 21c75c
class Timer(object):
Packit Service 21c75c
    def __init__(self, what):
Packit Service 21c75c
        self.what = what
Packit Service 21c75c
        self.start = time.time()
Packit Service 21c75c
Packit Service 21c75c
    def __call__(self):
Packit Service 21c75c
        diff = time.time() - self.start
Packit Service 21c75c
        msg = 'timer: %s: %d ms' % (self.what, diff * 1000)
Packit Service 21c75c
        logging.getLogger("dnf").log(DDEBUG, msg)
Packit Service 21c75c
Packit Service 21c75c
Packit Service 21c75c
_LIBDNF_TO_DNF_LOGLEVEL_MAPPING = {
Packit Service 21c75c
    libdnf.utils.Logger.Level_CRITICAL: CRITICAL,
Packit Service 21c75c
    libdnf.utils.Logger.Level_ERROR: ERROR,
Packit Service 21c75c
    libdnf.utils.Logger.Level_WARNING: WARNING,
Packit Service 21c75c
    libdnf.utils.Logger.Level_NOTICE: INFO,
Packit Service 21c75c
    libdnf.utils.Logger.Level_INFO: INFO,
Packit Service 21c75c
    libdnf.utils.Logger.Level_DEBUG: DEBUG,
Packit Service 21c75c
    libdnf.utils.Logger.Level_TRACE: TRACE
Packit Service 21c75c
}
Packit Service 21c75c
Packit Service 21c75c
Packit Service 21c75c
class LibdnfLoggerCB(libdnf.utils.Logger):
Packit Service 21c75c
    def __init__(self):
Packit Service 21c75c
        super(LibdnfLoggerCB, self).__init__()
Packit Service 21c75c
        self._dnf_logger = logging.getLogger("dnf")
Packit Service 21c75c
        self._librepo_logger = logging.getLogger("librepo")
Packit Service 21c75c
Packit Service 21c75c
    def write(self, source, *args):
Packit Service 21c75c
        """Log message.
Packit Service 21c75c
Packit Service 21c75c
        source -- integer, defines origin (libdnf, librepo, ...) of message, 0 - unknown
Packit Service 21c75c
        """
Packit Service 21c75c
        if len(args) == 2:
Packit Service 21c75c
            level, message = args
Packit Service 21c75c
        elif len(args) == 4:
Packit Service 21c75c
            time, pid, level, message = args
Packit Service 21c75c
        if source == libdnf.utils.Logger.LOG_SOURCE_LIBREPO:
Packit Service 21c75c
            self._librepo_logger.log(_LIBDNF_TO_DNF_LOGLEVEL_MAPPING[level], message)
Packit Service 21c75c
        else:
Packit Service 21c75c
            self._dnf_logger.log(_LIBDNF_TO_DNF_LOGLEVEL_MAPPING[level], message)
Packit Service 21c75c
Packit Service 21c75c
Packit Service 21c75c
libdnfLoggerCB = LibdnfLoggerCB()
Packit Service 21c75c
libdnf.utils.Log.setLogger(libdnfLoggerCB)