我正在尝试按需向我的 Swing 应用程序的用户显示日志内容。显示日志事件列表,可能允许他们查看有关日志消息的详细信息,理想情况下类似于 eclipse“错误日志”视图显示的内容。
是否有任何预制的日志显示组件/日志附加器?
我意识到我可以轻松地将字符串值附加到 Textarea,但如果可能的话,我想要更多“免费”的花哨(搜索、按事件过滤、按记录器过滤、导出等)
我目前正在使用带有 logback 的 SLF4J,但如果存在这样的组件,我在切换到另一个日志框架时没有任何问题。
我对此进行了抨击。虽然这对我来说是一个教学练习,并且非常接近完全可用,但仍然缺少一些必需品。也许有人可以帮助进行最后的润色。
这是一个更大的工作应用程序的一部分,所以虽然我只发布与这个问题相关的代码,但有一些应用程序的工件。代码在 Scala 中,但降级到 Java 应该很简单。我不包括大多数import
指令;希望很清楚我指的是什么类。我相信只有两个具有冲突简单类名的类是
scala.swing.Component
和java.awt.Component。
该代码存在于以下文件中:
Main.scala
具有创建日志窗口的代码。LogFrame
是显示日志条目的摆动窗口LogModel
存储记录数据AbstractBaseTableModel
是LogModel
我的应用程序中另一个类的超类,此处未显示。TableAppender.scala
是将 Swing 连接到 Logback 的原因。logback.xml
有一行激活我的新附加程序在Main.scala
显示日志输出的框架中可见:
object Main extends swing.SwingApplication {
override def startup(args: Array[String]) {
val logFrame = LogFrame
if (logFrame.size == new Dimension(0,0)) logFrame.pack()
logFrame.visible = true
}
}
单LogFrame
例对象定义在LogFrame.scala
:
object LogFrame extends Frame {
title = "Log"
iconImage = new ImageIcon("log.png").getImage
preferredSize = new Dimension(1200,370)
object LogTable extends Table {
model = LogModel
Map(0 -> 50, 1 -> 32, 4 -> 400) foreach { m =>
peer.getColumnModel getColumn m._1 setPreferredWidth m._2
}
override def rendererComponent(
isSelected: Boolean, focused: Boolean, row: Int, column: Int
) = {
val v = model.getValueAt(
peer.convertRowIndexToModel(row),
peer.convertColumnIndexToModel(column)
).toString
TableCellRenderer.componentFor(this, isSelected, focused, v, row, column)
}
import ch.qos.logback.classic.Level.{ERROR,WARN}
object TableCellRenderer extends AbstractRenderer[String, TextArea](new TextArea {
lineWrap = true; wordWrap = true
}) {
val brown = new java.awt.Color(143,112,0)
def configure(t: Table, sel: Boolean, foc: Boolean, s: String, row: Int, col: Int) = {
component.text = s
model.getValueAt(
LogTable.this.peer.convertRowIndexToModel(row),
LogModel.columnNames.indexOf("Level")
) match {
case ERROR => component.foreground = java.awt.Color.RED
case WARN => component.foreground = brown
case _ =>
}
}
}
}
contents = new BoxPanel(Vertical) {
contents += new ScrollPane { viewportView = LogTable }
}
def logEvent(event: ILoggingEvent) {
event +=: LogModel
}
}
单LogModel
例在LogModel.scala
.
object LogModel extends AbstractBaseTableModel {
final val columnNames = Array("Time","Level","Thread","Logger","Message")
val data = ListBuffer[Array[AnyRef]]()
def +=:(event: ILoggingEvent) {
Array[AnyRef](
formatTimeStamp(event.getTimeStamp),
event.getLevel,
event.getThreadName,
event.getLoggerName.replaceFirst(".*\\.",""),
event.getFormattedMessage
) +=: data
fireTableChanged( new TableModelEvent(this) )
}
}
我的包对象中的这两行定义了
formatTimeStamp()
方法:
final val isoFormatter = org.joda.time.format.ISODateTimeFormat.dateTimeNoMillis
def formatTimeStamp(millis: Long) = isoFormatter.print(millis)
LogModel
扩展的原因AbstractBaseTableModel
是因为我的应用程序有一些其他的表模型是一次性更新的,而不是像日志模型那样一次更新一行。因此
AbstractBaseTableModel
有一个data
type 的成员SeqLike
,并且子类可以根据需要使用不可变List
或可变
ListBuffer
的。
import scala.collection.SeqLike
abstract class AbstractBaseTableModel extends AbstractTableModel {
val columnNames: Array[String]
val data: SeqLike[Array[AnyRef],_]
def getRowCount: Int = data.size
def getColumnCount: Int = columnNames.size
override def getColumnName(column: Int) = columnNames(column)
override def getValueAt(row: Int, column: Int): AnyRef = data(row)(column)
override def isCellEditable(row: Int, col: Int) = false
override def getColumnClass(columnIndex: Int): Class[_] =
getValueAt(0, columnIndex).getClass
}
TableAppender.scala
文件很小:
class TableAppender extends AppenderBase[ILoggingEvent] {
def append(event: ILoggingEvent) {
LogFrame.logEvent(event)
}
}
最后,对我的logback.xml
文件的更改甚至更小:
<appender name="swingTable" class="mypackage.TableAppender"/>
唯一让我无法决定这比老式日志文件更好的问题是:
1)因为我已经覆盖Table.rendererComponent()
,
JTable.setDefaultRenderer()
不能像往常一样工作。
2)我还没有弄清楚如何让包裹线在表格中可见。我所知道的是我必须增加行的高度。
如果这些问题得到解决,接下来就是根据日志级别和记录器名称添加实时过滤。事实上,我至少暂时将其搁置一旁,但如果有人有任何建议,我很想听听他们的意见。
以下资源对我编写上述代码很有用,或者可能对解决剩余的问题有用:
Scala table cell renderers的惯用用法, oxbow_lakes的一个非常有帮助的 SO 答案
,但不幸的是没有解决这个JTable.setDefaultRenderer()
问题。
Rob Camick的博客文章Table Row Rendering解释了如何更改表格行的颜色。
如何在 jtable 单元格中换行?,来自Arvodan的另一个 SO 问题 。
我会看看SwingX。在这个页面上有一个很好的演示。以 JXTable 为例。