作为一个程序设计人员,我们深知日志的重要性,对于日志的监控,我们通常不外乎采用以下两种方式:日志文件方式和后台打印方式,常规情况下,这两种日志监控方式完全可以满足我们对日志监控的需要。但是,当我们用Swing进行前台开发时,常常想能不能把后台服务运行日志实时地显示在前台窗口中,或者只是将某类我们比较关心的日志信息(譬如异常日志等)实时动态地显示在前台窗口中,这样方便我们及时监控和处理。这个设想我们称之为“后台日志信息前台监控器”。

设计这样一个“后台日志信息前台监控器”,有两个难点,第一个是,当我们捕捉到后台日志信息时,如何将日志信息传递给前台监控器,实现实时传递。第二个是,当前台监控器收到日志信息后,如何实时显示。总结起来,就是一个“实时”的问题:实时传递和显示日志信息。

对于如何将后台日志信息实时传递到前台进行监控显示,我们采用了自定义事件机制,通过事件触发机制,监控日志内容的变更。思路是这样的,在LogMonitor类中,用StringBuilder实例化一个变量logs,用来存储日志信息,通过addLog和clearLogs方法来添加和删除日志信息,并在这两个方法体内监控日志信息的变更,当日志信息发生变更时,触发事件变更事件,最后在此事件内实时刷新前台监控区域。

  • 日志信息变更事件类
/**
* Description:日志信息事件<br>
* Copyright: Copyright (c) 2015<br>
* Company: 河南电力科学研究院智能电网所<br>
*
* @author shangbingbing 2015-01-01编写
* @version 1.0
*/
public class LogChangedEvent extends java.util.EventObject {
private static final long serialVersionUID = 7573194493258326711L;
public LogChangedEvent(Object source) {
super(source);
}
}
  • 日志信息变更监听器类
/**
* Description:日志信息变更监听器<br>
* Copyright: Copyright (c) 2015<br>
* Company: 河南电力科学研究院智能电网所<br>
*
* @author shangbingbing 2015-01-01编写
* @version 1.0
*/
public class LogChangedListener implements java.util.EventListener {
public void EventActivated(LogChangedEvent me) { }
}
  • 日志信息监听器类
/**
* Description:日志信息监听器类<br>
* Copyright: Copyright (c) 2015<br>
* Company: 河南电力科学研究院智能电网所<br>
* @author shangbingbing 2015-01-01编写
* @version 1.0
*/
public class LogMonitor implements Serializable {
private static final long serialVersionUID = 1L;
private static StringBuilder logs = new StringBuilder();
/**
* 获取日志信息
* @return
*/
public static StringBuilder getLogs() {
return logs;
}
/**
* 新增日志信息
* @param log
*/
public static void addLog(String log) {
if(StringUtils.isBlank(log)) {
logs.append("\r\n");
} else {
log = String.format("%s %s\r\n", new Date().toString(), log);
logs.append(log);
}
activateLogChangedEvent();
}
/**
* 清除日志信息
*/
public static void clearLogs() {
logs = new StringBuilder();
activateLogChangedEvent();
}
private static Vector<LogChangedListener> vectorListeners = new Vector<LogChangedListener>();
public static synchronized void addLogChangedListener(LogChangedListener listener) {
vectorListeners.addElement(listener);
}
public static synchronized void removeLogChangedListener(LogChangedListener listener) {
vectorListeners.removeElement(listener);
}
public static void activateLogChangedEvent() {
Vector<LogChangedListener> tempVector = null;
LogChangedEvent e = new LogChangedEvent(LogMonitor.class);
synchronized(LogMonitor.class) {
tempVector = (Vector<LogChangedListener>)vectorListeners.clone();
for(int i=0;i<tempVector.size();i++) {
LogChangedListener listener = tempVector.elementAt(i);
listener.EventActivated(e);
}
}
}
}
  • 日志信息前台监控窗口
import javax.swing.JDialog;
import javax.swing.UIManager;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/**
* Description:日志信息监听窗口<br>
* Copyright: Copyright (c) 2015<br>
* Company: 河南电力科学研究院智能电网所<br>
* @author shangbingbing 2015-01-01编写
* @version 1.0
*/
public class DialogLogMonitor extends JDialog {
private static final long serialVersionUID = 1L;
private JTextArea txtLogInfo;
public static void main(String[] args) {
try {
//设置系统观感器
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
DialogLogMonitor dialog = new DialogLogMonitor();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 日志信息变更监听处理(关键点)
*/
private void init() {
LogMonitor.addLogChangedListener(new LogChangedListener() {
@Override
public void EventActivated(LogChangedEvent me) {
txtLogInfo.setText(LogMonitor.getLogs().toString());
txtLogInfo.setCaretPosition(txtLogInfo.getText().length());
txtLogInfo.paintImmediately(txtLogInfo.getBounds());
}
});
}
public DialogLogMonitor() {
setResizable(false);
setTitle("\u540E\u53F0\u65E5\u5FD7\u76D1\u63A7\u5668");
setBounds(100, 100, 439, 274);
JScrollPane scrollPane = new JScrollPane();
GroupLayout groupLayout = new GroupLayout(getContentPane());
groupLayout.setHorizontalGroup(
groupLayout.createParallelGroup(Alignment.LEADING)
.addGroup(groupLayout.createSequentialGroup()
.addContainerGap()
.addComponent(scrollPane)
.addContainerGap())
);
groupLayout.setVerticalGroup(
groupLayout.createParallelGroup(Alignment.LEADING)
.addGroup(groupLayout.createSequentialGroup()
.addContainerGap()
.addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 230, Short.MAX_VALUE)
.addContainerGap())
);
txtLogInfo = new JTextArea();
txtLogInfo.setEditable(false);
txtLogInfo.setLineWrap(true);
scrollPane.setViewportView(txtLogInfo);
getContentPane().setLayout(groupLayout);
this.init();
}
}
  • 后台日志模拟生成窗口

我们设计了一个后台日志模拟生成窗口,来测试日志信息的监控效果。窗口源代码如下:

import javax.swing.JDialog;
import javax.swing.UIManager;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JLabel;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
/**
* Description:后台日志信息模拟生成窗口<br>
* Copyright: Copyright (c) 2015<br>
* Company: 河南电力科学研究院智能电网所<br>
* @author shangbingbing 2015-01-01编写
* @version 1.0
*/
public class DialogLogGenerator extends JDialog {
private static final long serialVersionUID = 1L;
private JTextArea txtLogInfo;
public static void main(String[] args) {
try {
//设置系统观感器
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
DialogLogGenerator dialog = new DialogLogGenerator();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true); DialogLogMonitor dialogLogMonitor = new DialogLogMonitor();
dialogLogMonitor.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialogLogMonitor.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
private void generatorLog() {
LogMonitor.addLog(this.txtLogInfo.getText());
this.txtLogInfo.setText("");
}
public DialogLogGenerator() {
setResizable(false);
setTitle("\u540E\u53F0\u65E5\u5FD7\u6A21\u62DF\u751F\u6210\u6D4B\u8BD5\u7A97\u53E3");
setBounds(100, 100, 439, 278);
JLabel lblNewLabel = new JLabel("\u8BF7\u8F93\u5165\u6A21\u62DF\u65E5\u5FD7\u4FE1\u606F\uFF1A");
JScrollPane scrollPane = new JScrollPane();
JButton btnCreateLog = new JButton("\u4F20\u9012\u6A21\u62DF\u65E5\u5FD7");
btnCreateLog.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
generatorLog();
}
});
GroupLayout groupLayout = new GroupLayout(getContentPane());
groupLayout.setHorizontalGroup(
groupLayout.createParallelGroup(Alignment.LEADING)
.addGroup(groupLayout.createSequentialGroup()
.addContainerGap()
.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
.addGroup(groupLayout.createSequentialGroup()
.addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 413, Short.MAX_VALUE)
.addContainerGap())
.addGroup(groupLayout.createSequentialGroup()
.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
.addComponent(btnCreateLog, GroupLayout.PREFERRED_SIZE, 131, GroupLayout.PREFERRED_SIZE)
.addComponent(lblNewLabel, GroupLayout.PREFERRED_SIZE, 170, GroupLayout.PREFERRED_SIZE))
.addGap(253))))
);
groupLayout.setVerticalGroup(
groupLayout.createParallelGroup(Alignment.LEADING)
.addGroup(groupLayout.createSequentialGroup()
.addGap(18)
.addComponent(lblNewLabel)
.addPreferredGap(ComponentPlacement.UNRELATED)
.addComponent(scrollPane, GroupLayout.PREFERRED_SIZE, 131, GroupLayout.PREFERRED_SIZE)
.addGap(18)
.addComponent(btnCreateLog, GroupLayout.PREFERRED_SIZE, 41, GroupLayout.PREFERRED_SIZE)
.addContainerGap(17, Short.MAX_VALUE))
);
txtLogInfo = new JTextArea();
scrollPane.setViewportView(txtLogInfo);
getContentPane().setLayout(groupLayout);
}
}
  • 运行测试

启动运行DialogLogGenerator窗口,会同时显示DialogLogMonitor窗口,在日志模拟窗口输入一些日志信息,然后点击“传递模拟日志”按钮,你将可以在DialogLogMonitor监控区域看到模拟的日志信息。运行效果图如下所示:

运行程序打包下载 hnepri-log-monitor.jar


作者:商兵兵

单位:河南省电力科学研究院智能电网所

QQ:52190634

主页:http://www.cnblogs.com/shangbingbing

空间:http://shangbingbing.qzone.qq.com

Swing应用开发实战系列之五:后台日志信息前台监控器的更多相关文章

  1. Swing应用开发实战系列之三:动态信息提示窗口

    这里所说的“动态信息提示窗口”可不同于JOptionPane中的Message窗口和Confirm窗口,它们都是静态的模态的,更重要的是线程阻塞的,迫使你必须选择某个动作才能继续执行.我们接下来要分享 ...

  2. Swing应用开发实战系列之四:组件内容实时刷新问题

    窗口组件动态刷新问题,在dotnet中根本不算什么问题,用几句代码很轻松就能搞定,但是在Swing中,实现动态刷新组件内容却是一件颇为吃力的事情.譬如针对我们经常用到的刷新JLable.JTextFi ...

  3. Swing应用开发实战系列之一:自定义JdbcTemplate

    笔者本人真正意义上接触编程开发是在2004年,最早用的就是VB,然后是Delphi等,后来转到.Net,中间断断续续还用过PowerBuilder等,无一例外,所研发设计的项目或系统都是WinForm ...

  4. Swing应用开发实战系列之二:设计日期选择面板窗口

    Swing本身没有提供什么华丽丽的日期时间选择控件,所以笔者就在网上搜了个第三方的jar包jdatepicker-1.3.2.jar,基于此设计了个很轻量的日期选择面板,很简单的.效果图如下所示: 代 ...

  5. WCF开发实战系列二:使用IIS发布WCF服务

    WCF开发实战系列二:使用IIS发布WCF服务 (原创:灰灰虫的家http://hi.baidu.com/grayworm) 上一篇中,我们创建了一个简单的WCF服务,在测试的时候,我们使用VS200 ...

  6. WCF开发实战系列一:创建第一个WCF服务

    WCF开发实战系列一:创建第一个WCF服务 (原创:灰灰虫的家http://hi.baidu.com/grayworm) 在这个实战中我们将使用DataContract,ServiceContract ...

  7. WCF开发实战系列三:自运行WCF服务

    WCF开发实战系列三:自运行WCF服务 (原创:灰灰虫的家 http://hi.baidu.com/grayworm)上一篇文章中我们建立了一个WCF服务站点,为WCF服务库运行提供WEB支持,我们把 ...

  8. WCF开发实战系列四:使用Windows服务发布WCF服务

    WCF开发实战系列四:使用Windows服务发布WCF服务 (原创:灰灰虫的家http://hi.baidu.com/grayworm) 上一篇文章中我们通过编写的控制台程序或WinForm程序来为本 ...

  9. WCF开发实战系列五:创建WCF客户端程序

    WCF开发实战系列五:创建WCF客户端程序 (原创:灰灰虫的家http://hi.baidu.com/grayworm) 在前面的三篇文章中我们分别介绍了WCF服务的三种载体:IIS.Self-Hos ...

随机推荐

  1. JWS ,JAX-WS ,JAX-RS,REST,Restlet,SOAP 相关概念

    与 WebServices 相关的 J2EE 技术称为 JWS(Java WebServices),其中含有 JAX-WS.JAX-RS.JAXB.JAXR.SAAJ.StAX 等技术 支持 SOAP ...

  2. 自学H5第二天

    笔记: 1.css之外联样式 2.css之行间样式: 3.css之内联样式 二.边框的知识: 1.边框的复合样式: 2.边框的单一样式: /*单一样式*/ border-width: 1px 2px ...

  3. php中数组遍历改值

    <?php $arr = array(100, 99, 88, 77, 55, 66); //方法1 foreach ($arr as &$v) { $v = 2; } print_r( ...

  4. No.018:4Sum

    问题: Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = ...

  5. PHP替代正则匹配的高效函数

    strpos() - 查找字符串首次出现的位置 strrpos() 函数查找字符串在另一字符串中最后一次出现的位置(区分大小写). strripos() 函数查找字符串在另一字符串中最后一次出现的位置 ...

  6. 【JAVA并发编程实战】1、对象的共享

    1.栈封闭 在栈封闭中,只能通过局部变量才能访问对象. 所谓栈封闭就是把变量的声明以及应用都局限在一个局部线程中,在这个局部线程中声明和实例化的对象对于线程外部是不可见的,这个局部线程的栈,无法被任何 ...

  7. CI框架源码阅读笔记2 一切的入口 index.php

    上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里再次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中, ...

  8. Android总结篇系列:Android广播机制

    1.Android广播机制概述 Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器).广播作为Android组件间的通 ...

  9. PHP 单例模式继承的实现方式

    /** * @author xiaojiang * 方式1 */ class father{ static $models = []; static public function getInstan ...

  10. 跨平台日志清理工具 Log-Cutter v2.0.1 RC-1 发布

    Log-Cutter 是JessMA开源组织开发的一个简单实用的日志切割清理工具.对于服务器的日常维护来说,日志清理是非常重要的事情,如果残留日志过多则严重浪费磁盘空间同时影响服务的性能.如果用手工方 ...