From 849de6197a7beaab1d948139e3848008eeb7c705 Mon Sep 17 00:00:00 2001 From: Rebecca Breu Date: Sat, 10 Jul 2021 20:10:24 +0200 Subject: [PATCH] Add new loglevel TRACE The debug log file still only logs DEBUG and above, so it's less verbose now. TRACE output will only show in the console if the loglevel is set accordingly. --- CHANGELOG.rst | 9 +++++++ beeref/config.py | 25 +++-------------- beeref/logging.py | 62 +++++++++++++++++++++++++++++++++++++++++++ beeref/utils.py | 8 ------ beeref/view.py | 4 +-- tests/test_config.py | 18 +------------ tests/test_logging.py | 55 ++++++++++++++++++++++++++++++++++++++ tests/test_utils.py | 20 -------------- 8 files changed, 132 insertions(+), 69 deletions(-) create mode 100644 beeref/logging.py create mode 100644 tests/test_logging.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 647d40d..0101b39 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,12 @@ +0.2.0 - unreleased +================== + +Changed +------- + +* Make debug log file less verbose + + 0.1.1 - 2021-07-18 ================== diff --git a/beeref/config.py b/beeref/config.py index 70b0958..00261f0 100644 --- a/beeref/config.py +++ b/beeref/config.py @@ -23,6 +23,7 @@ import os.path from PyQt6 import QtCore from beeref import constants +from beeref.logging import qt_message_handler parser = argparse.ArgumentParser( @@ -161,7 +162,7 @@ logging_conf = { 'level': CommandlineArgs().loglevel, }, 'file': { - 'class': 'beeref.utils.BeeRotatingFileHandler', + 'class': 'beeref.logging.BeeRotatingFileHandler', 'formatter': 'verbose', 'filename': logfile_name(), 'maxBytes': 1024 * 1000, # 1MB @@ -173,7 +174,7 @@ logging_conf = { 'loggers': { 'beeref': { 'handlers': ['console', 'file'], - 'level': 'DEBUG', + 'level': 'TRACE', 'propagate': False, }, }, @@ -183,27 +184,7 @@ logging_conf = { }, } - logging.config.dictConfig(logging_conf) - # Redirect Qt logging to Python logger: -qtlogger = logging.getLogger('Qt') - - -def qt_message_handler(mode, context, message): - logfuncs = { - QtCore.QtMsgType.QtDebugMsg: qtlogger.debug, - QtCore.QtMsgType.QtInfoMsg: qtlogger.info, - QtCore.QtMsgType.QtWarningMsg: qtlogger.warning, - QtCore.QtMsgType.QtCriticalMsg: qtlogger.critical, - QtCore.QtMsgType.QtFatalMsg: qtlogger.fatal, - } - if context and (context.file or context.line or context.function): - message = (f'{message}: File {context.file}, line {context.line}, ' - f'in {context.function}') - - logfuncs[mode](message) - - QtCore.qInstallMessageHandler(qt_message_handler) diff --git a/beeref/logging.py b/beeref/logging.py new file mode 100644 index 0000000..9e1394b --- /dev/null +++ b/beeref/logging.py @@ -0,0 +1,62 @@ +# This file is part of BeeRef. +# +# BeeRef is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BeeRef 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BeeRef. If not, see . + +import logging +import logging.handlers +import os.path + +from PyQt6 import QtCore + + +logging.TRACE = 5 + + +class BeeLogger(logging.Logger): + + def __init__(self, name, level=logging.NOTSET): + super().__init__(name, level) + logging.addLevelName(logging.TRACE, 'TRACE') + + def trace(self, msg, *args, **kwargs): + self.log(logging.TRACE, msg, *args, **kwargs) + + +logging.setLoggerClass(BeeLogger) + + +class BeeRotatingFileHandler(logging.handlers.RotatingFileHandler): + """RotatingFileHandler that creates log directory if necessary.""" + + def __init__(self, filename, **kwargs): + os.makedirs(os.path.dirname(filename), exist_ok=True) + super().__init__(filename, **kwargs) + + +qtlogger = logging.getLogger('Qt') + + +def qt_message_handler(mode, context, message): + logfuncs = { + QtCore.QtMsgType.QtDebugMsg: qtlogger.debug, + QtCore.QtMsgType.QtInfoMsg: qtlogger.info, + QtCore.QtMsgType.QtWarningMsg: qtlogger.warning, + QtCore.QtMsgType.QtCriticalMsg: qtlogger.critical, + QtCore.QtMsgType.QtFatalMsg: qtlogger.fatal, + } + if context and (context.file or context.line or context.function): + message = (f'{message}: File {context.file}, line {context.line}, ' + f'in {context.function}') + + logfuncs[mode](message) diff --git a/beeref/utils.py b/beeref/utils.py index d1ba24d..389e38f 100644 --- a/beeref/utils.py +++ b/beeref/utils.py @@ -70,11 +70,3 @@ def round_to(number, base): """ return base * round(number / base) - - -class BeeRotatingFileHandler(logging.handlers.RotatingFileHandler): - """RotatingFileHandler that creates log directory if necessary.""" - - def __init__(self, filename, **kwargs): - os.makedirs(os.path.dirname(filename), exist_ok=True) - super().__init__(filename, **kwargs) diff --git a/beeref/view.py b/beeref/view.py index af1de8c..62f3af8 100644 --- a/beeref/view.py +++ b/beeref/view.py @@ -471,7 +471,7 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin): if self.previous_transform: return - logger.debug('Recalculating scene rectangle...') + logger.trace('Recalculating scene rectangle...') try: topleft = self.mapFromScene( self.scene.itemsBoundingRect().topLeft()) @@ -486,7 +486,7 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin): self.setSceneRect(QtCore.QRectF(topleft, bottomright)) except OverflowError: logger.info('Maximum scene size reached') - logger.debug('Done recalculating scene rectangle') + logger.trace('Done recalculating scene rectangle') def get_zoom_size(self, func): """Calculates the size of all items' bounding box in the view's diff --git a/tests/test_config.py b/tests/test_config.py index 973756d..7536df9 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,13 +1,10 @@ import os.path import tempfile -from types import SimpleNamespace from unittest.mock import patch import pytest -from PyQt6 import QtCore - -from beeref.config import CommandlineArgs, qt_message_handler +from beeref.config import CommandlineArgs def test_command_line_args_singleton(): @@ -74,16 +71,3 @@ def test_settings_recent_files_update_respects_max_num(settings): assert len(recent) == 10 assert recent[0] == os.path.abspath('14.bee') assert recent[-1] == os.path.abspath('5.bee') - - -@patch('beeref.config.qtlogger.info') -def test_qt_message_handler_without(log_mock, qapp): - qt_message_handler(QtCore.QtMsgType.QtInfoMsg, None, 'foo') - log_mock.assert_called_once_with('foo') - - -@patch('beeref.config.qtlogger.warning') -def test_qt_message_handler_with_context(log_mock, qapp): - ctx = SimpleNamespace(file='bla.txt', line='1', function='myfunc') - qt_message_handler(QtCore.QtMsgType.QtWarningMsg, ctx, 'foo') - log_mock.assert_called_once_with('foo: File bla.txt, line 1, in myfunc') diff --git a/tests/test_logging.py b/tests/test_logging.py new file mode 100644 index 0000000..2de789b --- /dev/null +++ b/tests/test_logging.py @@ -0,0 +1,55 @@ +import logging +import os.path +from types import SimpleNamespace +from unittest.mock import patch + +from PyQt6 import QtCore + +from beeref.logging import ( + BeeLogger, + BeeRotatingFileHandler, + qt_message_handler, +) + + +def test_sets_new_loglevel(): + assert logging.getLevelName(5) == 'TRACE' + + +@patch('beeref.logging.BeeLogger.log') +def test_beelogger(log_mock): + logger = BeeLogger('mylogger', logging.TRACE) + logger.trace('blah: %s', 'spam', extra={'foo': 'bar'}) + log_mock.assert_called_once_with( + logging.TRACE, 'blah: %s', 'spam', extra={'foo': 'bar'}) + + +def test_rotating_file_handler_creates_new_dir(tmpdir): + logfile = os.path.join(tmpdir, 'foo', 'bar.log') + handler = BeeRotatingFileHandler(logfile) + handler.emit(logging.LogRecord( + 'foo', logging.INFO, 'bar', 66, 'baz', [], None)) + handler.close() + assert os.path.exists(logfile) + + +def testrotating_file_handler_uses_existing_dir(tmpdir): + logfile = os.path.join(tmpdir, 'bar.log') + handler = BeeRotatingFileHandler(logfile) + handler.emit(logging.LogRecord( + 'foo', logging.INFO, 'bar', 66, 'baz', [], None)) + handler.close() + assert os.path.exists(logfile) + + +@patch('beeref.logging.qtlogger.info') +def test_qt_message_handler_without(log_mock, qapp): + qt_message_handler(QtCore.QtMsgType.QtInfoMsg, None, 'foo') + log_mock.assert_called_once_with('foo') + + +@patch('beeref.logging.qtlogger.warning') +def test_qt_message_handler_with_context(log_mock, qapp): + ctx = SimpleNamespace(file='bla.txt', line='1', function='myfunc') + qt_message_handler(QtCore.QtMsgType.QtWarningMsg, ctx, 'foo') + log_mock.assert_called_once_with('foo: File bla.txt, line 1, in myfunc') diff --git a/tests/test_utils.py b/tests/test_utils.py index 82f2356..3d69f55 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,5 +1,3 @@ -import logging -import os.path import pytest from PyQt6 import QtCore, QtGui @@ -65,21 +63,3 @@ def test_get_rect_from_points_given_topright_bottomleft(): (3.1, 0.5, 3.0)]) def test_round_to(number, base, expected): assert utils.round_to(number, base) == expected - - -def test_rotating_file_handler_creates_new_dir(tmpdir): - logfile = os.path.join(tmpdir, 'foo', 'bar.log') - handler = utils.BeeRotatingFileHandler(logfile) - handler.emit(logging.LogRecord( - 'foo', logging.INFO, 'bar', 66, 'baz', [], None)) - handler.close() - assert os.path.exists(logfile) - - -def testrotating_file_handler_uses_existing_dir(tmpdir): - logfile = os.path.join(tmpdir, 'bar.log') - handler = utils.BeeRotatingFileHandler(logfile) - handler.emit(logging.LogRecord( - 'foo', logging.INFO, 'bar', 66, 'baz', [], None)) - handler.close() - assert os.path.exists(logfile)