Skip to content

JUL原生日志框架

更新: 12/31/2025, 6:43:00 AM   字数: 0 字   时长: 0 分钟

教程

简介

​  JUL(Java Util Logging)是Java原生日志框架,不需要引入第三方依赖,使用简单,一般用于小型应用中,主流项目中现在很少使用了,所以,此处仅限于了解

​  JUL架构中,用户使用Logger来进行日志记录,每一个Logger都会关联一组Hander将日志记录到日志文件、控制台等。

图片加载中... 请稍等(~ ̄▽ ̄)~

​  在Hander输出日志前会经过Filter过滤,支持过滤日志级别和关键字,并且使用Layout对日志输出内容进行格式化排版。注意,HandlerFilter是在LoggerFilter过滤后的结果上进行再次过滤的

快速开始

java
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单独配置 两种修改日志配置的方式。

java
@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配置。

java
// 关闭系统默认配置,即不使用父Logger的Handlers
logger.setUseParentHandlers(false);

​  首先,介绍通过硬编码形式实现JUL自定义Logger的示例,代码如下:

java
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的。

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记录器。

java
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)单元测试

java
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对象都包含了日志的文本信息、日志生成时间戳、来源类、来源方法、来源线程等信息。

java
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中配置自定义日志拦截

java
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)单元测试

java
@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都能默认继承此配置。

properties
###########################################################
# 自定义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完整示例

java
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("我是[默认]日志级别");
    }

}

更新时间:

Released under the MIT License.