improved logging

This commit is contained in:
Ayzen
2026-06-06 00:52:52 +03:00
parent af6005d68f
commit aea49f6128
65 changed files with 1206 additions and 240 deletions
@@ -256,6 +256,12 @@ class AppWindowLiveProcessingMixin:
for name, value in fields.items()
if name not in {"history_command", "history_command_seq"}
}
# Log only field names (not values) so remote edits are traceable
# without recording arbitrary client-supplied payloads.
self._log_debug(
f"Applying web live settings: fields={sorted(settings)}, "
f"history_command={history_command}."
)
self._suppress_live_settings_handler = True
try:
# processor_mode first: dual-sourced gpr_* fields route to the gpr or
@@ -218,7 +218,16 @@ class AppWindowConfigProfileIOMixin:
self._show_exception("Failed to load config profile", exc)
def _apply_loaded_profile(self, profile: GuiProfileModel, profile_path: Path) -> None:
"""Apply already parsed profile to GUI state without restarting the pipeline."""
"""Apply an already parsed profile to GUI state without restarting the pipeline.
Repopulates every radar/processing/preprocess widget under signal blockers,
resizes history buffers, and refreshes derived state. The pipeline is left
untouched; callers handle user-facing logging and error reporting.
"""
self._log_debug(
f"Applying loaded profile: path={profile_path}, "
f"has_gui_state={profile.gui is not None}."
)
config = profile.run_config.clone()
gui_state = profile.gui if profile.gui is not None else self._default_gui_state_for_config(config)
self._defaults_config = config
@@ -422,6 +422,7 @@ class AppWindowConfigStateBuildersMixin:
config.runtime.settling_ms = int(self._settling_ms.text().strip())
config.runtime.processing_live_config_path = str(self._live_config_writer.path)
config.logging.level = self._log_level_combo.currentText().strip().lower()
if config.is_matrix_radar:
if config.is_multi_device and len(config.radar.multi_device.slave_serials) != 2:
@@ -304,16 +304,22 @@ class AppWindowPipelineMixin:
self._log("All pipeline processes stopped")
def _close_readers(self, *, keep_results: bool = False) -> None:
"""Close active ring readers."""
"""Close active ring readers; keep the results reader when ``keep_results``."""
closed = []
if self._raw_reader is not None:
self._raw_reader.close()
self._raw_reader = None
closed.append("raw")
if self._pre_reader is not None:
self._pre_reader.close()
self._pre_reader = None
closed.append("preprocessed")
if not keep_results and self._result_reader is not None:
self._result_reader.close()
self._result_reader = None
closed.append("results")
if closed:
self._log_debug(f"Closed ring readers: {', '.join(closed)}.")
def _poll_rings(self) -> None:
"""Poll readers, ingest history, and trigger rendering."""
@@ -58,7 +58,13 @@ class AppWindowPreprocessMixin:
"""Clear selected preprocess sets when radar-key-defining settings change."""
try:
radar_key = self._radar_key_from_ui()
except Exception:
except Exception as exc: # noqa: BLE001
# Radar fields can be mid-edit (empty/partial) while signals fire; the
# key cannot be computed yet, so skip until the inputs are valid again.
self._log_debug(
f"Skipping preprocess-selection reset; radar key unavailable: "
f"{type(exc).__name__}: {exc}"
)
return
previous_radar_key = getattr(self, "_selected_preprocess_radar_key", radar_key)
@@ -778,8 +784,11 @@ class AppWindowPreprocessMixin:
)
def _cleanup_capture_session(self) -> None:
"""Close and clear current capture session object."""
"""Close and clear the current capture session object."""
if self._capture_session is not None:
self._log_debug(
f"Closing capture session: kind={self._capture_session.kind}."
)
self._capture_session.close()
self._capture_session = None
self._update_capture_dialog_state()
@@ -33,6 +33,7 @@ from python_app.gui.controllers.sections import (
build_radar_group,
build_switch_group,
)
from python_app.logging_setup import LOG_LEVELS
class AppWindowUiMixin:
@@ -178,6 +179,20 @@ class AppWindowUiMixin:
self._log_toggle_button.setObjectName("sectionToggleButton")
self._log_toggle_button.clicked.connect(lambda: self._toggle_log_panel())
# Log-level selector: live verbosity control, persisted to run_config.
self._log_level_label = QLabel("Level", self._settings_panel)
self._log_level_label.setObjectName("hintLabel")
self._log_level_combo = QComboBox(self._settings_panel)
self._log_level_combo.setObjectName("logLevelCombo")
self._log_level_combo.addItems([name.capitalize() for name in LOG_LEVELS])
self._log_level_combo.setToolTip("Logging verbosity — applied live and saved to run_config")
current_level = self._defaults_config.logging.level.capitalize()
current_index = self._log_level_combo.findText(current_level)
if current_index >= 0:
self._log_level_combo.setCurrentIndex(current_index)
# Connect AFTER seeding the value so reflecting the config does not save it back.
self._log_level_combo.currentTextChanged.connect(self._on_log_level_selected)
self._log_box = QTextEdit(self._settings_panel)
self._log_box.setObjectName("runtimeLogBox")
self._log_box.setReadOnly(True)
@@ -196,6 +211,8 @@ class AppWindowUiMixin:
header_layout.setSpacing(8)
header_layout.addWidget(self._log_panel_title)
header_layout.addStretch(1)
header_layout.addWidget(self._log_level_label)
header_layout.addWidget(self._log_level_combo)
header_layout.addWidget(self._log_toggle_button)
panel_layout.addWidget(header_row)
@@ -198,6 +198,7 @@ class AppWindowWebMixin:
with contextlib.suppress(Exception):
server.stop()
self._web_server = None
self._log("Web UI stopped.")
self._web_controller = None
@staticmethod