Dev_help: trying to add logs to deve_help panel and extend to different error codes

This commit is contained in:
2026-02-27 16:24:54 +01:00
parent c54d3c1f1e
commit 3fcd084b21
+117 -4
View File
@@ -1,10 +1,11 @@
from __future__ import annotations
import json
import logging
from typing import Dict
from PySide6.QtCore import Qt, Slot
from PySide6.QtGui import QGuiApplication
from PySide6.QtCore import Qt, Slot, QUrl
from PySide6.QtGui import QGuiApplication, QDesktopServices
from PySide6.QtWidgets import (
QDialog,
QVBoxLayout,
@@ -24,6 +25,7 @@ from PySide6.QtWidgets import (
)
from aare.common.error_codes import error_code_help
from aare.common.logger_config import QtLogEmitter, QtLogHandler, find_existing_formatter, attach_to_logger
from aare.gui.threads.daq_worker import DAQWorker
@@ -110,6 +112,17 @@ class DeveloperHelpDialog(QDialog):
self._copy_payload_btn.clicked.connect(self._copy_payload)
top.addWidget(self._copy_payload_btn)
# Staff utilities: open log files
self._open_gui_log_btn = QPushButton("Open GUI log", self)
self._open_gui_log_btn.setVisible(self._is_staff)
self._open_gui_log_btn.clicked.connect(lambda: self._open_log_file_for_logger("aareGUI"))
top.addWidget(self._open_gui_log_btn)
self._open_daq_log_btn = QPushButton("Open DAQ log", self)
self._open_daq_log_btn.setVisible(self._is_staff)
self._open_daq_log_btn.clicked.connect(lambda: self._open_log_file_for_logger("aareDAQ"))
top.addWidget(self._open_daq_log_btn)
# Tabs
self._tabs = QTabWidget(self)
root.addWidget(self._tabs, 1)
@@ -125,7 +138,6 @@ class DeveloperHelpDialog(QDialog):
self._codes_table.itemSelectionChanged.connect(self._update_code_details)
self._codes_table.horizontalHeader().setStretchLastSection(True)
# Details panel (pretty form layout)
self._details_frame = QFrame(self)
self._details_frame.setFrameShape(QFrame.Shape.StyledPanel)
self._details_frame.setStyleSheet(
@@ -183,7 +195,12 @@ class DeveloperHelpDialog(QDialog):
self._tabs.addTab(codes_container, "Error codes")
# Tab: last error payload (staff only)
# Tab: last error payload (staff only) + summary header
self._payload_summary = QLabel(self)
self._payload_summary.setVisible(self._is_staff)
self._payload_summary.setWordWrap(True)
self._payload_summary.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
self._payload_text = QTextEdit(self)
self._payload_text.setReadOnly(True)
self._payload_text.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)
@@ -192,11 +209,48 @@ class DeveloperHelpDialog(QDialog):
payload_container = QWidget(self)
payload_layout = QVBoxLayout(payload_container)
payload_layout.setContentsMargins(0, 0, 0, 0)
payload_layout.addWidget(self._payload_summary, 0)
payload_layout.addWidget(self._payload_text, 1)
self._tabs.addTab(payload_container, "Last error payload")
else:
self._payload_text.setPlainText("Hidden (staff only).")
# Tab: recent payloads (staff only)
self._payloads_text = QTextEdit(self)
self._payloads_text.setReadOnly(True)
self._payloads_text.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)
if self._is_staff:
payloads_container = QWidget(self)
payloads_layout = QVBoxLayout(payloads_container)
payloads_layout.setContentsMargins(0, 0, 0, 0)
payloads_layout.addWidget(self._payloads_text, 1)
self._tabs.addTab(payloads_container, "Recent payloads")
# Tab: tracebacks (staff only)
self._tracebacks_text = QTextEdit(self)
self._tracebacks_text.setReadOnly(True)
self._tracebacks_text.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)
self._tracebacks_text.setPlainText("Tracebacks will appear here (last 10).")
if self._is_staff:
tb_container = QWidget(self)
tb_layout = QVBoxLayout(tb_container)
tb_layout.setContentsMargins(0, 0, 0, 0)
tb_layout.addWidget(self._tracebacks_text, 1)
self._tabs.addTab(tb_container, "Tracebacks")
# Tab: error log (staff only) live view from python logging
self._error_log_text = QTextEdit(self)
self._error_log_text.setReadOnly(True)
self._error_log_text.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)
if self._is_staff:
log_container = QWidget(self)
log_layout = QVBoxLayout(log_container)
log_layout.setContentsMargins(0, 0, 0, 0)
log_layout.addWidget(self._error_log_text, 1)
self._tabs.addTab(log_container, "Error log")
# Bottom button box
buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Close, parent=self)
buttons.rejected.connect(self.reject)
@@ -206,10 +260,40 @@ class DeveloperHelpDialog(QDialog):
# Wire signals
self._daq.error_codes_loaded.connect(self.set_error_codes)
self._daq.last_error_payload_changed.connect(self.set_last_error_payload)
self._daq.last_error_payloads_changed.connect(self.set_last_error_payloads)
# Hook a Qt logging handler to show ERROR+ messages in the dialog
if self._is_staff:
self._qt_log_emitter = QtLogEmitter()
self._qt_log_emitter.message.connect(self._append_error_log_line)
self._qt_log_handler = QtLogHandler(self._qt_log_emitter)
self._qt_log_handler.setLevel(logging.ERROR)
self._qt_log_handler.setFormatter(find_existing_formatter())
attach_to_logger("aareGUI", self._qt_log_handler)
attach_to_logger("aareDAQ", self._qt_log_handler)
self._update_banner()
self._update_code_details()
def _open_log_file_for_logger(self, logger_name: str) -> None:
"""
Opens the first FileHandler path attached to the given logger name.
"""
log = logging.getLogger(logger_name)
paths: list[str] = []
for h in getattr(log, "handlers", []) or []:
p = getattr(h, "baseFilename", None)
if p and isinstance(p, str):
paths.append(p)
if not paths:
self._banner.setText(f"No file handler found for logger '{logger_name}'.")
return
QDesktopServices.openUrl(QUrl.fromLocalFile(paths[0]))
@Slot(bool)
def _set_freeze_payload(self, enabled: bool) -> None:
self._freeze_payload = bool(enabled)
@@ -222,6 +306,8 @@ class DeveloperHelpDialog(QDialog):
def refresh(self) -> None:
self._daq.get_error_codes()
self.set_last_error_payload(self._daq.get_last_error_payload())
if self._is_staff:
self.set_last_error_payloads(self._daq.get_last_error_payloads())
@Slot(dict)
def set_error_codes(self, codes: dict) -> None:
@@ -238,9 +324,36 @@ class DeveloperHelpDialog(QDialog):
self._last_payload = payload or {}
pretty = json.dumps(self._last_payload, indent=2, sort_keys=True, default=str)
self._payload_text.setPlainText(pretty)
code, msg = self._extract_code_message()
url = str((self._last_payload or {}).get("url") or "")
status = (self._last_payload or {}).get("http_status")
self._payload_summary.setText(
f"URL: {url}\nHTTP: {status}\nCode: {code or '-'}\nMessage: {msg or '-'}"
)
self._update_banner()
self._select_code_from_last_error()
@Slot(list)
def set_last_error_payloads(self, payloads: list) -> None:
if not self._is_staff:
return
blocks: list[str] = []
for i, p in enumerate(payloads[-10:], start=max(1, len(payloads) - 9)):
try:
pretty = json.dumps(p or {}, indent=2, sort_keys=True, default=str)
except Exception:
pretty = str(p)
blocks.append(f"#{i}\n{pretty}")
self._payloads_text.setPlainText("\n\n".join(blocks) if blocks else "(none captured yet)")
@Slot(str)
def _append_error_log_line(self, line: str) -> None:
if not self._is_staff:
return
self._error_log_text.append(line)
def _code_name_for_value(self, value: str) -> str | None:
for k, v in self._codes.items():
if v == value: