Java日志通常可以分为:error、warn、info、debug、trace五个级别。在J2SE中预定义的级别更多,分别为:SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST。两者的对应大致如下:
Log4j、slf4j | J2se | 使用场景 |
error | SEVERE | 问题已经影响到软件的正常运行,并且软件不能自行恢复到正常的运行状态,此时需要输出该级别的错误日志。 |
warn | WARNING | 与业务处理相关的失败,此次失败不影响下次业务的执行,通常的结果为外部的输入不能获得期望的结果。 |
info | INFO | 系统运行期间的系统运行状态变化,或关键业务处理记录等用户或管理员在系统运行期间关注的一些信息。 |
CONFIG | 系统配置、系统运行环境信息,有助于安装实施人员检查配置是否正确。 | |
debug | FINE | 软件调试信息,开发人员使用该级别的日志发现程序运行中的一些问题,排除故障。 |
FINER | 基本同上,但显示的信息更详尽。 | |
trace | FINEST | 基本同上,但显示的信息更详尽。 |
SEVERE (最高级别)FINEST (最低级别)
接下来以一个程序来说明:
package debug;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.*;
/**
* Created by Administrator on 2016/10/27.
*/
public class LoggingImageViewer {
public static void main(String[]args){
//默认的日志配置将级别等于或高于INFO级别的所有信息记录到控制台,用户可以覆盖默认的配置文件
//如果用户要使用自定义的配置文件,则在启动时,使用以下命令:
//java -Djava.util.logging.config.file=configFile MainClass
//java.util.logging.config.file的值为日志配置文件的存储位置, 及configFile是一个路径字符串 MainClass为启动的类名
//注意 在main方法中调用System.setProperty("java.util.logging.config.file",file)没有任何影响,因为日志管理器在VM启动过程中被初始化,它会在main之前执行
//除以上方式外,还可以利用java.util.logging.config,class系统属性设置为某个类名,该类再通过其他方式设定日志管理器属性
if(System.getProperty("java.util.logging.config.class")==null&&
System.getProperty("java.util.logging.config.file")==null){//当以上两种方式都没有设置的情况下,在代码中手动设置
try{
Logger.getLogger("com.horstmann.corejava").setLevel(Level.ALL);//创建或检索日志记录器, 这里是创建一个Logger实例并设置级别(ALL开启所有级别的记录),日志记录器名采用层次结构,一般都把日志记录器命名为与主应用程序包一样的名字
final int LOG_ROTATION_COUN=10;
Handler handler=new FileHandler("%h/LoggingImageViewer.log",0,LOG_ROTATION_COUN);//日志处理器,默认情况下,日志记录器将记录发送到ConsoleHandler中,
// 并由它输出到System.err流中,要想将日志发送到其他地方,就要添加其他的处理器,FileHandler是将记录发送到指定文件的处理器将消息发送至单个一般文件或一个可回滚的文件集合。可回滚文件集中的文件依据文件大小进行回滚,久文件名称通过当前文件名附加编号0、1、2等方式依次进行标示
//默认发送到用户主目录的javan.log文件中,nn为唯一编号, 这里设置为路径为:%h/LoggingImageViewer.log这个文件,%h为系统属性user.home的值,可通过System.out.println(System.getProperty("user.home") );查询
//此处的声明为FileHandler(String pattern, int limit, int count) limit:在打开另一个文件之前允许写入一个文件的近似最大字节数(0表示无限制)也就是说已向某个文件写入给定限制的数据量(近似)后,则打开另一个文件
//count:在循环序列中的日志记录数量,1为不循环,循环序列是指,日志文件以LoggingImageViewer.log.0,LoggingImageViewer.log.1,LoggingImageViewer.log.2,这种循环序列的形式出现,只要文件超出了大小限制,最旧的文件就会被删除,其他的文件将重新命名,同时创建一个新文件,其编号为0
Logger.getLogger("com.horstmann.corejava").addHandler(handler);
}catch (IOException e){
Logger.getLogger("com.horstmann.corejava").log(Level.SEVERE,"Can't create log file hanndler",e);
}
}
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
Handler windowHandler=new WindowHandler();//自定义日志处理器,日志记录器可有多个处理器,如本例中FileHandler和自定义的处理器
windowHandler.setLevel(Level.ALL);
Logger.getLogger("com.horstmann.corejava").addHandler(windowHandler);
JFrame frame=new ImageViewerFrame();
frame.setTitle("LoggingImageViewer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Logger.getLogger("com.horstmann.corejava").fine("Showing frame");
frame.setVisible(true);
}
});
}
}
class ImageViewerFrame extends JFrame{
private static final int DEFAULT_WIDTH=300;
private static final int DEFAULT_HEIGHT=400;
private JLabel label;
private static Logger logger=Logger.getLogger("com.horstmann.corejava");//获得该Logger实例,设置为静态域添加到类中
public ImageViewerFrame(){
//entering、exiting 用来跟踪执行流,为FINER级别,生成以字符串"ENTRY"和"RETURN"开始的日志记录
logger.entering("ImageViewerFrame", "<init>");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
JMenuBar menuBar=new JMenuBar();
setJMenuBar(menuBar);
JMenu menu=new JMenu("File");
menuBar.add(menu);
JMenuItem openItem=new JMenuItem("Open");
menu.add(openItem);
openItem.addActionListener(new FileOpenListener());
JMenuItem exitItem=new JMenuItem("Exit");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
logger.fine("Exiting.");
System.exit(0);
}
});
label=new JLabel();
add(label);
logger.exiting("ImageViewerFrame","<init>");
}
private class FileOpenListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
logger.entering("ImageViewFrame.FileOpenListener", "actionPerformed", e);
JFileChooser chooser=new JFileChooser();
chooser.setCurrentDirectory(new File("."));
chooser.setFileFilter(new javax.swing.filechooser.FileFilter(){
public boolean accept(File f){
return f.getName().toLowerCase().endsWith(".jpg")||f.isDirectory();
}
public String getDescription(){
return "GIF Image";
}
});
int r=chooser.showOpenDialog(ImageViewerFrame.this);
if(r==JFileChooser.APPROVE_OPTION){
String name=chooser.getSelectedFile().getPath();
logger.log(Level.FINE,"Reading file {0}",name);
label.setIcon(new ImageIcon(name));
}else{
logger.fine("File open dialog canceled");
}
logger.exiting("ImageViewerFrame.FileOpenListenter","actionPerformed");
}
}
}
//通过扩展Handler类和StreamHandler类自定义处理器
class WindowHandler extends StreamHandler{
private JFrame frame;
public WindowHandler(){
frame = new JFrame();
final JTextArea output = new JTextArea();
output.setEditable(false);
frame.setSize(200, 200);
frame.add(new JScrollPane(output));
frame.setFocusableWindowState(false);
frame.setVisible(true);
setOutputStream(new OutputStream()//设置输出流,定向到文本框中
{
public void write(int b)
{
} // not called
public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
}
//默认情况下日志信息都存放在I/O缓冲中,但如果一条完整的日志信息会触发清空缓冲的动作,所以需要覆盖publish方法,以便在处理器获得每个记录之后刷新缓冲区
public void publish(LogRecord record) {
if (!frame.isVisible()) return;
super.publish(record);
flush();
}
}
//虽然设置了日志输出路径,但是所有级别为INFO、WARNING、SEVERE的消息都将显示到控制台上,例如加一句logger.warning("ImageViewerFrame");程序
//运行时,控制台和文本框都会出现该日志记录,因此,最好只将对程序用户有意义的消息设置为这几个级别,程序员想要的日志记录,设定为FINE是一个很好的选择