Java中如何通过Logger实现不同日志级别记录系统运行信息?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1006个文字,预计阅读时间需要5分钟。
Java 自带的日志记录器 `java.util.logging.Logger` 默认只输出 `INFO` 及以上级别(即 `WARNING`、`SEVERE`)的消息。默认情况下,`FINE`、`FINER`、`FINEST` 级别的日志会被过滤掉——这并非 bug,而是默认的 `Level` 配置限制。
要记录 `FINE` 及以上级别的日志,并确保日志可以被输出,必须显式设置 `Handler` 的级别,同时确保 `Logger` 本身也允许该级别的日志输出。
常见误区是只调用 logger.setLevel(Level.FINE) 却没改 ConsoleHandler 的级别,结果日志依然不输出。
-
SEVERE:严重错误,应用可能已不可用(如空指针导致核心流程中断) -
WARNING:潜在问题,但未中断执行(如配置项缺失,使用默认值) -
INFO:关键业务节点(如“订单创建成功”,“缓存刷新完成”) -
FINE:常规调试信息(如方法入参、SQL 执行前拼接结果) -
FINER:更细粒度追踪(如循环内每次迭代的中间状态) -
FINEST:高频/低价值细节(如网络请求每个 header 的收发)
如何让 FINE 级日志真正打印到控制台
必须同时设置 Logger 实例和它的 Handler(比如 ConsoleHandler)的级别,否则 Handler 会拦截掉低级别日志。
Logger logger = Logger.getLogger("MyApp"); logger.setLevel(Level.ALL); // 允许所有级别通过 Logger ConsoleHandler handler = new ConsoleHandler(); handler.setLevel(Level.ALL); // 关键:Handler 也要放开 handler.setFormatter(new SimpleFormatter()); // 可选,避免 XML 格式干扰阅读 logger.addHandler(handler);
如果使用 IDE 运行,注意部分环境(如 IntelliJ 的 JUnit 模式)会重定向 System.out,导致 ConsoleHandler 不可见——此时建议改用 FileHandler 或切换为 SLF4J + Logback。
立即学习“Java免费学习笔记(深入)”;
log.info() 和 log.log(Level.INFO, ...) 的区别
表面上等价,但参数处理逻辑不同:log.info(String) 是快捷方法,而 log.log(Level.INFO, String, Object) 支持延迟求值和异常堆栈自动附加。
- 用
log.info("User {0} logged in", userId)时,若日志被禁用,userId.toString()不会执行 —— 这是性能优势 - 用
log.log(Level.WARNING, "DB timeout", ex)会自动把ex的 stack trace 输出在日志末尾;而log.warning("DB timeout: " + ex)只打印toString(),丢失堆栈 - 不要混用占位符风格和字符串拼接:
log.info("id=" + id + ", name=" + name)总是触发对象转换,无条件消耗 CPU
生产环境为什么不该直接用 JUL(java.util.logging)
JUL 的 Handler 是全局共享的,默认 ConsoleHandler 和 FileHandler 都是单例,多个 Logger.addHander() 会导致日志重复输出;且配置只能通过 logging.properties 文件或硬编码,无法按包动态调权。
真实项目中,几乎都用桥接方案:
- 引入
slf4j-jdk14把 SLF4J 日志转给 JUL(兼容遗留系统) - 更常见的是用
slf4j-api+logback-classic,通过logback.xml精确控制每个包的日志级别、输出格式、滚动策略 - Spring Boot 默认集成 Logback,
application.properties中写logging.level.com.example.service=DEBUG就生效,比 JUL 的 properties 文件直观得多
如果你正在维护一个老系统且不能引入新依赖,那 JUL 能用,但得小心 Handler 复用和级别穿透问题——这点最容易在多模块共用同一个 Logger 名称时暴露出来。
本文共计1006个文字,预计阅读时间需要5分钟。
Java 自带的日志记录器 `java.util.logging.Logger` 默认只输出 `INFO` 及以上级别(即 `WARNING`、`SEVERE`)的消息。默认情况下,`FINE`、`FINER`、`FINEST` 级别的日志会被过滤掉——这并非 bug,而是默认的 `Level` 配置限制。
要记录 `FINE` 及以上级别的日志,并确保日志可以被输出,必须显式设置 `Handler` 的级别,同时确保 `Logger` 本身也允许该级别的日志输出。
常见误区是只调用 logger.setLevel(Level.FINE) 却没改 ConsoleHandler 的级别,结果日志依然不输出。
-
SEVERE:严重错误,应用可能已不可用(如空指针导致核心流程中断) -
WARNING:潜在问题,但未中断执行(如配置项缺失,使用默认值) -
INFO:关键业务节点(如“订单创建成功”,“缓存刷新完成”) -
FINE:常规调试信息(如方法入参、SQL 执行前拼接结果) -
FINER:更细粒度追踪(如循环内每次迭代的中间状态) -
FINEST:高频/低价值细节(如网络请求每个 header 的收发)
如何让 FINE 级日志真正打印到控制台
必须同时设置 Logger 实例和它的 Handler(比如 ConsoleHandler)的级别,否则 Handler 会拦截掉低级别日志。
Logger logger = Logger.getLogger("MyApp"); logger.setLevel(Level.ALL); // 允许所有级别通过 Logger ConsoleHandler handler = new ConsoleHandler(); handler.setLevel(Level.ALL); // 关键:Handler 也要放开 handler.setFormatter(new SimpleFormatter()); // 可选,避免 XML 格式干扰阅读 logger.addHandler(handler);
如果使用 IDE 运行,注意部分环境(如 IntelliJ 的 JUnit 模式)会重定向 System.out,导致 ConsoleHandler 不可见——此时建议改用 FileHandler 或切换为 SLF4J + Logback。
立即学习“Java免费学习笔记(深入)”;
log.info() 和 log.log(Level.INFO, ...) 的区别
表面上等价,但参数处理逻辑不同:log.info(String) 是快捷方法,而 log.log(Level.INFO, String, Object) 支持延迟求值和异常堆栈自动附加。
- 用
log.info("User {0} logged in", userId)时,若日志被禁用,userId.toString()不会执行 —— 这是性能优势 - 用
log.log(Level.WARNING, "DB timeout", ex)会自动把ex的 stack trace 输出在日志末尾;而log.warning("DB timeout: " + ex)只打印toString(),丢失堆栈 - 不要混用占位符风格和字符串拼接:
log.info("id=" + id + ", name=" + name)总是触发对象转换,无条件消耗 CPU
生产环境为什么不该直接用 JUL(java.util.logging)
JUL 的 Handler 是全局共享的,默认 ConsoleHandler 和 FileHandler 都是单例,多个 Logger.addHander() 会导致日志重复输出;且配置只能通过 logging.properties 文件或硬编码,无法按包动态调权。
真实项目中,几乎都用桥接方案:
- 引入
slf4j-jdk14把 SLF4J 日志转给 JUL(兼容遗留系统) - 更常见的是用
slf4j-api+logback-classic,通过logback.xml精确控制每个包的日志级别、输出格式、滚动策略 - Spring Boot 默认集成 Logback,
application.properties中写logging.level.com.example.service=DEBUG就生效,比 JUL 的 properties 文件直观得多
如果你正在维护一个老系统且不能引入新依赖,那 JUL 能用,但得小心 Handler 复用和级别穿透问题——这点最容易在多模块共用同一个 Logger 名称时暴露出来。

