定义:
JUL全称Java util logging,是java原生的日志框架,使用时不需要另外引入第三方类库,相对于其他框架使用方便,学习简单,能够在小型的应用中灵活使用。
架构:
- Application:我们的程序应用。
- LogManager:管理Logger,是个单例Bean。
- Logger:日志记录器,我们的应用程序通过获取日志记录器Logger对象,调用其API来发布日志信息,Logger通常是应用程序访问日志系统的入口。
- Handler:日志处理器,每个Logger会关联持有多个Handler,Logger会把日志交给Handler进行处理,由Handler负责日志记录。Handler在这里是一个抽象,其具体实现决定了日志输出的位置,比如控制台,文件等。
- Formattter:日志格式转化器,负责对日志的格式进行处理。决定了输出的日志的最终形式。
- Level:每条日志信息都有一个关联的日志级别,该级别处理指导了日志信息的重要性。我们可以将Level作用于Logger和Handler上。以便于我们过滤消息。
- Filter:过滤器,根据规则过滤掉某些日志信息。
总体流程:
- 初始化LogManager,加载logging.properties配置文件,添加Logger到LogManager中。
- 从单例Bean LogManager获取Logger。
- 设置日志级别Level。
- Handler处理日志输出。
- Formatter处理日志格式。
日志的级别:
JUL日志级别由类java.util.logging.Level记录,总共有七个日志级别,由高到低分别是:
SEVERE //错误信息,一般是记录导致系统终止的信息。
WARNING //警告信息,一般记录程序的问题,该问题不会导致系统终止,但是却值得我们关注。
INFO // 一般信息,一般记录一些连接信息,访问信息等。(这是JUL的默认级别)
CONFIG // 一般记录加载配置文件等日志信息。
FINE // Debug日志信息,一般记录程序一般运行的状态。执行的流程参数的传递等信息。
FINER //与FINE 类似,只是记录的颗粒度要高一些。
FINEST //与上面两个类似,只是记录的颗粒度要高一些。
还有两个特殊的级别:
OFF: 可用来关闭日志信息,不输出任何级别的日志。
ALL: 记录所有级别的日志信息。
当Logger或者Handler设置了某一日志级别,低于该级别的日志信息将不会被记录。
入门案例:
public class JULDemo {
@Test
public void testHelloWorld(){
//创建日志记录器,传入参数是日志记录器的名称
Logger logger = Logger.getLogger("juldemo.JULDemo");
//记录severe级别信息
logger.severe("severe信息");
//记录warning级别信息
logger.warning("warning信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
}
}
输出结果:
因为默认的日志级别是INFO,所以比INFO级别低的 CONFIG、FINE、FINER、FINEST的日志记录消息没有记录。
自定义日志级别:
@Test
public void testCustomLogLevel(){
//创建日志记录器,传入参数是日志记录器的名称
Logger logger = Logger.getLogger("juldemo.JULDemo");
//创建一个输出到控制台的handler
ConsoleHandler consoleHandler = new ConsoleHandler();
//设置handler的日志级别为ALL,输出全部日志。
consoleHandler.setLevel(Level.ALL);
//把handler添加到logger中
logger.addHandler(consoleHandler);
//logger也设置日志级别为ALL
logger.setLevel(Level.ALL);
//logger设置不使用父logger的handler,不然日志会重复记录。此处后面会讲
logger.setUseParentHandlers(false);
//记录severe级别信息
logger.severe("severe信息");
//记录warning级别信息
logger.warning("warning信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
}
结果:
七个级别的日志信息都输出了。
Logger之间的父子关系:
JUL的Logger之间会存在父子关系,这种父子关系通过树状关系存储。JUL在初始化时会创建一个顶层的RootLogger作为所有Logger的父Logger。RootLogger作为树结构的根节点,Logger之间的关系通过树路径来关联。Logger记录器是用名称作为标志的。子Logger和默认使用父Logger的Handler。
Logger的父子关系默认是通过名子的层级关系来确定的。层级关系用 . 号分开。
也可以通过手动设置父Logger。
@Test
public void testLoggerParent(){
//创建一个名称为aaa的logger
Logger logger1 = Logger.getLogger("aaa");
//创建一个名称为aaa.bbb的logger
Logger logger2 = Logger.getLogger("aaa.bbb");
//创建一个名称为aaa.bbb.ccc的logger
Logger logger3 = Logger.getLogger("aaa.bbb.ccc");
//此时logger3的父Logger是logger2, logger2的父logger是logger1
//判断logger3的父Logger是不是logger2
System.out.println(logger3.getParent() == logger2);
//判断logger2的父logger是不是logger1
System.out.println(logger2.getParent() == logger1);
//logger1的父节点是顶级Logger RootLogger
System.out.println("logger1的父logger是 " + logger1.getParent());
//RootLogger的父Logger
System.out.println("RootLogger的父Logger是 " + logger1.getParent().getParent());
//手动设置父Logger
logger3.setParent(logger1);
//判断设置是否成功
System.out.println(logger3.getParent() == logger1);
}
执行结果:
子Logger默认会使用父Logger的Handler:
@Test
public void testUserParentHandler() throws IOException {
//创建一个名为aaa的logger
Logger logger1 = Logger.getLogger("aaa");
//创建一个名为aaa.bbb的logger,父Logger是handler
Logger logger2 = Logger.getLogger("aaa.bbb");
//创建一个handler
ConsoleHandler consoleHandler = new ConsoleHandler();
//把handler添加到logger1和logger2中。
logger1.addHandler(consoleHandler);
logger2.addHandler(consoleHandler);
//使用logger进行日志输出
//记录severe级别信息
logger2.severe("severe信息");
//记录warning级别信息
logger2.warning("warning信息");
logger2.info("info信息");
logger2.config("config信息");
logger2.fine("fine信息");
logger2.finer("finer信息");
logger2.finest("finest信息");
}
结果:
分析:
每个级别的日志信息输出了三次,因为logger2使用了父Logger logger1 ,父Logger的父Logger RootLogger、还有自身的handler共三个handler,所以日志会输出三倍。
使用logger2.setUseParentHandlers(false); 设置不使用父Logger的Handler。
结果:
只用了自身的handler。
使用FileHandler和SimpleFormatter
@Test
public void testFileHandler(){
Logger logger = Logger.getLogger("juldemo.JULDemo");
logger.setLevel(Level.ALL);
try {
//创建一个输出到文件的handler,第一个参数是生成文件名的pattern,第二个参数是是否已追加的方式输出到文件,默认false
FileHandler fileHandler = new FileHandler("D:\\logs\\java%u.log",true);
//创建一个SimpleFormatter,输出格式
SimpleFormatter formatter = new SimpleFormatter();
//设置formatter
fileHandler.setFormatter(formatter);
//设置日志级别
fileHandler.setLevel(Level.ALL);
//把handler添加到logger
logger.addHandler(fileHandler);
//设置不使用父Logger的handler
logger.setUseParentHandlers(false);
logger.severe("severe信息");
//记录warning级别信息
logger.warning("warning信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
} catch (IOException e) {
e.printStackTrace();
}
}
输出到文件:
配置文件详解:
上面的使用方式都是使用硬编码方式进行配置,现在介绍配置文件配置。
默认的配置文件:
$JAVA_HOME/jre/lib/logging.properties
默认配置:
#配置RootLogger的Handler,多个用逗号分隔。默认只有一个输出到控制台的handler
handlers= java.util.logging.ConsoleHandler
#配置RootLogger的日志级别,默认是INFO
.level= INFO
#配置FileHandler
#配置FileHandler的生成文件路径以及文件名的生成方式
java.util.logging.FileHandler.pattern = %h/java%u.log
#默认一个文件最多50000条日志记录
java.util.logging.FileHandler.limit = 50000
#默认生成一个文件
java.util.logging.FileHandler.count = 1
#默认使用XMLFormatter格式器
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
#ConsoleHandler的日志级别默认是INFO
java.util.logging.ConsoleHandler.level = INFO
#ConsoleHandler的默认格式化器时SimpleFormatter
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
我们可以使用自定义的文件或者修改默认配置文件进行配置。
#配置RootLogger的Handler,有java.util.logging.ConsoleHandler,java.util.logging.FileHandler
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
#配置RootLogger的日志级别ALL
.level= ALL
java.util.logging.FileHandler.pattern = D:/logs/java%u.log
#默认一个文件最多50000条日志记录
java.util.logging.FileHandler.limit = 50000
#设置FileHandle的日志级别为ALL
java.util.logging.FileHandler.level= ALL
#配置生成一个文件
java.util.logging.FileHandler.count = 1
#配置使用SimpleFormatter格式器
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
#配置追加模式
java.util.logging.FileHandler.append=true
#ConsoleHandler的日志级别默认是INFO
java.util.logging.ConsoleHandler.level = ALL
#ConsoleHandler的默认格式化器时SimpleFormatter
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
#设置日志格式
java.util.logging.SimpleFormatter.format= %1$tc %2$s%n%4$s: %5$s%6$s%n
使用:
@Test
public void testCustomConfig(){
LogManager logManager = LogManager.getLogManager();
try {
logManager.readConfiguration(this.getClass().getClassLoader().getResourceAsStream("logging.properties"));
Logger logger = Logger.getLogger("juldemo.JULDemo");
logger.severe("severe信息");
//记录warning级别信息
logger.warning("warning信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
} catch (IOException e) {
e.printStackTrace();
}
}
结果:
并且生成了日志文件,这里就不贴了。
如果不想用logmanager加载指定配置文件的话,就直接修改$JAVA_HOME/jre/lib/logging.properties文件也行。