环境
- 操作系统:Ubuntu 20.04
- JDK:17.0.1
Java自带log功能,用的是JDK标准库中的类java.util.logging.Logger 。下面是一个简单的例子:
package pkg1;
import java.util.logging.*;
public class Test0509 {
public static void main(String[] args) {
Logger logger = Logger.getLogger(Test0509.class.getName());
logger.finest("finest");
logger.finer("finer");
logger.fine("fine");
logger.config("config");
logger.info("info");
logger.warning("warning");
logger.severe("severe");
}
}
运行程序,结果如下:
May 09, 2022 2:57:02 PM pkg1.Test0509 main
INFO: info
May 09, 2022 2:57:02 PM pkg1.Test0509 main
WARNING: warning
May 09, 2022 2:57:02 PM pkg1.Test0509 main
SEVERE: server
可见,默认的log级别是 INFO ,也就是说 INFO 以及更高级别的log会生效。
注:Logger的 info() 等方法都有一个重载方法,参数的类型是 Supplier<String> ,例如:
logger.info("info1" + "info2");
logger.info(() -> "info1" + "info2");
两者功能非常类似,区别在于,前者一定会运行 "info1" + "info2" ,而后者只有在log级别为 INFO 或者更低的时候,才会运行 "info1" + "info2" ,避免了不必要的运算,显然后者性能更好。
Logger与Handler
现在我们来尝试调整log级别:
......
logger.setLevel(Level.WARNING);
logger.finest("finest");
logger.finer("finer");
logger.fine("fine");
logger.config("config");
logger.info("info");
logger.warning("warning");
logger.severe("severe");
......
运行程序,结果如下:
May 09, 2022 3:01:00 PM pkg1.Test0509 main
WARNING: warning
May 09, 2022 3:01:00 PM pkg1.Test0509 main
SEVERE: server
可见,把Logger的级别调整到 WARNING 之后,只有 WARNING 和 SEVERE 级别的log生效。
接下来把Logger的级别换成 FINER 试试看:
......
logger.setLevel(Level.FINER);
logger.finest("finest");
logger.finer("finer");
logger.fine("fine");
logger.config("config");
logger.info("info");
logger.warning("warning");
logger.severe("severe");
......
运行程序,结果如下:
May 09, 2022 6:56:55 PM pkg1.Test0509_3 main
INFO: info
May 09, 2022 6:56:55 PM pkg1.Test0509_3 main
WARNING: warning
May 09, 2022 6:56:55 PM pkg1.Test0509_3 main
SEVERE: severe
问题来了,为什么还是只有 INFO 以及更高的log级别生效呢?
原因在于,Logger可以有多个Handler,比如下面要提到的 ConsoleHandler 和 FileHandler 。顾名思义,分别把log输出到控制台和文件。默认情况下(参见下面的默认配置文件),是使用 ConsoleHandler ,这就是为什么log默认会输出到命令行。
如果给Logger配置了多个Handler,则多个Handler同时生效,也就是说log可以既输出到控制台,同时也输出到文件。通过文件或者代码,都可以配置Handler。
事实上,Handler也可以设置级别(默认级别也是 INFO ),与Logger的级别相比,最终二者中较高的级别生效。这就解释了为什么把Logger级别设置为 WARNING 生效,而设置为 FINER 却不生效了,因为Handler的级别仍然是 INFO ,二者中 INFO 级别较高,因此 INFO 及以上级别生效。
要使 FINER 级别生效,需要把Handler的级别也设置成 FINER ,在上面的 comment 1 处添加代码:
for (Handler handler : Logger.getLogger("").getHandlers()) {
handler.setLevel(Level.FINER);
}
再次运行程序,结果如下:
May 09, 2022 7:09:44 PM pkg1.Test0509_3 main
FINER: finer
May 09, 2022 7:09:44 PM pkg1.Test0509_3 main
FINE: fine
May 09, 2022 7:09:44 PM pkg1.Test0509_3 main
CONFIG: config
May 09, 2022 7:09:44 PM pkg1.Test0509_3 main
INFO: info
May 09, 2022 7:09:44 PM pkg1.Test0509_3 main
WARNING: warning
May 09, 2022 7:09:44 PM pkg1.Test0509_3 main
SEVERE: severe
可见,这回 Finer 及以上级别生效了。
配置Log
文件配置
默认配置文件是 $JAVA_HOME/conf/logging.properties ,里面可以配置各种日志选项,比如:
handlers= java.util.logging.ConsoleHandler :设置Handler,若有多个Handler(比如 FileHandler ),handler之间用 , 隔开;.level= INFO :默认的全局log级别;java.util.logging.FileHandler.pattern = %h/java%u.log :文件日志的命名,其中 %h 表示home目录, %u 表示0、1、2这样的数字;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 :控制台日志的格式- ……
例如:把 java.util.logging.ConsoleHandler.level = INFO 改为 java.util.logging.ConsoleHandler.level = SEVERE 。再次运行程序,结果如下:
May 09, 2022 3:31:25 PM pkg1.Test0509 main
SEVERE: severe
注意:配置文件里Handler的级别为 SEVERE ,而代码里Logger级别设置为 WARNING ,二者相比,级别较高者生效。
如果要使用自定义配置文件,需要在运行程序时,指定 java.util.logging.config.file 选项。比如 java -Djava.util.logging.config.file=myfile 。
最后别忘了还原默认配置文件。
代码配置
前面我们已经试过在代码中设置log级别,下面我们再看看其它配置。比如添加一个 FileHandler :
......
try {
Handler handler = new FileHandler();
logger.addHandler(handler);
} catch (IOException e) {
throw new RuntimeException(e);
}
logger.setLevel(Level.WARNING);
logger.finest("finest");
logger.finer("finer");
logger.fine("fine");
logger.config("config");
logger.info("info");
logger.warning("warning");
logger.severe("severe");
......
运行程序,除了命令行输出结果以外,在home目录下生成 java0.log 文件(这是默认的log文件路径和命名),内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2022-05-09T07:51:05.215198475Z</date>
<millis>1652082665215</millis>
<nanos>198475</nanos>
<sequence>0</sequence>
<logger>pkg1.Test0509</logger>
<level>WARNING</level>
<class>pkg1.Test0509</class>
<method>main</method>
<thread>1</thread>
<message>warning</message>
</record>
<record>
<date>2022-05-09T07:51:05.265971919Z</date>
<millis>1652082665265</millis>
<nanos>971919</nanos>
<sequence>1</sequence>
<logger>pkg1.Test0509</logger>
<level>SEVERE</level>
<class>pkg1.Test0509</class>
<method>main</method>
<thread>1</thread>
<message>severe</message>
</record>
</log>
可见,对于log文件和命令行log,都是 WARNING 和 SEVERE 级别的log生效,只不过两种log的格式不同。
如果要指定文件路径和文件名,只需在创建 FileHandler 时指定,比如:
Handler handler = new FileHandler("aaa.log");
现在运行程序,就会在当前目录下生成 aaa.log 文件。注意,在本例中,当前目录指的是项目的根目录。
注意代码中被注释那一行。如果Handler没有设置级别,默认级别是 INFO ,与Logger级别 WARNING 相比,取较高的 WARNING 。如果设置了级别,则二者中级别较高者生效:
handler.setLevel(Level.SEVERE); :Handler中 SEVERE 级别生效;handler.setLevel(Level.INFO); :Logger中 WARNING 级别生效;
参考
|