JUL 从基础配置到高级玩法
🎯 JUL 的总体架构
JUL 的核心组件:
- Logger:记录日志的入口
- Handler:日志输出目标(控制台、文件等)
- Formatter:日志格式
- Level:日志级别
- Filter:过滤日志记录
- LogManager:管理全局配置(读取 logging.properties)
整体日志流:
Logger → Filter → Handler → Filter → 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-从基础配置到高级玩法/