JUL 从基础配置到高级玩法

🎯 JUL 的总体架构

JUL 的核心组件:

  • Logger:记录日志的入口
  • Handler:日志输出目标(控制台、文件等)
  • Formatter:日志格式
  • Level:日志级别
  • Filter:过滤日志记录
  • LogManager:管理全局配置(读取 logging.properties)

整体日志流:

Logger → FilterHandlerFilter → Formatter → 输出

🚀 最基础的用法

import java.util.logging.*;

public class Demo {
    private static final Logger logger = Logger.getLogger(Demo.class.getName());

    public static void main(String[] args) {
        logger.info("Hello logging");
        logger.warning("Something wrong!");
    }
}

默认输出到控制台,格式是 JUL 默认格式。

⚙️ 通过 logging.properties 进行全局配置(常用)

JUL 的默认全局配置文件:

$JAVA_HOME/jre/lib/logging.properties

你也可以指定自己的配置:

java -Djava.util.logging.config.file=./logging.properties Demo

示例 properties:

# 全局日志级别
.level = INFO

# 配置控制台输出处理器
handlers = java.util.logging.ConsoleHandler

# ConsoleHandler 级别
java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

如需输出到文件:

handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler

java.util.logging.FileHandler.pattern = logs/app.%u.%g.log
java.util.logging.FileHandler.limit = 5000000
java.util.logging.FileHandler.count = 3
java.util.logging.FileHandler.append = true
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter

📌 代码动态配置(无需 logging.properties)

(1)关闭父 Logger 的默认 Handler

logger.setUseParentHandlers(false);

(2)添加控制台 Handler

ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.ALL);

logger.addHandler(consoleHandler);
logger.setLevel(Level.ALL);

(3)添加文件输出

FileHandler fileHandler = new FileHandler("app.log", true);
fileHandler.setFormatter(new SimpleFormatter());

logger.addHandler(fileHandler);

🎨 自定义 Formatter(美化日志)

默认格式不太好看,可以自定义:

public class MyFormatter extends Formatter {
    @Override
    public String format(LogRecord record) {
        return String.format(
            "[%1$tF %1$tT] [%2$-7s] %3$s - %4$s%n",
            record.getMillis(),
            record.getLevel().getName(),
            record.getLoggerName(),
            record.getMessage()
        );
    }
}

使用它:

ConsoleHandler handler = new ConsoleHandler();
handler.setFormatter(new MyFormatter());
logger.addHandler(handler);

输出示例:

[2025-11-16 10:15:33] [INFO   ] com.example.Main - Started

这已经很接近 Log4j / SLF4J 的格式。

🚧 自定义 Handler(进阶玩法)

例如把日志写到数据库、MQ、ElasticSearch。

public class MyDbHandler extends Handler {

    @Override
    public void publish(LogRecord record) {
        String msg = getFormatter().format(record);
        // 写入数据库
    }

    @Override
    public void flush() {}

    @Override
    public void close() throws SecurityException {}
}

使用:

MyDbHandler handler = new MyDbHandler();
handler.setFormatter(new MyFormatter());
logger.addHandler(handler);

🔍 使用 Filter(按条件过滤日志)

例如只输出包含 “ORDER” 的日志:

public class OrderFilter implements Filter {
    @Override
    public boolean isLoggable(LogRecord record) {
        return record.getMessage().contains("ORDER");
    }
}

挂载过滤器:

handler.setFilter(new OrderFilter());

🧬 使用 LogManager 手动加载配置

如果你想运行时动态加载 logging.properties:

try (InputStream is = new FileInputStream("logging.properties")) {
    LogManager.getLogManager().readConfiguration(is);
} catch (Exception e) {
    e.printStackTrace();
}

应用场景:

  • Web 容器
  • 自写框架
  • 动态切换日志级别
  • 集中式日志管理

🪝 Logger 继承体系

JUL 的 Logger 是分层级的(基于命名空间)。

Logger parent = Logger.getLogger("com");
Logger child = Logger.getLogger("com.example");

子 logger 默认继承父的:

  • handler
  • level(如果未显式设置)

如果你想让 child 独立:

child.setUseParentHandlers(false);

⚡ JUL vs Log4j vs SLF4J(补充)

特性 JUL Log4j SLF4J
标准库
配置灵活度 一般 取决于 Logging 桥
功能丰富度 一般 取决于实现
Formatter 简单 丰富 取决于实现
性能 中等

现代 Java 项目常用:

SLF4J + Logback

Jakarta Logging + Log4j2

但 JUL 内置、无需依赖,仍适合:

  • 小工具程序
  • Java Agent
  • 嵌入式场景
  • JDK 自带库

🧪 高级玩法示例:只输出某个包的 DEBUG

# 全局 INFO
.level = INFO

# 单独调低某个包的日志级别
com.example.payment.level = FINE

然后代码里:

logger.fine("debug data");

全局不会显示,但 payment 包会显示,非常适合定位问题。

🏆 最佳实践总结

场景 方案
程序小、无外部依赖 直接用 JUL
自定义格式 自定义 Formatter
程序必须运行中调整日志级别 使用 LogManager 动态加载
输出到数据库/ES/MQ 自定义 Handler
分模块调试 使用 Logger 层级
替换默认输出 setUseParentHandlers(false)

JUL 从基础配置到高级玩法
https://liuyuhe666.github.io/2025/11/20/JUL-从基础配置到高级玩法/
作者
Liu Yuhe
发布于
2025年11月20日
许可协议