intellicrack.ui.log_viewer

Live log viewer package.

Provides a standalone QMainWindow (LogViewerWindow) that subscribes to the structlog stream via QtSignalingHandler and backfills history from the on-disk JSON-Lines log file.

class LogFileTailReader[source]

Bases: QObject

Watches a JSON-Lines log file and emits new records as they appear.

The reader does an initial backfill on a worker thread, then uses a QFileSystemWatcher on both the file and its parent directory so log rotation (truncate-or-rename) and slow first-write scenarios are handled. Each diff read is capped at _MAX_INCREMENTAL_BYTES bytes; if more data is available it re-schedules itself via QTimer.singleShot() to keep the GUI responsive.

Variables:
  • record_emitted – Emitted with a LogRecordDict for each parsed line, including synthetic rotation notices.

  • initial_load_complete – Emitted (with the offset where live tailing resumes) once the historical load finishes.

__init__(log_path, max_initial_bytes=5242880, parent=None)[source]

Initialize the reader for the given log file.

Parameters:
  • log_path (Path) – Path to the JSON-Lines log file (need not exist yet; the directory watcher will pick it up on creation).

  • max_initial_bytes (int) – Maximum number of trailing bytes read on first load.

  • parent (QObject | None) – Parent QObject for ownership.

Return type:

None

start()[source]

Begin the initial historical load and set up file watching.

Repeated calls are no-ops once a load is in progress. Tests can call force_poll() after start() if needed.

Return type:

None

stop()[source]

Stop watching and tear down internal resources without blocking the GUI thread.

The historical-load worker is not owned by this reader (it is created with no parent in start()), so it is safe to detach from it here even while it is still running: dropping self._initial_worker does not risk premature collection of the still-running QThread because RetainedWorker already pinned it in the shared worker registry, which is immune to Python’s cyclic garbage collector. The worker keeps running in the background, is dropped from that registry once it finishes, and deletes itself via its own finished signal once the read completes. This avoids both a synchronous GUI-thread wait and the Qt crash that results from destroying a QThread while it is still running.

Return type:

None

force_poll()[source]

Trigger an immediate incremental read.

Useful for tests where signals from QFileSystemWatcher may be delivered asynchronously.

Return type:

None

class LogFilterProxyModel[source]

Bases: QSortFilterProxyModel

Sort/filter proxy applying the viewer’s filter state to a model.

Variables:

min_level (int) – Records with a numeric level below this value are hidden.

__init__(parent=None)[source]

Initialize the proxy with permissive defaults.

Parameters:

parent (QObject | None) – Parent QObject.

Return type:

None

min_level: int
set_min_level(level)[source]

Set the minimum numeric level shown.

Parameters:

level (int) – Numeric logging level (e.g. logging.WARNING).

Return type:

None

set_logger_pattern(pattern)[source]

Set the logger-name regex filter.

Invalid regular expressions silently clear the filter so the viewer remains usable while the user is mid-typing.

Parameters:

pattern (str) – Regular-expression source string. Empty disables the filter.

Return type:

None

logger_pattern_source()[source]

Return the source string of the currently compiled logger pattern.

Returns:

The pattern’s source, or empty string when no pattern.

Return type:

str

set_text_query(query)[source]

Set the free-text search query.

Parameters:

query (str) – Substring to look for in the event identifier or the JSON form of the extras.

Return type:

None

set_case_sensitive(*, case_sensitive)[source]

Toggle whether the free-text search is case sensitive.

Parameters:

case_sensitive (bool) – True to match exactly as typed.

Return type:

None

filterAcceptsRow(source_row, source_parent)[source]

Return whether the given source row passes all active filters.

Parameters:
  • source_row (int) – Row index in the source model.

  • source_parent (QModelIndex) – Parent index for hierarchical models (unused for the flat log table).

Returns:

True when the row should be visible.

Return type:

bool

class LogRecordDetailsDialog[source]

Bases: QDialog

Modal dialog showing the full JSON of a single log record.

Variables:

record (intellicrack.ui.log_viewer._record.LogRecordDict) – The record being displayed.

__init__(record, parent=None)[source]

Initialize the dialog with the given record.

Parameters:
  • record (LogRecordDict) – Normalized log record to display.

  • parent (QWidget | None) – Parent widget.

Return type:

None

record: LogRecordDict
property text: str

The JSON text currently displayed in the dialog.

Returns:

Pretty-printed JSON for the record.

Return type:

str

class LogRecordDict[source]

Bases: TypedDict

Normalized log record shape consumed by the viewer model.

Variables:
  • timestamp (str) – Human-readable timestamp string in local time.

  • level (str) – Upper-case level name (e.g. "INFO").

  • logger (str) – Dotted logger name.

  • module (str) – Source module short name.

  • function (str) – Source function name.

  • line_number (int) – Source line number, or 0 when unknown.

  • event (str) – The structured event identifier (the structlog event key).

  • extras (dict[str, object]) – Remaining structured key/value pairs from the event dict.

timestamp: str
level: str
logger: str
module: str
function: str
line_number: int
event: str
extras: dict[str, object]
class LogRecordTableModel[source]

Bases: QAbstractTableModel

Bounded ring-buffer table model for log records.

Incoming records are buffered into _pending and flushed in a single beginInsertRows transaction on a 50 ms timer. The buffer is capped at max_rows and evicts the oldest entries via beginRemoveRows so views stay consistent under load.

Variables:

max_rows (int) – Soft cap on the number of stored rows.

__init__(max_rows=50000, parent=None)[source]

Initialize the model with the given soft row cap.

Parameters:
  • max_rows (int) – Maximum number of rows kept in the ring buffer.

  • parent (QObject | None) – Parent QObject for ownership.

Return type:

None

max_rows: int
property total_received: int

Total number of records ever appended.

Returns:

Cumulative count, including evicted records.

Return type:

int

level_foreground(level)[source]

Return the resolved foreground color for a log level.

Parameters:

level (str) – Log level name (e.g. "INFO").

Returns:

The theme-resolved foreground color, or None when

the level has no dedicated color.

Return type:

QColor | None

level_background(level)[source]

Return the resolved background color for a log level.

Parameters:

level (str) – Log level name (e.g. "CRITICAL").

Returns:

The theme-resolved background color, or None when

the level has no background override.

Return type:

QColor | None

append_record(record)[source]

Queue a record for the next coalesced insert.

Parameters:

record (dict[str, object]) – Normalized log record (treated as LogRecordDict).

Return type:

None

flush()[source]

Force-drain pending records into the model immediately.

Useful in tests and at shutdown to avoid losing buffered rows.

Return type:

None

clear()[source]

Remove all records and pending buffers from the model.

Return type:

None

set_max_rows(max_rows)[source]

Update the row cap, evicting oldest rows if necessary.

Parameters:

max_rows (int) – New row cap; clamped to [1_000, 500_000].

Return type:

None

record_at(row)[source]

Return the record at the given row, if any.

Parameters:

row (int) – Row index into the underlying ring buffer.

Returns:

The record, or None when out of

range.

Return type:

LogRecordDict | None

all_records()[source]

Iterate over all stored records.

Returns:

Iterator over the ring buffer.

Return type:

Iterable[LogRecordDict]

rowCount(parent=None)[source]

Return the number of rows currently stored.

Parameters:

parent (QModelIndex | None) – Parent index; non-default values yield zero per table-model conventions.

Returns:

Row count.

Return type:

int

columnCount(parent=None)[source]

Return the number of columns exposed by the model.

Parameters:

parent (QModelIndex | None) – Parent index; non-default values yield zero per table-model conventions.

Returns:

Fixed column count.

Return type:

int

headerData(section, orientation, role=ItemDataRole.DisplayRole)[source]

Return header text for the given section and orientation.

Parameters:
  • section (int) – Column or row index.

  • orientation (Orientation) – Horizontal for column headers.

  • role (int) – Display role; only DisplayRole returns header text.

Returns:

Header text for display roles, otherwise None.

Return type:

object

data(index, role=ItemDataRole.DisplayRole)[source]

Return cell data for the given index and role.

Parameters:
  • index (QModelIndex) – Cell index.

  • role (int) – Item data role.

Returns:

Cell value for display/edit roles, the raw record

for UserRole, or None otherwise.

Return type:

object

class LogViewerWindow[source]

Bases: QMainWindow

Modeless log viewer window.

Subscribes to the global QtSignalingHandler and tails the on-disk JSON-Lines log to backfill history. Owned by the main window; geometry and filter state persist via QSettings.

Variables:

log_path (Path) – Path to the JSON-Lines log file currently displayed.

__init__(config, parent=None)[source]

Initialize the viewer for the given application config.

Parameters:
  • config (Config) – Application configuration providing the logs path.

  • parent (QWidget | None) – Optional parent window (typically the MainWindow).

Return type:

None

log_path: Path
property model: LogRecordTableModel

The underlying log-record table model.

Returns:

The source model backing the table.

Return type:

LogRecordTableModel

property proxy: LogFilterProxyModel

The filter proxy connected between model and view.

Returns:

The active proxy model.

Return type:

LogFilterProxyModel

property pause_action: QAction | None

The toolbar Pause action, available once the toolbar is built.

Returns:

The Pause QAction, or None if

the UI is not yet constructed.

Return type:

QAction | None

is_paused()[source]

Return whether the live capture is currently paused.

Returns:

True when the handler is suppressing emission.

Return type:

bool

set_min_level(level)[source]

Programmatically set the proxy’s minimum level filter.

Parameters:

level (int) – Numeric logging level (e.g. logging.WARNING).

Return type:

None

clear()[source]

Clear all records from the model.

Public entry point exposed for callers that don’t have access to the toolbar’s Clear action.

Return type:

None

closeEvent(a0)[source]

Persist settings and detach the tail reader when the window closes.

Parameters:

a0 (QCloseEvent | None) – Close event from Qt.

Return type:

None

class QtSignalingHandler[source]

Bases: Handler

Logging handler that forwards records into a Qt signal.

The handler converts each accepted logging.LogRecord into a LogRecordDict on the calling thread, then emits record_received so connected slots can run on the GUI thread via a queued connection. The handler is safe to install on the root logger alongside the existing console and file handlers.

Variables:
  • bridge (intellicrack.ui.log_viewer._handler._HandlerBridge) – Internal QObject exposing record_received.

  • paused (bool) – When True, records are accepted but no signal is emitted.

__init__()[source]

Initialize the handler at level NOTSET with the structlog formatter pre-attached.

Return type:

None

bridge: _HandlerBridge
paused: bool
property record_received: pyqtBoundSignal

Expose the bridge’s record-received signal.

Returns:

Bound signal emitted with a

LogRecordDict.

Return type:

pyqtBoundSignal

set_paused(*, paused)[source]

Toggle whether the handler emits to subscribers.

Parameters:

paused (bool) – True suppresses emission; False resumes it.

Return type:

None

emit(record)[source]

Convert record to a dict and emit it on the bridge.

Re-entrant emissions on the same thread are dropped to avoid infinite recursion when a connected slot logs. Emissions are skipped silently after the underlying QObject bridge has been destroyed (typical during application teardown) so the logging pipeline does not surface RuntimeError cascades while Qt cleanup is in progress.

Parameters:

record (LogRecord) – The standard logging record to forward.

Return type:

None

get_qt_log_handler()[source]

Return the currently installed Qt log handler, if any.

Returns:

The shared handler instance, or

None when not installed.

Return type:

QtSignalingHandler | None

install_qt_log_handler()[source]

Install (or return the existing) Qt-signaling handler on the root logger.

The function is idempotent: repeated calls return the same handler instance and do not attach duplicate handlers. The handler is attached to the root logger so it observes records from intellicrack (which propagates) and any third-party libraries.

Returns:

The shared handler instance.

Return type:

QtSignalingHandler

level_name_to_int(name)[source]

Return the numeric logging level for a name, defaulting to INFO.

Parameters:

name (str) – Upper- or lower-case level name (e.g. "WARNING").

Returns:

Numeric level, or logging.INFO when unknown.

Return type:

int

parse_json_line(line)[source]

Parse a single JSON-Lines log entry into a LogRecordDict.

Lines that are blank, not valid JSON, or not JSON objects are skipped by returning None. Missing fields fall back to safe defaults so the viewer never crashes on partially-written or non-structlog lines.

Parameters:

line (str) – A single text line from the log file.

Returns:

The parsed record, or None when the

line cannot be parsed as a structured record.

Return type:

LogRecordDict | None

record_to_json_text(record)[source]

Render a record as a pretty-printed JSON string for the details dialog.

Parameters:

record (LogRecordDict) – Normalized log record.

Returns:

Indented JSON text safe to display in a monospace viewer.

Return type:

str

uninstall_qt_log_handler()[source]

Detach and forget the shared handler if one is installed.

Used by tests to keep state clean between cases. Safe to call when no handler has been installed.

Return type:

None

Submodules

window

Modeless QMainWindow exposing the live log stream.