GUI: Add "Generate debug log" action - #3328
Conversation
Introduce a new debug subsystem under deeplabcut/core/debug. Adds an in-memory, bounded log recorder (InMemoryDebugRecorder) with safe fail-open semantics, helper functions to install/get the recorder, and a scoped log_timing context manager. Provides utilities to collect runtime, package, and external executable information (LibrarySpec, ExecutableSpec, collect_version_summary, collect_executable_summary, collect_debug_sections) and to format/assemble a full debug report (format_debug_report, build_debug_report). Includes small helper functions (_which, _command_version) and sane defaults (lists of core/GUI/TensorFlow libs, ffmpeg executable). Exports are wired in __init__.py.
Introduce a reusable debug text dialog and helper functions for generating and displaying DeepLabCut diagnostic reports. Adds deeplabcut/gui/dialogs/debug_dialog.py implementing DebugTextDialog (read-only log view with refresh/copy/keyboard shortcut), providers to render logs and full issue reports, show_debug_report_dialog to install/reuse recorders and present the report, and create_generate_debug_log_action to wire up a QAction. Also exports these symbols via deeplabcut/gui/dialogs/__init__.py. Dialog instances are attached to the parent to avoid GC and duplicate windows.
Import and install a debug recorder and add a "Generate Debug Log" action to the Help menu. The patch imports create_generate_debug_log_action and install_debug_recorder, creates a recorder (logger_name="deeplabcut"), builds the debug-log action with specified options (include_module_paths=False, include_executable_paths=True, log_limit=1000), inserts it into the Help menu with a separator, and adjusts menu sizing. This allows users to generate debug logs from the GUI.
Add a new test module for deeplabcut.core.debug covering InMemoryDebugRecorder and log_timing. Tests verify install_debug_recorder idempotency and retrieval, message and exception capture, bounded buffer behavior, render_text contents, clear() resetting records and drop count, and log_timing behavior when enabled, disabled, and with thresholding. Uses pytest fixtures to create isolated logger namespaces and monkeypatch to simulate timings and logger behavior.
There was a problem hiding this comment.
Pull request overview
This PR adds a DeepLabCut-scoped debug reporting utility (in-memory log recorder + environment/package/tool summary) and wires it into the GUI via a new Help → Generate debug log action that shows a copy/paste-ready report dialog.
Changes:
- Added
deeplabcut.core.debugutilities to record recent logs and build an environment/debug report. - Added a new GUI dialog + action factory to display/copy the generated debug report.
- Added a minimal pytest suite covering the in-memory recorder and timing context manager.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
deeplabcut/core/debug/debug_logger.py |
Implements the recorder, timing helper, environment/tool/package collection, and report formatting. |
deeplabcut/core/debug/_debug_utils.py |
Adds helper utilities for resolving executables and extracting version output. |
deeplabcut/core/debug/__init__.py |
Exposes the debug utilities as a public package API. |
deeplabcut/gui/dialogs/debug_dialog.py |
Adds a reusable dialog for viewing/copying debug text + helpers to open it from actions. |
deeplabcut/gui/dialogs/__init__.py |
Exports the new debug dialog helpers at the package level. |
deeplabcut/gui/window.py |
Installs the recorder at GUI startup and adds the Help menu action. |
tests/core/debug/test_logger.py |
Adds tests for recorder behavior and log_timing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Causes conflicts with pytorch tests of same name in CI
Add project/license header comments to deeplabcut/core/debug/__init__.py and deeplabcut/gui/dialogs/__init__.py. In core/debug/__init__.py, clean up the public API exports: remove internal symbols DLC_LOG_TIMING and LOG_QUEUE_MAXLEN from __all__, reorder entries, and ensure collect_executable_summary is exported. These changes clarify public exports and add attribution/licensing headers.
Replace generic "GUI" and "console" loggers with namespaced loggers ("deeplabcut.gui" and "deeplabcut.gui.console"). Store a console logger on the MainWindow instance and use it for setting level and printing status messages. This standardizes logger names and avoids relying on a global "console" logger.
Add DebugTextDialog.update_content to update window title, text provider, and hint when reusing an existing dialog instance. Update _get_or_create_debug_dialog to call update_content for existing dialogs instead of recreating them. Rename the initial_hint parameter to text_hint and adjust the show_debug_report_dialog call site accordingly. This lets the debug dialog refresh its metadata without reconstructing the widget.
Add environment-driven debug/timing configuration and make the in-memory debug recorder less intrusive. Changes: - deeplabcut/core/debug/_debug_utils.py: add license header, import os, add reload_debug_settings_from_env(), and helper parsers _env_flag and _env_optional_float to read boolean/optional-float env vars. - deeplabcut/core/debug/debug_logger.py: add license header, read DLC_LOG_TIMING and DLC_LOG_TIMING_THRESHOLD_MS from environment, import the new env helpers, and use an effective_threshold in log_timing(). - install_debug_recorder(): add handler_level and ensure_logger_level params, use handler_level for the recorder, and only lower the logger level when explicitly requested. - Adjust external tools section reporting logic to avoid misleading reporting when executables is None. Rationale: allow debug timing and thresholds to be controlled via environment variables, make recorder installation configurable without forcing global logger verbosity, and improve robustness of external-tool reporting.
Move the reload_debug_settings_from_env function from deeplabcut/core/debug/_debug_utils.py into deeplabcut/core/debug/debug_logger.py. The function (which reloads DLC_LOG_TIMING and DLC_LOG_TIMING_THRESHOLD_MS from environment variables) is removed from _debug_utils.py and added to debug_logger.py to colocate timing/debug env handling with the logging code. No behavioral changes intended.
Import QApplication and set an override WaitCursor in DebugTextDialog.refresh_text while calling the text provider, restoring the cursor in a finally block. This provides visual feedback during potentially long debug-text generation and ensures the cursor is always reset even if the provider raises an exception.
Allow the in-memory debug recorder to optionally auto-initialize the target logger's level. Import typing.Literal and change install_debug_recorder's ensure_logger_level parameter to accept None, int, or the new "auto" sentinel (default). Expand the docstring to document behavior: "auto" sets the logger to handler_level only when the logger has no explicit level (NOTSET); int lowers the logger level only if more restrictive; None leaves the logger untouched. Adjust the logic accordingly and update the GUI caller to pass handler_level and ensure_logger_level="auto" when installing the recorder.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Convert libraries and executables to tuples in make_issue_report_provider so the returned provider can be called repeatedly even if given a one-shot iterable. Comment out redundant initial refresh_text() calls in the dialog (showEvent triggers the initial refresh). Also set logger.propagate = False in the test cleanup to avoid log propagation during tests.
Change install_debug_recorder to remove the special "auto" behavior for ensure_logger_level and make its default None; now only an int will lower the logger level when explicitly requested. Simplify some docstrings and clarify capacity wording. Add LibrarySpec.prefer_module_version and a new _module_version helper to prefer a module's __version__ when collecting versions (falling back to the distribution version if unavailable). Mark opencv-python to prefer the module version and update collect_version_summary to use the resolved module name for module paths.
Expose DebugSection from deeplabcut.core.debug by adding it to the module imports and __all__. Expand test_debug_logger.py with extensive tests for building and formatting debug reports, version and executable summaries, and log handling (using monkeypatched helpers and InMemoryDebugRecorder) to validate report contents and edge cases.
|
Thanks, this is going to make life easier! Will review next week. |
deruyter92
left a comment
There was a problem hiding this comment.
Just minor suggestions. Very helpful feature and nicely implemented!
| # https://github.com/DeepLabCut/DeepLabCut | ||
| # | ||
| # Please see AUTHORS for contributors. | ||
| # https://github.com/DeepLabCut/DeepLabCut/blob/master/AUTHORS |
There was a problem hiding this comment.
| # https://github.com/DeepLabCut/DeepLabCut/blob/master/AUTHORS | |
| # https://github.com/DeepLabCut/DeepLabCut/blob/main/AUTHORS |
There was a problem hiding this comment.
Nice catch, should we replace all in a separate PR ?
Make InMemoryDebugRecorder.dropped_count thread-safe by acquiring the internal lock before returning the dropped counter to avoid race conditions (core/debug/debug_logger.py). In the main window, set ensure_logger_level to logging.INFO instead of the string "auto" to ensure the logger level is explicitly configured (gui/window.py). Minor GUI cleanup: update AUTHORS URL to the main branch, remove an unused TYPE_CHECKING stub and a redundant commented refresh call in the debug dialog (gui/dialogs/debug_dialog.py). These changes improve stability and tidy up UI code/comments.
Motivation
To standardize error reports and make it easier for users to share diagnostics, this PR adds a utility that generates a copy-pasteable summary of the user environment.
This is added here with three components:
Scope
The initial version added here provides:
Debug utils
loggingGUI
Tests
Note
No project or config info is included as of yet. This could be a very worthwhile extension in subsquent PRs.
Automated summary
This pull request introduces a new debug logging and reporting system for DeepLabCut, including both core functionality and GUI integration. The changes provide a robust mechanism to capture, view, and report diagnostic information, making it easier for users and developers to troubleshoot issues. The update also adds comprehensive tests for the debug recorder and integrates a new "Generate debug log" action into the application's Help menu.
Core debug logging and reporting system:
deeplabcut.core.debugpackage that exposes debug logging utilities and report generation functions, includingInMemoryDebugRecorder,build_debug_report, and related helpers via its__init__.py._debug_utils.pywith helper functions to resolve executable paths and versions, supporting richer debug reports.GUI integration:
debug_dialog.py, a new GUI dialog for displaying and copying debug logs and reports, with utilities to create actions and dialogs for generating debug logs.gui/dialogs/__init__.pyto export the new debug dialog utilities.window.py), initializing it on startup and adding a "Generate debug log" action to the Help menu for easy access. [1] [2] [3] [4]Testing:
test_logger.py) covering recorder installation, message capture, bounded queue behavior, text rendering, clearing, and log timing utilities.