一、简介
JUL(java.util.logging)是Java原生日志框架,使用时不需要引入第三方类库。
二、相关组件
- Logger:应用程序进行日志记录调用的主要实体。 Logger 对象用于记录特定系统或应用程序组件的消息。
- LogRecord:用于在日志框架和单个日志处理程序之间传递日志请求的实体类。
- Handler:将 LogRecord 对象导出到各种目的地,包括内存、输出流、控制台、文件和套接字。
- Level:定义一组可用于控制日志输出的标准日志级别。程序可以配置为输出某些级别的日志,而忽略其他级别的输出。
- Formatter:提供对格式化 LogRecord 对象的支持。这个包包括两个格式化程序,SimpleFormatter 和 XMLFormatter,分别用于格式化纯文本或 XML 格式的日志记录。
三、简单使用
JUL记录日志的时候,可以选择不同的级别以区分不同的日志信息,JUL主要有如下7个级别(此外,还有一个级别 OFF 可用于关闭日志记录,还有一个级别 ALL 可用于启用所有日志记录)
- SEVERE (highest value):错误
- WARNING:警告
- INFO(default value):消息
- CONFIG:配置
- FINE:详细信息(少)
- FINER:详细信息(中)
- FINEST (lowest value):详细信息(多)
接下来,使用Logger分别打印7个级别日志。
package com.example;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import org.junit.Test;
public class AppTest {
@Test
public void testLog() {
Logger logger = Logger.getLogger(this.getClass().getName());
//打印不同Level日志
logger.severe("it's a severe level log");
logger.warning("it's a warning level log");
logger.info("it's a info level log");
logger.config("it's a config level log");
logger.fine("it's a fine level log");
logger.finer("it's a finer level log");
logger.finest("it's a finest level log");
}
}

从控制台输出可以看到,我们打印了7个不同级别的日志,最终只输出了severe、warning、info三个级别的日志,这是因为系统默认的日志级别是info,接下来使用logger.setLevel(Level.CONFIG);设置输出CONFIG级别及该级别以上的日志,观察输出。

可以发现输出并没有任何变化,这是因为logger需要添加自定义的handler。
package com.example;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import org.junit.Test;
public class AppTest {
@Test
public void testLog() {
Logger logger = Logger.getLogger(this.getClass().getName());
//不使用父级处理器
logger.setUseParentHandlers(false);
ConsoleHandler handler = new ConsoleHandler();
SimpleFormatter formatter = new SimpleFormatter();
handler.setFormatter(formatter);
logger.addHandler(handler);
logger.setLevel(Level.ALL);
handler.setLevel(Level.ALL);
logger.severe("it's a severe level log");
logger.warning("it's a warning level log");
logger.info("it's a info level log");
logger.config("it's a config level log");
logger.fine("it's a fine level log");
logger.finer("it's a finer level log");
logger.finest("it's a finest level log");
}
}

将日志输出到文件,这时候就无需使用ConsoleHandler,而是使用FileHandler。
package com.example;
import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest {
@Test
public void testFileHandlerLog() throws SecurityException, IOException {
Logger logger = Logger.getLogger(this.getClass().getName());
logger.setUseParentHandlers(false);
FileHandler fileHandler = new FileHandler("/Users/wag/projects/demo/log.log");
SimpleFormatter formatter = new SimpleFormatter();
fileHandler.setFormatter(formatter);
logger.addHandler(fileHandler);
logger.setLevel(Level.ALL);
fileHandler.setLevel(Level.ALL);
logger.severe("it's a severe level log");
logger.warning("it's a warning level log");
logger.info("it's a info level log");
logger.config("it's a config level log");
logger.fine("it's a fine level log");
logger.finer("it's a finer level log");
logger.finest("it's a finest level log");
}
}

此时,如果还需要添加控制台输出,则直接将上面的ConsoleHandler添加到logger的处理器中即可。
JUL的父子关系
JUL的Logger存在着父子关系,该父子关系不是一般意义上的继承,而是通过树结构存在的,比如:
package com.example;
import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest {
@Test
public void logParentTest() {
Logger parentLogger = Logger.getLogger("com.example");
Logger subLogger = Logger.getLogger("com.example.app");
System.out.println(subLogger.getParent() == parentLogger); //true
System.out.println(subLogger.getParent().getName()); //com.example
System.out.println(parentLogger.getParent()); //java.util.logging.LogManager$RootLogger@31cefde0
//RootLogger是所有Logger对象的顶层Logger
}
}
接下来对parentLogger做相关设置,然后使用subLogger进行打印。
package com.example;
import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest {
@Test
public void logParentTest() {
Logger parentLogger = Logger.getLogger("com.example");
Logger subLogger = Logger.getLogger("com.example.app");
parentLogger.setUseParentHandlers(false);
ConsoleHandler handler = new ConsoleHandler();
SimpleFormatter formatter = new SimpleFormatter();
handler.setFormatter(formatter);
parentLogger.addHandler(handler);
parentLogger.setLevel(Level.CONFIG);
handler.setLevel(Level.CONFIG);
subLogger.severe("it's a severe level log");
subLogger.warning("it's a warning level log");
subLogger.info("it's a info level log");
subLogger.config("it's a config level log");
subLogger.fine("it's a fine level log");
subLogger.finer("it's a finer level log");
subLogger.finest("it's a finest level log");
}
}

系统默认配置文件
JUL一般都是以文件的方式进行配置,如果没有设置配置文件,则会读取系统默认配置文件,位置为jre/lib/logging.properties。
#Logger使用的处理器,如果需要多个可以在后面直接添加,以逗号分隔
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
自定义配置文件输出级别
接着在src下新建配置文件,并将INFO改为CONFIG,然后读取配置文件观察输出。
package com.example;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest {
@Test
public void fileConfigTest() throws SecurityException, IOException{
InputStream in = new FileInputStream("/Users/wag/projects/demo/logging.properties");
LogManager logManager = LogManager.getLogManager();
logManager.readConfiguration(in);
Logger logger = Logger.getLogger(this.getClass().getName());
logger.severe("it's a severe level log");
logger.warning("it's a warning level log");
logger.info("it's a info level log");
logger.config("it's a config level log");
logger.fine("it's a fine level log");
logger.finer("it's a finer level log");
logger.finest("it's a finest level log");
}
}

可以看到在配置文件中自定义的输出级别生效了。
自定义配置文件日志
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.SimpleFormatter
#自定义Logger
com.example.AppTest.handlers = java.util.logging.FileHandler
com.example.AppTest.level = CONFIG
com.example.AppTest.useParentHandlers = false
运行后,在用户目录下可以看到java0.log,并且设置了useParentHandlers=false,控制台不会有任何输出。
