慢慢来比较快,虚心学技术
前言:之所以未按照前一篇文章 java日志篇(1)-日志概述所描述的日志发展史进行文章编写,是因为JUL是jdk自带的日志实现,其他日志实现原理大同小异,理解起来也是相对简单,如有兴趣可先去看看上一篇文章
目录
JUL是什么?
JUL基本使用
JUL使用分析
一、Logger
二、Handler
三、Formatter
总结
备注
JUL是jdk自带的一个日志实现,使用简单,控制灵活,如果是小型系统或者测试程序,显然JUL会是比log4j等更好的选择
使用java.util.logging.Logger的静态方法getLogger(loggerName)来获取或创建一个日志logger
import java.io.IOException;
import java.util.logging.*;
public class JULTest {
public static Logger log = Logger.getLogger("TestLog"); //获取日志对象
public static void main(String[] args) throws IOException {
log.info("info"); //信息日志
log.warning("warning"); //警告日志
log.log(Level.SEVERE,"server"); //严重日志
log.fine("fine");
}
}
使用日志我们可以使用默认的等级实现方法进行输出:如log.info("****")等,也可以使用特定的等级进行输出:如log.log(等级,输出信息)
如上代码输出日志结果如下:
一月 14, 2019 9:20:32 下午 JULTest main
信息: info
一月 14, 2019 9:20:32 下午 JULTest main
警告: warning
一月 14, 2019 9:20:32 下午 JULTest main
严重: server
为什么log.fine("fine");没有输出?
我们首先看一下第一个问题,需要先了解一下JUL的日志等级划分:
JUL日志等级划分(优先级递减)及内置代表的整数如下:
- OFF(Integer.MAX_VALUE)
- SEVERE(1000)
- WARNING(900)
- INFO(800)
- CONFIG(700)
- FINE(500)
- FINER(400)
- FINEST(300)
- ALL(Integer.MIN_VALUE)
为了更好更灵活的控制日志的输出,jul使用如上的9个等级作为日志等级划分,其中常用的日志等级主要是server,warning和info,当为 Logger 指定了一个 Level, 该 Logger 会包含当前指定级别以及更高级别的日志,logger默认的级别是INFO,比INFO更低的日志将不显示。由上述输出结果不难看出,只是输出了info及以上等级的日志,而自动忽略info等级以下的日志内容,为什么会有这样的实现呢?此处涉及到JUL的默认配置文件loging.properties,该配置文件位于jdk安装目录的lib包下
handlers= java.util.logging.ConsoleHandler
.level= INFO
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
com.xyz.foo.level = SEVERE
logger.log(Level.FINE,"file");调用方法如下
public void log(Levellevel, String msg) {
if (level.intValue() < levelValue ||levelValue == offValue) {
return;
}
LogRecord lr = new LogRecord(level, msg);
doLog(lr);
}
其中,我们可以看到.level属性指定了logger默认的日志级别为info,而logger类会先读取默认的日志级别,当选定的日志level整数值低于默认值,就不会进行日志输出
所以我们如果要更改logger的输出等级,可以通过修改对应配置项的level等级,也可以通过代码的方式去动态设置logger的等级
如下:结果输出为所有日志信息
import java.io.IOException;
import java.util.logging.*;
public class JULTest {
public static Logger log = Logger.getLogger("TestLog"); //获取日志对象
public static void main(String[] args) throws IOException {
log.setLevel(Level.ALL);//设置logger的日志级别为全部,默认输出所有级别日志信息
log.info("info"); //信息日志
log.warning("warning"); //警告日志
log.log(Level.SEVERE,"server"); //严重日志
log.fine("fine");
}
}
输出结果:
一月 15, 2019 12:12:23 上午 JULTest main
信息: info
一月 15, 2019 12:12:23 上午 JULTest main
警告: warning
一月 15, 2019 12:12:23 上午 JULTest main
严重: server
一月 15, 2019 12:12:23 上午 JULTest main
较详细: fine
既已引出logging.properties文件,我们就接着以此作为分析入口
由文件内容可以看到上一篇文章中介绍的handler组件的配置
默认Handler:
JUL中用的比较多的是两个Handler类:ConsoleHandler和FileHandler
其中,ConsoleHandler是对控制台输出的默认处理类,FileHandler是对文件输出的默认处理类
虽然我们可以通过修改配置文件更改系统日志的处理方式,但是更灵活的方式是从代码进行更改
举例:1、更改控制台Handler处理类的默认级别
import java.io.IOException;
import java.util.logging.*;
public class JULTest {
public static Logger log = Logger.getLogger("TestLog"); //获取日志对象
public static void main(String[] args) throws IOException {
log.setLevel(Level.ALL);//设置logger的日志级别为全部,默认输出所有级别日志信息
log.setUseParentHandlers(false); //禁用日志原本处理类
ConsoleHandler consoleHandler = new ConsoleHandler(); //创建控制台输出控制Handler
consoleHandler.setLevel(Level.INFO); //设置控制台输出级别
log.addHandler(consoleHandler); //将Handler加入logger中
log.info("info"); //信息日志
log.warning("warning"); //警告日志
log.log(Level.SEVERE,"server"); //严重日志
log.fine("fine");
}
}
输出结果:
一月 15, 2019 12:03:56 上午 JULTest main
信息: info
一月 15, 2019 12:03:56 上午 JULTest main
警告: warning
一月 15, 2019 12:03:56 上午 JULTest main
严重: server
上面的代码新建了一个控制台Handler,级别设置为INFO,所以结果又变成了只输出INFO及以上级别的日志
注:如果没有log.setUseParentHandlers(false); 父Handler与子Handler都会生效,此时会输出两遍日志内容
2、更改文件FileHandler处理类的处理方式
import java.io.IOException;
import java.util.logging.*;
public class JULTest {
public static Logger log = Logger.getLogger("TestLog"); //获取日志对象
public static void main(String[] args) throws IOException {
log.setLevel(Level.ALL);//设置logger的日志级别为全部,默认输出所有级别日志信息
log.setUseParentHandlers(false); //禁用日志原本处理类
FileHandler fileHandler = new FileHandler("日志路径/testJUL.log");
fileHandler.setLevel(Level.ALL); //记录级别
log.addHandler(fileHandler); //添加Handler
log.info("info"); //信息日志
log.warning("warning"); //警告日志
log.log(Level.SEVERE,"server"); //严重日志
log.fine("fine");
}
}
testJUL.log的内容如下
2019-01-15T00:22:58
1547482978598
0
TestLog
INFO
JULTest
main
1
info
2019-01-15T00:22:58
1547482978622
1
TestLog
WARNING
JULTest
main
1
warning
2019-01-15T00:22:58
1547482978622
2
TestLog
SEVERE
JULTest
main
1
server
2019-01-15T00:22:58
1547482978622
3
TestLog
FINER
JULTest
main
1
fine
3、自定义Handler
有时候我们在日志中做些自定义的操作,此时我们需要编写自定义Handler并加入logger的Handlers中
//继承Handler类编写自定义Handler
public class MyHandler extends Handler {
private LogRecord record;
//
@Override
public void publish(LogRecord record) {
this.record = record;
........(自定义操作)
}
@Override
public void flush() {
System.out.println("logger:"+this.record.getLoggerName()+"flush");
}
@Override
public void close() throws SecurityException {
System.out.println("logger:"+this.record.getLoggerName()+"close");
}
}
public class JULTest {
public static Logger log = Logger.getLogger("TestLog"); //获取日志对象
public static void main(String[] args) throws IOException {
log.setLevel(Level.ALL);//设置logger的日志级别为全部,默认输出所有级别日志信息
log.setUseParentHandlers(false); //禁用日志原本处理类
MyHandler myHandler = new MyHandler(); //创建自定义日志处理类实体
log.addHandler(myHandler); //添加日志处理实体类
log.info("info"); //信息日志
log.warning("warning"); //警告日志
log.log(Level.SEVERE,"server"); //严重日志
log.fine("fine");
}
}
可以看到上述我们的日志文件中默认输出是xml格式,很明显是不利于查看的。此时我们需要去更改日志输出样式,同样的可以使用代码实现:
//继承Formatter自定义日志输出形式
public class MyFormate extends Formatter {
@Override
public String format(LogRecord record) {
return new Date()+"-["+record.getSourceClassName()+"."+record.getSourceMethodName()+"]"+record.getLevel()+":"+record.getMessage()+"\n";
}
}
public class JULTest {
public static Logger log = Logger.getLogger("TestLog"); //获取日志对象
public static void main(String[] args) throws IOException {
log.setLevel(Level.ALL);//设置logger的日志级别为全部,默认输出所有级别日志信息
log.setUseParentHandlers(false); //禁用日志原本处理类
FileHandler fileHandler = new FileHandler("日志路径/testJUL.log");
fileHandler.setLevel(Level.ALL); //记录级别
fileHandler.setFormatter(new MyFormate()); //设置自定义样式
log.addHandler(fileHandler); //添加Handler
log.info("info"); //信息日志
log.warning("warning"); //警告日志
log.log(Level.SEVERE,"server"); //严重日志
log.fine("fine");
}
}
此时testJUL.log的输出样式更直观简洁了:
Tue Jan 15 00:36:09 CST 2019-[JULTest.main]INFO:info
Tue Jan 15 00:36:09 CST 2019-[JULTest.main]WARNING:warning
Tue Jan 15 00:36:09 CST 2019-[JULTest.main]SEVERE:server
Tue Jan 15 00:36:09 CST 2019-[JULTest.main]FINER:fine
1、JUL通过对级别的整数值比对决定是否输出日志,对外则是使用通俗易懂的Level名,这样的设计使得系统设计清晰简便,值得学习。
2、JUL的logging.propertise配置了基本的日志控制属性,可以通过更改该配置文件控制系统日志的输出
3、JUL的Handler使得JUL的日志控制十分的灵活,但是注意logger可以具备多个Handler,需要考虑Handler之间的干扰和冗余可能
4、JUL的Formatter使JUL的日志输出更加优雅美观,在学习的过程中,我们可以更加多的考虑如何精准输出日志所需要的信息,比如类名,方法名等
多参考前辈,源码未深入理解,还望多多探讨,文章结构不大严谨,后续继续完善,谢谢
部分内容参考文章:qingkangxu-JDK Logging深入分析