目录
首先,为什么要使用日志系统?
如果单纯地用System.out.println打印信息,如果项目比较大,存在大量的信息就会显得非常凌乱。
而且,当我们希望在debug的时候打印某些信息,而运行的时候不打印,使用直接输出的方法就行不通了。
因此,我们需要使用日志框架来规范日志的输出。
打印日志
JDK为我们提供了一个自带的日记框架,位于java.util.logging(JUL名称的来源)包下,可以使用此框架来实现日志的规范化打印:
java
public static void main(String[] args) {
//获取日志打印器
Logger logger = Logger.getLogger(Main.class.getName());
//输出一个字符串信息
logger.info("我是日志");
}
以上日志打印的结果为:
十一月 01, 2024 9:21:52 下午 com.test.Main main
信息: 我是日志
可以看到,日志打印是会比普通的print方法多出一些内容的
日志的级别
日志分为7个级别:
- SERVER(最高级别) :一般用于代表严重错误
- WARNING:一般用于表示某些警告,单不足以判断为错误
- INFO(默认级别):常规消息
- CONFIG
- FINE
- FINER
- FINEST(最低级别)
我们之前使用的代码:
java
logger.info("我是日志");
就是输出的INFO级别的日志
可以通过log方法来设定日志输出的级别:
java
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class.getName());
//server级别的错误可以传入异常
logger.log(Level.SEVERE,"我是server日志",new IOException("IO错误"));
logger.log(Level.WARNING,"我是warning日志");
logger.log(Level.INFO,"我是info日志");
logger.log(Level.CONFIG,"我是config日志");
logger.log(Level.FINE,"我是fine日志");
}
输出的结果为:
十一月 01, 2024 10:07:41 下午 com.test.Main main
严重: 我是server日志
java.io.IOException: IO错误
at com.test.Main.main(Main.java:13)
十一月 01, 2024 10:07:41 下午 com.test.Main main
警告: 我是warning日志
十一月 01, 2024 10:07:41 下午 com.test.Main main
信息: 我是info日志
可以看到,server级别的错误可以传入异常类型,打印出的日志会明确得到异常的类型,但程序并不会因为这个传入的异常终止
另外,可以发现低于默认级别的日志没有输出,这是因为日志设定的输出等级就是输出默认级别以上的内容,而我们可以手动修改日志的打印级别:
java
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class.getName());
//修改日志级别
logger.setLevel(Level.ALL);
//不使用父日志处理器
logger.setUseParentHandlers(false);
//自定义日志处理器
ConsoleHandler hander = new ConsoleHandler();
hander.setLevel(Level.ALL);
logger.addHandler(hander);
logger.log(Level.SEVERE,"我是server日志",new IOException("IO错误"));
logger.log(Level.WARNING,"我是warning日志");
logger.log(Level.INFO,"我是info日志");
logger.log(Level.CONFIG,"我是config日志");
logger.log(Level.FINE,"我是fine日志");
}
每一个Logger都有一个父日志打印器,而这个父日志打印器默认使用的ConsoleHandler,这个的日志级别为INFO。并且,默认情况下,Java 的日志记录器会使用父处理器进行日志输出。因此想自定义日志输出级别时,需要先关闭父处理器。
既然关闭了父处理器,就需要自己再新建一个处理器,在这段代码中,设置了两次Level.ALL,logger.setLevel(Level.ALL) 的作用是确保日志记录器捕获所有日志信息,**hander.setLevel(Level.ALL)**的作用是控制实际输出的日志内容。即便记录器捕获了所有级别的日志,但只有满足处理器级别条件的日志才会被真正输出。
打印文件
日志处理器不仅仅只有控制台打印,也可以打印文件
这时就需要使用文件处理器
(文件处理器和日志处理器可以同时存在)
java
//打印到本地文件
//自定义文件处理器
FileHandler fileHandler = new FileHandler("test.log");
//设置文件打印的级别
fileHandler.setLevel(Level.WARNING);
logger.addHandler(fileHandler);
运行后会生成一个test.log文件,文件中记录了打印的日志:
XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2024-11-01T22:33:44</date>
<millis>1730471624479</millis>
<sequence>0</sequence>
<logger>com.test.Main</logger>
<level>SEVERE</level>
<class>com.test.Main</class>
<method>main</method>
<thread>1</thread>
<message>我是server日志</message>
<exception>
<message>java.io.IOException: IO错误</message>
<frame>
<class>com.test.Main</class>
<method>main</method>
<line>32</line>
</frame>
</exception>
</record>
<record>
<date>2024-11-01T22:33:44</date>
<millis>1730471624496</millis>
<sequence>1</sequence>
<logger>com.test.Main</logger>
<level>WARNING</level>
<class>com.test.Main</class>
<method>main</method>
<thread>1</thread>
<message>我是warning日志</message>
</record>
</log>
可以看到,文件处理器打印出的内容格式和控制台处理器打印出的内容格式不太一样,文件处理器默认打印出的格式为xml格式
这样有点难以阅读,我们想修改一下文件处理器打印出的格式,应该怎么办呢?
这时就要用到另外一个类,叫做SimpleFormatter,这个类叫做日志格式化器,是专门用于将日志转化为简单易读的格式的。
而控制台处理器就默认使用了这个类。
在文件处理器中使用**.setFormatter()**方法添加格式化器:
java
SimpleFormatter simpleFormatter = new SimpleFormatter();
//打印到本地文件
//自定义文件处理器
FileHandler fileHandler = new FileHandler("test.log");
fileHandler.setFormatter(simpleFormatter);
//设置文件打印的级别
fileHandler.setLevel(Level.WARNING);
logger.addHandler(fileHandler);
除了**SimpleFormatter,**也可以使用XMLFormatter来将日志的格式改为xml格式
日志过滤器
如果我们不希望某些日志信息被输出,可以自定义日志过滤器
java
Filter customFilter = new Filter() {
@Override
public boolean isLoggable(LogRecord record) {
// 只记录包含 "IMPORTANT" 的日志
return record.getMessage().contains("IMPORTANT");
}
};
// 将过滤器添加到处理器
handler.setFilter(customFilter);
自定义日志过滤器的本质是通过重写Filter接口中的isLoggable接口实现的
过滤器可以被应用到handler中,也可以被应用到logger中
java
logger.setFilter(customFilter);
日志输出流程
整个日志输出流程是一个层层筛选、逐步处理的过程:
- 应用程序生成日志,
Logger
接收并通过其自身的过滤器进行初步筛选。 - 筛选通过的日志记录传递给
Handler
,并经过Handler
的过滤器进一步筛选。 - 最终符合条件的日志通过
Handler
输出到指定的外部系统。