Update log formatting again (#2117)
This commit is contained in:
+42
-28
@@ -4,7 +4,6 @@ novelWriter – Init File
|
|||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2018-09-22 [0.0.1] main
|
Created: 2018-09-22 [0.0.1] main
|
||||||
Created: 2024-11-22 [2.6b2] ColorFormatter
|
|
||||||
|
|
||||||
This file is a part of novelWriter
|
This file is a part of novelWriter
|
||||||
Copyright 2018–2024, Veronica Berglyd Olsen
|
Copyright 2018–2024, Veronica Berglyd Olsen
|
||||||
@@ -61,10 +60,26 @@ logger = logging.getLogger(__name__)
|
|||||||
# Main Program
|
# Main Program
|
||||||
##
|
##
|
||||||
|
|
||||||
# Global config and data singletons
|
# Globals Singletons
|
||||||
CONFIG = Config()
|
CONFIG = Config()
|
||||||
SHARED = SharedData()
|
SHARED = SharedData()
|
||||||
|
|
||||||
|
# ANSI Colours
|
||||||
|
C_RED = "\033[91m"
|
||||||
|
C_GREEN = "\033[92m"
|
||||||
|
C_YELLOW = "\033[93m"
|
||||||
|
C_BLUE = "\033[94m"
|
||||||
|
C_WHITE = "\033[97m"
|
||||||
|
C_END = "\033[0m"
|
||||||
|
|
||||||
|
# Log Formats
|
||||||
|
L_TIME = "[{asctime:}]"
|
||||||
|
L_FILE = "{filename:>18}"
|
||||||
|
L_LINE = "{lineno:<4d}"
|
||||||
|
L_LVLP = "{levelname:8}"
|
||||||
|
L_LVLC = "{levelname:17}"
|
||||||
|
L_TEXT = "{message:}"
|
||||||
|
|
||||||
|
|
||||||
def main(sysArgs: list | None = None) -> GuiMain | None:
|
def main(sysArgs: list | None = None) -> GuiMain | None:
|
||||||
"""Parse command line, set up logging, and launch main GUI."""
|
"""Parse command line, set up logging, and launch main GUI."""
|
||||||
@@ -109,13 +124,12 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
|
|||||||
|
|
||||||
# Defaults
|
# Defaults
|
||||||
logLevel = logging.WARN
|
logLevel = logging.WARN
|
||||||
logFormat = "{levelname:8} {message:}"
|
fmtFlags = 0b00
|
||||||
logFormatter = logging.Formatter
|
|
||||||
confPath = None
|
confPath = None
|
||||||
dataPath = None
|
dataPath = None
|
||||||
testMode = False
|
testMode = False
|
||||||
qtStyle = "Fusion"
|
qtStyle = "Fusion"
|
||||||
cmdOpen = None
|
cmdOpen = None
|
||||||
|
|
||||||
# Parse Options
|
# Parse Options
|
||||||
try:
|
try:
|
||||||
@@ -139,10 +153,10 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
|
|||||||
logLevel = logging.INFO
|
logLevel = logging.INFO
|
||||||
elif inOpt == "--debug":
|
elif inOpt == "--debug":
|
||||||
CONFIG.isDebug = True
|
CONFIG.isDebug = True
|
||||||
|
fmtFlags = fmtFlags | 0b10
|
||||||
logLevel = logging.DEBUG
|
logLevel = logging.DEBUG
|
||||||
logFormat = "[{asctime:}] {filename:>18}:{lineno:<4d} {levelname:8} {message:}"
|
|
||||||
elif inOpt == "--color":
|
elif inOpt == "--color":
|
||||||
logFormatter = ColorFormatter
|
fmtFlags = fmtFlags | 0b01
|
||||||
elif inOpt == "--style":
|
elif inOpt == "--style":
|
||||||
qtStyle = inArg
|
qtStyle = inArg
|
||||||
elif inOpt == "--config":
|
elif inOpt == "--config":
|
||||||
@@ -154,13 +168,32 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
|
|||||||
elif inOpt == "--meminfo":
|
elif inOpt == "--meminfo":
|
||||||
CONFIG.memInfo = True
|
CONFIG.memInfo = True
|
||||||
|
|
||||||
|
if fmtFlags & 0b01:
|
||||||
|
# This will overwrite the default level names, and also ensure that
|
||||||
|
# they can be converted back to integer levels
|
||||||
|
logging.addLevelName(logging.DEBUG, f"{C_BLUE}DEBUG{C_END}")
|
||||||
|
logging.addLevelName(logging.INFO, f"{C_GREEN}INFO{C_END}")
|
||||||
|
logging.addLevelName(logging.WARNING, f"{C_YELLOW}WARNING{C_END}")
|
||||||
|
logging.addLevelName(logging.ERROR, f"{C_RED}ERROR{C_END}")
|
||||||
|
logging.addLevelName(logging.CRITICAL, f"{C_RED}CRITICAL{C_END}")
|
||||||
|
|
||||||
|
# Determine Log Format
|
||||||
|
if fmtFlags == 0b00:
|
||||||
|
logFmt = f"{L_LVLP} {L_TEXT}"
|
||||||
|
elif fmtFlags == 0b01:
|
||||||
|
logFmt = f"{L_LVLC} {L_TEXT}"
|
||||||
|
elif fmtFlags == 0b10:
|
||||||
|
logFmt = f"{L_TIME} {L_FILE}:{L_LINE} {L_LVLP} {L_TEXT}"
|
||||||
|
elif fmtFlags == 0b11:
|
||||||
|
logFmt = f"{L_TIME} {C_BLUE}{L_FILE}{C_END}:{C_WHITE}{L_LINE}{C_END} {L_LVLC} {L_TEXT}"
|
||||||
|
|
||||||
# Setup Logging
|
# Setup Logging
|
||||||
pkgLogger = logging.getLogger(__package__)
|
pkgLogger = logging.getLogger(__package__)
|
||||||
pkgLogger.setLevel(logLevel)
|
pkgLogger.setLevel(logLevel)
|
||||||
if len(pkgLogger.handlers) == 0:
|
if len(pkgLogger.handlers) == 0:
|
||||||
# Make sure we only create one logger (mostly an issue with tests)
|
# Make sure we only create one logger (mostly an issue with tests)
|
||||||
cHandle = logging.StreamHandler()
|
cHandle = logging.StreamHandler()
|
||||||
cHandle.setFormatter(logFormatter(fmt=logFormat, style="{"))
|
cHandle.setFormatter(logging.Formatter(fmt=logFmt, style="{"))
|
||||||
pkgLogger.addHandler(cHandle)
|
pkgLogger.addHandler(cHandle)
|
||||||
|
|
||||||
logger.info("Starting novelWriter %s (%s) %s", __version__, __hexversion__, __date__)
|
logger.info("Starting novelWriter %s (%s) %s", __version__, __hexversion__, __date__)
|
||||||
@@ -247,22 +280,3 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
|
|||||||
nwGUI.postLaunchTasks(cmdOpen)
|
nwGUI.postLaunchTasks(cmdOpen)
|
||||||
|
|
||||||
sys.exit(app.exec())
|
sys.exit(app.exec())
|
||||||
|
|
||||||
|
|
||||||
class ColorFormatter(logging.Formatter):
|
|
||||||
|
|
||||||
def __init__(self, fmt: str, style: str) -> None:
|
|
||||||
super().__init__(fmt, style="{")
|
|
||||||
self._formats = {
|
|
||||||
logging.DEBUG: f"\033[1;34m{fmt}\033[0m",
|
|
||||||
logging.INFO: f"\033[1;32m{fmt}\033[0m",
|
|
||||||
logging.WARNING: f"\033[1;33m{fmt}\033[0m",
|
|
||||||
logging.ERROR: f"\033[1;31m{fmt}\033[0m",
|
|
||||||
logging.CRITICAL: f"\033[1;31m{fmt}\033[0m",
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
def format(self, record: logging.LogRecord) -> str:
|
|
||||||
"""Overload the format string for each record."""
|
|
||||||
self._style._fmt = self._formats.get(record.levelno, "ERR")
|
|
||||||
return super().format(record)
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from novelwriter import CONFIG, ColorFormatter, logger, main
|
from novelwriter import CONFIG, logger, main
|
||||||
|
|
||||||
from tests.mocked import MockGuiMain
|
from tests.mocked import MockGuiMain
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ def testBaseInit_Options(monkeypatch, fncPath):
|
|||||||
assert logger.getEffectiveLevel() == logging.WARNING
|
assert logger.getEffectiveLevel() == logging.WARNING
|
||||||
assert nwGUI.closeMain() == "closeMain"
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
|
||||||
# Log Levels
|
# Log Levels w/Color
|
||||||
nwGUI = main(
|
nwGUI = main(
|
||||||
["--testmode", "--info", "--color", f"--config={fncPath}", f"--data={fncPath}"]
|
["--testmode", "--info", "--color", f"--config={fncPath}", f"--data={fncPath}"]
|
||||||
)
|
)
|
||||||
@@ -112,6 +112,21 @@ def testBaseInit_Options(monkeypatch, fncPath):
|
|||||||
assert logger.getEffectiveLevel() == logging.DEBUG
|
assert logger.getEffectiveLevel() == logging.DEBUG
|
||||||
assert nwGUI.closeMain() == "closeMain"
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
|
||||||
|
# Log Levels wo/Color
|
||||||
|
nwGUI = main(
|
||||||
|
["--testmode", "--info", f"--config={fncPath}", f"--data={fncPath}"]
|
||||||
|
)
|
||||||
|
assert nwGUI is not None
|
||||||
|
assert logger.getEffectiveLevel() == logging.INFO
|
||||||
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
|
||||||
|
nwGUI = main(
|
||||||
|
["--testmode", "--debug", f"--config={fncPath}", f"--data={fncPath}"]
|
||||||
|
)
|
||||||
|
assert nwGUI is not None
|
||||||
|
assert logger.getEffectiveLevel() == logging.DEBUG
|
||||||
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
|
||||||
# Help and Version
|
# Help and Version
|
||||||
with pytest.raises(SystemExit) as ex:
|
with pytest.raises(SystemExit) as ex:
|
||||||
nwGUI = main(
|
nwGUI = main(
|
||||||
@@ -171,24 +186,3 @@ def testBaseInit_Imports(caplog, monkeypatch, fncPath):
|
|||||||
assert "At least Python" in caplog.messages[0]
|
assert "At least Python" in caplog.messages[0]
|
||||||
assert "At least Qt5" in caplog.messages[1]
|
assert "At least Qt5" in caplog.messages[1]
|
||||||
assert "At least PyQt5" in caplog.messages[2]
|
assert "At least PyQt5" in caplog.messages[2]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.base
|
|
||||||
def testBaseInit_ColorFormatter(qtbot, monkeypatch, fncPath, tstPaths):
|
|
||||||
"""Check launching the main GUI."""
|
|
||||||
formatter = ColorFormatter(fmt="<{message:}>", style="{")
|
|
||||||
|
|
||||||
record = logging.LogRecord("", logging.INFO, "", 1, "Info", None, None)
|
|
||||||
assert formatter.format(record) == "\x1b[1;32m<Info>\x1b[0m"
|
|
||||||
|
|
||||||
record = logging.LogRecord("", logging.DEBUG, "", 1, "Debug", None, None)
|
|
||||||
assert formatter.format(record) == "\x1b[1;34m<Debug>\x1b[0m"
|
|
||||||
|
|
||||||
record = logging.LogRecord("", logging.WARNING, "", 1, "Warning", None, None)
|
|
||||||
assert formatter.format(record) == "\x1b[1;33m<Warning>\x1b[0m"
|
|
||||||
|
|
||||||
record = logging.LogRecord("", logging.ERROR, "", 1, "Error", None, None)
|
|
||||||
assert formatter.format(record) == "\x1b[1;31m<Error>\x1b[0m"
|
|
||||||
|
|
||||||
record = logging.LogRecord("", logging.CRITICAL, "", 1, "Critical", None, None)
|
|
||||||
assert formatter.format(record) == "\x1b[1;31m<Critical>\x1b[0m"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user