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)