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()