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.
This commit is contained in:
Rebecca Breu 2021-07-10 20:10:24 +02:00
parent 2de388ec5f
commit 849de6197a
8 changed files with 132 additions and 69 deletions

View file

@ -1,3 +1,12 @@
0.2.0 - unreleased
==================
Changed
-------
* Make debug log file less verbose
0.1.1 - 2021-07-18
==================

View file

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

62
beeref/logging.py Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
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)

View file

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

View file

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

View file

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

55
tests/test_logging.py Normal file
View file

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

View file

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