JUL原生日志框架
更新: 12/31/2025, 6:43:00 AM 字数: 0 字 时长: 0 分钟
简介
JUL(Java Util Logging)是Java原生日志框架,不需要引入第三方依赖,使用简单,一般用于小型应用中,主流项目中现在很少使用了,所以,此处仅限于了解。
JUL架构中,用户使用Logger来进行日志记录,每一个Logger都会关联一组Hander将日志记录到日志文件、控制台等。

在Hander输出日志前会经过Filter过滤,支持过滤日志级别和关键字,并且使用Layout对日志输出内容进行格式化排版。注意,Handler的Filter是在Logger的Filter过滤后的结果上进行再次过滤的。
快速开始
package com.shooter.springboot.log;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.logging.*;
public class JULTest {
@Test
public void testQuickStart(){
// 获取日志记录器对象
Logger logger = Logger.getLogger("com.shooter.springboot.log.JULTest");
// 默认只输出severe、warning、info三个级别的日志
// 要想输出更细颗粒的日志,需要指定日志级别
logger.severe("我是[严重]级别日志");
logger.warning("我是[警告]级别日志");
logger.info("我是[默认]日志级别");
// 以下日志级别是不能输出的
logger.config("我是[配置]级别日志");
logger.fine("我是[详细]级别日志");
logger.finer("我是[较详细]级别日志");
logger.finest("我是[非常详细]级别日志");
}
}运行结果:
严重: 我是[严重]级别日志
警告: 我是[警告]级别日志
信息: 我是[默认]日志级别说明:JUL内置工7种日志级别,另外有2中特殊级别:
七种日志级别:
SEVERE(最高等级)-> WARNING -> INFO(默认等级)
-> CONFIG -> FINE -> FINER-> FINEST(最低等级)
两种特殊日志级别,用于开关日志:
- OFF:可用于关闭日志记录;
- ALL:启用所有消息的日志记录。日志配置
RootLogger
在JUL中,Logger是有父子继承关系的,子代会继承父Logger的配置,如日志级别、关联的Handler等。对于所有Logger对象,若没有特殊配置,则最终都会继承RootLogger的配置。所以,存在 直接修改RootLogger默认处理器 和 对某个Logger单独配置 两种修改日志配置的方式。
@Test
public void testRootLogger() {
// 获取日志记录器对象
Logger logger = Logger.getLogger("com.shooter.springboot.log.JULTest");
final Logger parent = logger.getParent();
// 修改RootLogger默认处理器
// 先在RootLogger过滤日志等级
parent.setLevel(Level.WARNING);
// 在RootLogger过滤后,再次过滤到ConsoleHandler中
parent.getHandlers()[0].setLevel(Level.CONFIG);
// 日志输出
logger.severe("我是[严重]级别日志");
logger.warning("我是[警告]级别日志");
logger.info("我是[默认]日志级别");
logger.config("我是[配置]级别日志");
logger.fine("我是[详细]级别日志");
logger.finer("我是[较详细]级别日志");
logger.finest("我是[非常详细]级别日志");
}输出结果:
严重: 我是[严重]级别日志
警告: 我是[警告]级别日志硬编码形式
对某个Logger单独配置时,需要将其设置为不继承使用父类的Logger配置。
// 关闭系统默认配置,即不使用父Logger的Handlers
logger.setUseParentHandlers(false); 首先,介绍通过硬编码形式实现JUL自定义Logger的示例,代码如下:
package com.shooter.springboot.log;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.logging.*;
public class JULTest {
@Test
public void testCodeConfig() throws IOException{
// 获取日志记录器对象
Logger logger = Logger.getLogger("com.shooter.springboot.log.JULTest");
// 关闭系统默认配置,即不使用父Logger的Handlers
logger.setUseParentHandlers(false);
// 配置logger记录器日志级别
logger.setLevel(Level.ALL);
// 日志记录格式,使用简单格式转换对象
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 控制台输出Handler,并设置日志级别为INFO 和 日志记录格式
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(simpleFormatter);
consoleHandler.setLevel(Level.ALL);
// 文件输出Handler,并设置日志级别为ALL 和 日志记录格式
FileHandler fileHandler = new FileHandler("./log.log");
fileHandler.setLevel(Level.ALL);
fileHandler.setFormatter(simpleFormatter);
// 计录器关联处理器,即此logger对象的日志信息输出到这两个Handler进行处理
logger.addHandler(consoleHandler);
logger.addHandler(fileHandler);
// 测试:日志记录输出
logger.severe("我是[严重]级别日志");
logger.warning("我是[警告]级别日志");
logger.info("我是[默认]日志级别");
logger.config("我是[配置]级别日志");
logger.fine("我是[详细]级别日志");
logger.finer("我是[较详细]级别日志");
logger.finest("我是[非常详细]级别日志");
}
}运行结果(在控制台和./log.log都有打印):
严重: 我是[严重]级别日志
警告: 我是[警告]级别日志
信息: 我是[默认]日志级别
配置: 我是[配置]级别日志
详细: 我是[详细]级别日志
较详细: 我是[较详细]级别日志
非常详细: 我是[非常详细]级别日志
信息: Hello 1 2
严重: 我是空指针异常
java.lang.NullPointerException日志配置文件
但是在实际开发中,一般采用的是日志配置文件的形式进行统一配置的。
(1)新建日志配置文件
我们可以在resource目录下新建日志配置文件logging.properties,将配置信息统一配置到该文件中。
PS:系统默认从JDK的安装目录下的
lib目录下读取配置文件logging.properties的。
###########################################################
# 全局属性
###########################################################
# 顶级RootLogger关连的Handler,多个Handler使用逗号分隔
# 对于其他Logger,如果没有指定自己的Handler,则默认继承
handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# 默认全局日志级别,Logger和Handler都可以设置自己的日志级别来覆盖次级别
.level = ALL
###########################################################
# Handler 配置
###########################################################
# ConsoleHandler配置
# 日志级别
java.util.logging.ConsoleHandler.level = INFO
# 日志追加方式
java.util.logging.ConsoleHandler.append = true
# 字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 日志格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# FileHandler配置
# 日志文件存储位置
#java.util.logging.FileHandler.pattern = ./log_%u.log
java.util.logging.FileHandler.pattern = ./log.log
# 单个文件的最大字节数,单位是bit,1024bit即为1kb 0 代表不限制
java.util.logging.FileHandler.limit = 1024*1024*10
# 文件数量上限,多个文件为log.log.0、log.log.1、、log.log.2
java.util.logging.FileHandler.count = 5
# 日志级别
java.util.logging.FileHandler.level = CONFIG
# 日志追加方式
java.util.logging.FileHandler.append = true
# 字符集
java.util.logging.FileHandler.encoding = UTF-8
# FileHandler持有的最大并发锁数
java.util.logging.FileHandler.maxLocks=100
# 指定要使用的Formatter类的名称,FileHandler默认使用的是XMLFormatter
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
# SimpleFormatter的输出格式配置
java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n(2)新建日志工厂类
这里建议使用工厂类统一返回Logger记录器。
package com.shooter.springboot.log;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.LogManager;
import java.util.logging.Logger;
public class LoggerFactory {
public static Logger getLogger (Class<?> clazz) throws IOException {
// 读取配置文件
InputStream inputStream = clazz.getClassLoader()
.getResourceAsStream("logging.properties");
// 获取LogManager
LogManager logManager = LogManager.getLogManager();
// 加载配置文件
logManager.readConfiguration(inputStream);
return Logger.getLogger(clazz.getName());
}
}(3)单元测试
package com.shooter.springboot.log;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.logging.*;
public class JULTest {
@Test
public void testLoggerFactory() throws IOException{
// 获取日志记录器对象
Logger logger = LoggerFactory.getLogger(JULTest.class);
// 日志记录输出
logger.severe("我是[严重]级别日志");
logger.warning("我是[警告]级别日志");
logger.info("我是[默认]日志级别");
logger.config("我是[配置]级别日志");
logger.fine("我是[详细]级别日志");
logger.finer("我是[较详细]级别日志");
logger.finest("我是[非常详细]级别日志");
}
} 观察发现,控制台只执行INFO级别以上的日志,文件只记录CONFIG级别以上的日志。
更多配置
日志过滤器
(1)新增自定义日志过滤器
Filter日志过滤器用于对输出的日志记录进行过滤,如只输出包含某段文字的日志、只输出某个方法中记录的日志 或 某个级别的日志等待。
Logger对象将日志信息包装成一个LogRecord对象,然后将该对象传给Handler处理。其中,每个LogRecord对象都包含了日志的文本信息、日志生成时间戳、来源类、来源方法、来源线程等信息。
package com.shooter.springboot.log;
import java.util.logging.Filter;
import java.util.logging.LogRecord;
public class MyLoggerFilter implements Filter{
private static final String SENSITIVE_MESSAGE = "严重";
@Override
public boolean isLoggable(LogRecord record) {
String message = record.getMessage();
return message == null || !message.contains(SENSITIVE_MESSAGE);
}
}(2)在Logger中配置自定义日志拦截
public static Logger getLogger (Class<?> clazz) throws IOException {
// 读取配置文件
InputStream inputStream = clazz.getClassLoader()
.getResourceAsStream("logging.properties");
// 获取LogManager
LogManager logManager = LogManager.getLogManager();
// 加载配置文件
logManager.readConfiguration(inputStream);
// 加载自定义拦截
Logger logger = Logger.getLogger(clazz.getName());
logger.setFilter(new MyLoggerFilter());
return logger;
}(3)单元测试
@Test
public void testMyLoggerFilter() throws IOException{
// 获取日志记录器对象
Logger logger = LoggerFactory.getLogger(JULTest.class);
// 日志记录输出
logger.severe("我是[严重]级别日志");
logger.info("我是[默认]日志级别");
// 占位符
// 输出指定日志级别的日志记录
logger.log(Level.INFO,"Hello {0} {1}",new Object[]{1,2});
// 异常堆栈信息
logger.log(Level.SEVERE,"我是空指针异常",new NullPointerException());
} 运行后,发现第一条包含严重字样的日志是没有输出的。
自定义Logger
我们可以针对某一个Logger进行单独配置,例如日志级别、关联的Handler,而不默认继承父类。或可对以包名为名称的Logger进行配置,这样这个包名下所有子代的Logger都能默认继承此配置。
###########################################################
# 自定义Logger
###########################################################
# 设置com.shooter.springboot.log1的Logger对象的日志级别为WARNING
com.shooter.springboot.log1 = WARNING
# 只关联FileHandler
com.shooter.springboot.log1.handlers = java.util.logging.FileHandler
# 关闭父日志处理器
# 若不关闭,则在控制台会同时出现父日志处理器和自定义的处理器,消息将重复输出
com.shooter.springboot.log1.useParentHandlers = false附录
JULTest完整示例
package com.shooter.springboot.log;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.logging.*;
public class JULTest {
@Test
public void testQuickStart(){
// 获取日志记录器对象
Logger logger = Logger.getLogger("com.shooter.springboot.log.JULTest");
// 默认只输出severe、warning、info三个级别的日志
// 要想输出更细颗粒的日志,需要指定日志级别
logger.severe("我是[严重]级别日志");
logger.warning("我是[警告]级别日志");
logger.info("我是[默认]日志级别");
}
@Test
public void testRootLogger() {
// 获取日志记录器对象
Logger logger = Logger.getLogger("com.shooter.springboot.log.JULTest");
final Logger parent = logger.getParent();
// 修改RootLogger默认处理器
// 先在RootLogger过滤日志等级
parent.setLevel(Level.WARNING);
// 在RootLogger过滤后,再次过滤到ConsoleHandler中
parent.getHandlers()[0].setLevel(Level.CONFIG);
// 日志输出
logger.severe("我是[严重]级别日志");
logger.warning("我是[警告]级别日志");
logger.info("我是[默认]日志级别");
logger.config("我是[配置]级别日志");
logger.fine("我是[详细]级别日志");
logger.finer("我是[较详细]级别日志");
logger.finest("我是[非常详细]级别日志");
}
@Test
public void testCodeConfig() throws IOException{
// 获取日志记录器对象
Logger logger = Logger.getLogger("com.shooter.springboot.log.JULTest");
// 关闭系统默认配置,即不使用父Logger的Handlers
logger.setUseParentHandlers(false);
// 配置记录器日志级别
logger.setLevel(Level.ALL);
// 日志记录格式,使用简单格式转换对象
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 控制台输出Handler,并设置日志级别为INFO 和 日志记录格式
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(simpleFormatter);
consoleHandler.setLevel(Level.ALL);
// 文件输出Handler,并设置日志级别为ALL 和 日志记录格式
FileHandler fileHandler = new FileHandler("./log.log");
fileHandler.setLevel(Level.ALL);
fileHandler.setFormatter(simpleFormatter);
// 计录器关联处理器,即此logger对象的日志信息输出到这两个Handler进行处理
logger.addHandler(consoleHandler);
logger.addHandler(fileHandler);
// 日志记录输出
logger.severe("我是[严重]级别日志");
logger.warning("我是[警告]级别日志");
logger.info("我是[默认]日志级别");
logger.config("我是[配置]级别日志");
logger.fine("我是[详细]级别日志");
logger.finer("我是[较详细]级别日志");
logger.finest("我是[非常详细]级别日志");
// 占位符
// 输出指定日志级别的日志记录
logger.log(Level.INFO,"Hello {0} {1}",new Object[]{1,2});
// 异常堆栈信息
logger.log(Level.SEVERE,"我是空指针异常",new NullPointerException());
}
@Test
public void testLoggerFactory() throws IOException{
// 获取日志记录器对象
Logger logger = LoggerFactory.getLogger(JULTest.class);
// 日志记录输出
logger.severe("我是[严重]级别日志");
logger.warning("我是[警告]级别日志");
logger.info("我是[默认]日志级别");
logger.config("我是[配置]级别日志");
logger.fine("我是[详细]级别日志");
logger.finer("我是[较详细]级别日志");
logger.finest("我是[非常详细]级别日志");
}
@Test
public void testMyLoggerFilter() throws IOException{
// 获取日志记录器对象
Logger logger = LoggerFactory.getLogger(JULTest.class);
// 日志记录输出
logger.severe("我是[严重]级别日志");
logger.info("我是[默认]日志级别");
}
}