Skip to main content

pyqt-logging-manager

Reusable logging utilities for PyQt6 applications.

This package wraps the complete logging setup - file handlers, GUI handler, and history dialogs - in one LoggingManager class that can be reused in PyQt6 projects without project-specific changes.

Current version: 1.1.1


Contents


Features

  • LoggingManager: initialize once and let the manager wire everything up.
  • RotatingFileHandler for all file handlers, with configurable maximum file size and backup count.
  • Optional handlers for main logs, error logs, and debug logs.
  • GUI integration through QTextEdit with level-based color coding.
  • Log history dialogs with filter buttons (All / Warnings + Errors / Errors Only).
  • Bounded GUI history through the optional max_records setting.
  • Structured formatter configuration through the LogColumn enum instead of raw format strings.
  • Thread-safe GUI logging: GuiLogger forwards records from worker threads to the GUI thread through a Qt signal.
  • Safe log paths: generated file names cannot escape the configured log directory.
  • No project-specific code and no external runtime dependency besides PyQt6.

Requirements

  • Python >= 3.13
  • PyQt6

Installation

Install From PyPI

pip install pyqt-logging-manager

Editable Install From A Local Checkout

Use this during development so package changes are immediately visible in consuming projects.

# Clone the package from GitLab
git clone git@gitlab.dlr.de:fm/fks/thermoelektrik/python-lib/pyqt-logging-manager.git

# Install it into the target project after activating its virtual environment
pip install -e /path/to/pyqt-logging-manager

Install Directly From GitLab

pip install git+ssh://git@gitlab.dlr.de/fm/fks/thermoelektrik/python-lib/pyqt-logging-manager.git

Install A Specific Version Or Tag

pip install git+ssh://git@gitlab.dlr.de/fm/fks/thermoelektrik/python-lib/pyqt-logging-manager.git@v1.1.1

Quick Start

Minimal GUI-Only Usage

from pyqt_logging_manager import LoggingManager

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUi("my_app.ui", self)

        self.log_manager = LoggingManager(system_name="MyTool")
        self.log_manager.connect_widget(self.plainLog)  # QTextEdit from the UI

        logger = self.log_manager.get_logger("MAIN")
        logger.info("Application started")

Standard File Configuration

from pyqt_logging_manager import LoggingManager

self.log_manager = LoggingManager(
    system_name="MyApplication",
    system_version="1.2.0",
    log_dir="logs/",
)
self.log_manager.connect_widget(self.plainLog)

Creates: logs/MyApplication.log

LoggingManager currently attaches its handlers to Python's root logger. This allows loggers created in other modules with logging.getLogger(...) to use the same handlers without receiving a manager instance.

Full Configuration

from pyqt_logging_manager import LoggingManager, LogColumn
import logging

self.log_manager = LoggingManager(
    system_name="MyApplication",
    system_version="1.2.0",
    log_dir="logs/",
    level=logging.INFO,
    delete_old_logs=True,
    error_log=True,          # -> logs/MyApplication_Error.log
    debug=True,              # -> logs/MyApplication_Debug.log
    max_bytes=20_000_000,    # 20 MB per file
    backup_count=5,          # max. 5 backup files
    columns=[
        (LogColumn.TIMESTAMP, 32),
        (LogColumn.LEVEL,     10),
        (LogColumn.LOGGER,    16),
        (LogColumn.MESSAGE,    0),
    ],
    separator="\t",
)
self.log_manager.connect_widget(self.plainLog)

# Run custom logic for warnings, for example opening a dialog
self.log_manager.connect_extra_slot(self._on_warning)

logger = self.log_manager.get_logger("MAIN")
logger.info("Ready")

LoggingManager API

Constructor

LoggingManager(
    system_name: str,
    system_version: str = "",
    log_dir: str | None = None,
    level: int = logging.INFO,
    delete_old_logs: bool = True,
    error_log: bool = False,
    debug: bool = False,
    max_bytes: int = 20_000_000,
    backup_count: int = 5,
    formatter_string: str | None = None,
    formatter_width: int | None = None,
    columns: list[tuple[LogColumn, int]] | None = None,
    separator: str = "\t",
    max_records: int | None = None,
)
Parameter Type Default Description
system_name str - Application name used for log headers and as the sanitized file-name base.
system_version str "" Optional version shown in the log header.
log_dir str | None None Directory for log files. None means GUI-only logging.
level int logging.INFO Minimum root logger level.
delete_old_logs bool True Delete current log files and rotating backups at startup. When False, existing logs are preserved and a new start header is appended.
error_log bool False Create a separate error FileHandler for WARNING and above.
debug bool False Create a debug RotatingFileHandler for DEBUG and above.
max_bytes int 20_000_000 Maximum file size in bytes for all file handlers.
backup_count int 5 Maximum number of backup files per handler.
formatter_string str | None None Raw Python logging format string. Overrides columns.
formatter_width int | None 128 Automatic line wrap width.
columns list | None None Structured column definition; see LogColumn.
separator str "\t" Separator between columns.
max_records int | None None Maximum entries retained in GUI widgets and history dialogs.

Formatter priority: formatter_string > columns > default format.

Log File Names And Existing Logs

Only letters, numbers, _, and - from system_name are used unchanged in generated file names. Spaces, path separators, dots, and other characters become _. Windows-reserved names such as CON, NUL, COM1, and LPT1 receive a leading underscore.

My Application  -> My_Application.log
../outside      -> ___outside.log
CON             -> _CON.log

All resolved log paths must remain inside log_dir; paths through symlinks to files outside that directory are rejected with ValueError.

The default delete_old_logs=True starts with fresh log files. To keep logs across application starts, set:

self.log_manager = LoggingManager(
    system_name="MyApplication",
    log_dir="logs/",
    delete_old_logs=False,
)

Methods

connect_widget(widget: QTextEdit) -> None

Connects a QTextEdit as GUI log output. In PyQt6 projects, call this after loadUi().

self.log_manager.connect_widget(self.plainLog)

get_logger(name: str) -> logging.Logger

Returns a named logger. All loggers automatically propagate to all registered handlers.

logger = self.log_manager.get_logger("CALC_ENGINE")
logger.warning("Temperature outside the expected range")

connect_extra_slot(slot) -> None

Connects an additional slot to the GuiLogger signal. Call this after connect_widget().

The slot must have the signature (message: str, level: int, levelname: str).

def _on_warning(self, _msg: str, level: int, _name: str) -> None:
    if level >= logging.WARNING:
        self.log_manager.show_errors(parent=self)

self.log_manager.connect_extra_slot(self._on_warning)

show_history(parent: QWidget | None = None) -> None

Opens the log history dialog with all records and filter buttons. If it is already open, it is brought to the foreground.

self.log_manager.show_history(parent=self)

show_errors(parent: QWidget | None = None) -> None

Opens the error log dialog for warnings and errors only. If it is already open, it is brought to the foreground.

self.log_manager.show_errors(parent=self)

Attributes

Attribute Type Description
log_records list[tuple[str, int, str]] All log records as (message, level, levelname).

LogColumn

Instead of a raw format string, the formatter can be configured with the LogColumn enum and column widths.

from pyqt_logging_manager import LogColumn

columns=[
    (LogColumn.TIMESTAMP, 32),   # Timestamp, 32 characters
    (LogColumn.LEVEL,     10),   # Log level, 10 characters
    (LogColumn.LOGGER,    16),   # Logger name, 16 characters
    (LogColumn.MESSAGE,    0),   # Message, no fixed width
]

Width 0 means no padding; the field is as wide as needed.

Available Column Types

LogColumn.X Content Example Value
TIMESTAMP Date and time 2026-05-05 14:32:01
LEVEL Log level INFO, WARNING, ERROR
LOGGER Logger name MAIN, CALC_ENGINE
MESSAGE Message Calculation finished
FILE Source file without path calc_engine.py
LINE Line number 217
FUNCTION Function name run_segment

Additional Classes And Functions

GuiLogger

Qt-based logging.Handler that forwards records as pyqtSignal(str, int, str). It is used internally by LoggingManager, but can also be used directly.

WrappingFormatter

logging.Formatter with automatic line wrapping. Continuation lines are indented below the first message line.

formatter = WrappingFormatter(
    fmt="%(asctime)s\t%(levelname)s\t%(message)s",
    width=100,
)

LogHistoryDialog

QDialog for displaying and filtering log records. It supports two modes:

  • mode="all": all records with filter buttons (All / Warnings + Errors / Errors Only).
  • mode="errors": warnings and errors only, with a checkbox to hide warnings.
dialog = LogHistoryDialog(self.log_manager.log_records, mode="all", parent=self)
dialog.show()

setup_exception_hook

Installs a global handler for uncaught exceptions.

from pyqt_logging_manager import setup_exception_hook

setup_exception_hook(
    logger=self.log_manager.get_logger("MAIN"),
    show_dialog=True,
    parent=self,
)

add_measurement_log_handler / remove_measurement_log_handler

Adds a temporary FileHandler for a measurement and removes it afterwards.

from pyqt_logging_manager import add_measurement_log_handler, remove_measurement_log_handler

# Start measurement
handler = add_measurement_log_handler(
    log_dir="measurements/2026-05-05/",
    measurement_name="Run_01",
)

# ... measurement is running ...

# End measurement
remove_measurement_log_handler(handler)

Measurement names use the same safe file-name conversion as system_name.

Legacy Helpers

setup_multi_handler_logger() and rotate_default_log_files() remain available for existing applications. New integrations should prefer LoggingManager, which provides rotating handlers and the complete GUI integration through one API.


Examples

Small GUI Tool Without Files

from pyqt_logging_manager import LoggingManager

self.log_manager = LoggingManager("MyTool")
self.log_manager.connect_widget(self.logWidget)
self.log_manager.get_logger("MAIN").info("Started")

Application With All Handlers

import logging
from pyqt_logging_manager import LoggingManager, LogColumn, setup_exception_hook

self.log_manager = LoggingManager(
    system_name="MySystem",
    system_version="2.0.0",
    log_dir="logs/",
    error_log=True,
    debug=True,
    columns=[
        (LogColumn.TIMESTAMP, 24),
        (LogColumn.LEVEL,      8),
        (LogColumn.LOGGER,    20),
        (LogColumn.MESSAGE,    0),
    ],
)
self.log_manager.connect_widget(self.plainLog)
self.log_manager.connect_extra_slot(self._auto_open_errors)

setup_exception_hook(
    logger=self.log_manager.get_logger("MAIN"),
    show_dialog=True,
    parent=self,
)

Loggers In Other Modules

# In calc_engine.py; no LoggingManager import required
import logging

class CalcEngine:
    def __init__(self):
        self.logger = logging.getLogger("CALC_ENGINE")

    def run(self):
        self.logger.info("Calculation starts")
        # ...
        self.logger.debug("Segment 5 finished")

All records automatically propagate to the root logger and therefore to all handlers managed by LoggingManager.


License

Internal - DLR FM-KP

Metadata

Release files for pyqt-logging-manager 1.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyqt-logging-manager 1.1.1
File Size Uploaded
pyqt_logging_manager-1.1.1.tar.gz 18.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyqt-logging-manager 1.1.1
File Interpreter ABI Platform
pyqt_logging_manager-1.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 33.5 kB

Release files / pyqt_logging_manager-1.1.1.tar.gz

Download URL pyqt_logging_manager-1.1.1.tar.gz
Size 18.4 kB
Tags Source
SHA-256 checksum
How to use checksums
88487fbdb24fb7b67aaef6455783117f3248824def19ebe0de5d3bf03c297418
BLAKE2b-256 checksum
How to use checksums
3e647ccc00c32de67952000484437c55e3928b7d7be74cad639f04919b99e06e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / pyqt_logging_manager-1.1.1-py3-none-any.whl

Download URL pyqt_logging_manager-1.1.1-py3-none-any.whl
Size 15.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0400d7f5b3472dcc8b3b5145d7e4ad14d1c1b4247a25799a28cf03a416aeb0cf
BLAKE2b-256 checksum
How to use checksums
8592bcc553cfb48b22e1408398b2b8f086484980adc1af152cde378a80025785
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

1.1.1 This release

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page