Source code for apptools.logger.plugin.view.logger_view

# (C) Copyright 2005-2024 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

# Standard library imports
from datetime import datetime
import logging

# Enthought library imports.
from pyface.api import ImageResource, clipboard
from pyface.workbench.api import TraitsUIView
from traits.api import (
    Button,
    Instance,
    List,
    Property,
    Str,
    cached_property,
    observe,
)
from traitsui.api import View, Group, Item, CodeEditor, TabularEditor, spring
from traitsui.tabular_adapter import TabularAdapter

# Local imports
from apptools.logger.agent.quality_agent_view import QualityAgentView
from apptools.logger.plugin.logger_service import LoggerService

# Constants
_IMAGE_MAP = {
    logging.DEBUG: ImageResource("debug"),
    logging.INFO: ImageResource("info"),
    logging.WARNING: ImageResource("warning"),
    logging.ERROR: ImageResource("error"),
    logging.CRITICAL: ImageResource("crit_error"),
}


[docs]class LogRecordAdapter(TabularAdapter): """A TabularEditor adapter for logging.LogRecord objects.""" columns = [ ("Level", "level"), ("Date", "date"), ("Time", "time"), ("Message", "message"), ] column_widths = [80, 100, 120, -1] level_image = Property level_text = Property(Str) date_text = Property(Str) time_text = Property(Str) message_text = Property(Str)
[docs] def get_width(self, object, trait, column): return self.column_widths[column]
def _get_level_image(self): return _IMAGE_MAP[self.item.levelno] def _get_level_text(self): return self.item.levelname.capitalize() def _get_date_text(self): dt = datetime.fromtimestamp(self.item.created) return dt.date().isoformat() def _get_time_text(self): dt = datetime.fromtimestamp(self.item.created) return dt.time().isoformat() def _get_message_text(self): # Just display the first line of multiline messages, like stacktraces. msg = self.item.getMessage() msgs = msg.strip().split("\n") if len(msgs) > 1: suffix = "... [double click for details]" else: suffix = "" abbrev_msg = msgs[0] + suffix return abbrev_msg
[docs]class LoggerView(TraitsUIView): """The Workbench View showing the list of log items.""" id = Str("apptools.logger.plugin.view.logger_view.LoggerView") name = Str("Logger") service = Instance(LoggerService) log_records = List(Instance(logging.LogRecord)) formatted_records = Property(Str, observe="log_records") activated = Instance(logging.LogRecord) activated_text = Property(Str, observe="activated") reset_button = Button("Reset Logs") show_button = Button("Complete Text Log") copy_button = Button("Copy Log to Clipboard") code_editor = CodeEditor(lexer="null", show_line_numbers=False) log_records_editor = TabularEditor( adapter=LogRecordAdapter(), editable=False, activated="activated" ) trait_view = View( Group( Item("log_records", editor=log_records_editor), Group( Item("reset_button"), spring, Item("show_button"), Item("copy_button"), orientation="horizontal", show_labels=False, ), show_labels=False, ) ) ########################################################################### # LogQueueHandler view interface ###########################################################################
[docs] def update(self, force=False): """Update 'log_records' if our handler has new records or 'force' is set. """ service = self.service if service.handler.has_new_records() or force: log_records = [ rec for rec in service.handler.get() if rec.levelno >= service.preferences.level_ ] log_records.reverse() self.log_records = log_records
########################################################################### # Private interface ########################################################################### @observe("service.preferences.level_") def _update_log_records(self, event): self.service.handler._view = self self.update(force=True) def _reset_button_fired(self): self.service.handler.reset() self.log_records = [] def _show_button_fired(self): self.edit_traits( view=View( Item( "formatted_records", editor=self.code_editor, style="readonly", show_label=False, ), width=800, height=600, resizable=True, buttons=["OK"], title="Complete Text Log", ) ) def _copy_button_fired(self): clipboard.text_data = self.formatted_records @cached_property def _get_formatted_records(self): return "\n".join( [ self.service.handler.formatter.format(record) for record in self.log_records ] ) def _activated_changed(self): if self.activated is None: return msg = self.activated.getMessage() if self.service.preferences.enable_agent: dialog = QualityAgentView(msg=msg, service=self.service) dialog.open() else: self.edit_traits( view=View( Item( "activated_text", editor=self.code_editor, style="readonly", show_label=False, ), width=800, height=600, resizable=True, buttons=["OK"], title="Log Message Detail", ) ) @cached_property def _get_activated_text(self): if self.activated is None: return "" else: return self.activated.getMessage()