ConsoleHandler是Java JUL中将日志输出到控制台(默认System.err)的处理器,而print()仅简单输出到标准输出;前者支持级别控制、格式化、多目标输出和动态配置,后者无级别、无格式、不可配置。

ConsoleHandler 是什么,它和 print() 有什么区别
ConsoleHandler 是 Java java.util.logging(JUL)框架中的一个具体日志处理器,负责把日志记录(LogRecord)格式化后输出到 System.err(默认)或指定的 OutputStream。它不是简单的 print() 或 System.out.println() —— 它参与完整的日志级别控制、格式化、父处理器继承链,且可被 Logger 动态添加/移除。
常见错误是直接 new 一个 ConsoleHandler 就以为万事大吉,结果没看到输出:因为默认构造器创建的 handler 没有设置级别,而 JUL 的 Logger 和 Handler 各自都有级别阈值,两者都需满足才会输出。
如何让 ConsoleHandler 真正生效并输出日志
关键在于三处显式设置:Logger 级别、Handler 级别、Handler 的 Formatter(否则可能输出为空或只有时间戳)。
- 调用
logger.setLevel(Level.ALL)或至少不低于你后续logger.info()的级别 - 对
ConsoleHandler实例调用setLevel(Level.ALL)(默认是Level.INFO,但若 Logger 是WARNING,仍不触发) - 建议显式设置
handler.setFormatter(new SimpleFormatter()),否则可能沿用平台默认 formatter,某些 JDK 版本下输出不可读 - 必须通过
logger.addHandler(handler)注册,仅 new 出来不注册无效
示例片段:
Logger logger = Logger.getLogger("MyApp");
logger.setLevel(Level.ALL);
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.ALL);
handler.setFormatter(new SimpleFormatter());
logger.addHandler(handler);
logger.info("Hello from ConsoleHandler"); // 这行才会输出
为什么有时候 ConsoleHandler 输出到了 System.err 而不是 System.out
ConsoleHandler 默认使用 System.err 作为输出流,这是设计使然 —— 日志通常被视为“诊断信息”,JUL 认为它更接近错误流语义。这会导致在 IDE 控制台中颜色偏红,或在 shell 中无法用 2>/dev/null 单独屏蔽日志(因为它混在 stderr 里)。
- 若需输出到
System.out,得用带参构造器:new ConsoleHandler(new OutputStreamWriter(System.out)) - 注意:JDK 9+ 的
ConsoleHandler(OutputStream)构造器是protected,必须继承后重写或改用反射(不推荐)。更稳妥的方式是用StreamHandler替代:new StreamHandler(System.out, new SimpleFormatter()) - 修改输出流不影响日志级别和格式化逻辑,只改变目的地
和 Log4j / SLF4J 的 ConsoleAppender 比较时容易忽略的点
JUL 的 ConsoleHandler 没有内置异步能力、无自动滚动、不支持 pattern layout(靠 Formatter 子类实现),且它的配置优先级依赖于代码顺序而非外部配置文件。
- 它不会自动读取
logging.properties文件里的handlers=java.util.logging.ConsoleHandler配置,除非你显式调用LogManager.getLogManager().readConfiguration() - 多个
ConsoleHandler实例同时注册到同一个Logger,会重复输出 —— JUL 不做去重,需自行管理 handler 生命周期 - 如果应用用了 SLF4J + JUL 绑定(
slf4j-jdk14),那最终走的还是ConsoleHandler,但级别映射有转换规则(如 SLF4J 的debug→ JUL 的FINER),容易因级别不匹配导致“明明写了 debug 却看不到”
真正麻烦的从来不是加一行 addHandler,而是确认当前 Logger 的 parent 是否也启用了 handler、是否被 LogManager 全局配置覆盖、以及 formatter 是否真的生成了可见字符串。










