gui/mozregui/log_report.py (92 lines of code) (raw):
from datetime import datetime
from mozlog import get_default_logger
from mozlog.structuredlog import log_levels
from PySide6.QtCore import QObject, Signal, Slot
from PySide6.QtGui import (
QAction,
QActionGroup,
QColor,
QTextBlockUserData,
QTextCharFormat,
QTextCursor,
)
from PySide6.QtWidgets import QMenu, QPlainTextEdit
COLORS = {
"DEBUG": QColor(6, 146, 6), # green
"INFO": QColor(250, 184, 4), # deep yellow
"WARNING": QColor(255, 0, 0, 127), # red
"CRITICAL": QColor(255, 0, 0, 127),
"ERROR": QColor(255, 0, 0, 127),
}
class LogLevelData(QTextBlockUserData):
def __init__(self, log_lvl):
QTextBlockUserData.__init__(self)
self.log_lvl = log_lvl
class LogView(QPlainTextEdit):
def __init__(self, parent=None):
QPlainTextEdit.__init__(self, parent)
self.setMaximumBlockCount(1000)
self.group = QActionGroup(self)
self.actions = [QAction(log_lvl, self.group) for log_lvl in ["Debug", "Info"]]
for action in self.actions:
action.setCheckable(True)
action.triggered.connect(self.on_log_filter)
self.actions[1].setChecked(True)
self.customContextMenuRequested.connect(self.on_custom_context_menu_requested)
self.log_lvl = log_levels["INFO"]
def text_blocks(self):
current_block = QTextCursor(self.document()).block()
while current_block.isValid and current_block.text():
yield current_block
current_block = current_block.next()
@Slot(dict)
def on_log_received(self, data):
time_info = datetime.fromtimestamp((data["time"] / 1000)).isoformat()
log_message = "%s: %s : %s" % (time_info, data["level"], data["message"])
message_document = self.document()
cursor_to_add = QTextCursor(message_document)
cursor_to_add.movePosition(QTextCursor.End)
cursor_to_add.insertText(log_message + "\n")
if data["level"] in COLORS:
fmt = QTextCharFormat()
fmt.setForeground(COLORS[data["level"]])
cursor_to_add.movePosition(QTextCursor.PreviousBlock)
log_lvl_data = LogLevelData(log_levels[data["level"].upper()])
cursor_to_add.block().setUserData(log_lvl_data)
cursor_to_add_fmt = message_document.find(data["level"], cursor_to_add.position())
cursor_to_add_fmt.mergeCharFormat(fmt)
if log_levels[data["level"]] > self.log_lvl:
cursor_to_add.block().setVisible(False)
self.ensureCursorVisible()
@Slot()
def on_custom_context_menu_requested(self):
menu = QMenu(self)
for action in self.actions:
menu.addAction(action)
menu.popup(self.cursor().pos())
@Slot()
def on_log_filter(self):
log_lvl_name = str(self.sender().iconText()).upper()
self.log_lvl = log_levels[log_lvl_name]
it = self.document().begin()
while it != self.document().end():
userdata = it.userData()
if userdata:
block_log_lvl = userdata.log_lvl
it.setVisible(block_log_lvl <= self.log_lvl)
it = it.next()
self.viewport().update()
class LogModel(QObject):
log = Signal(dict)
def __call__(self, data):
self.log.emit(data)
def log(text, log=True, status_bar=True, status_bar_timeout=2.0):
if log:
logger = get_default_logger("mozregui")
if logger:
logger.info(text)
if status_bar:
from mozregui.mainwindow import MainWindow
mw = MainWindow.INSTANCE
if mw:
mw.ui.status_bar.showMessage(text, int(status_bar_timeout * 1000))