|
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)
|