Java中Logger需通过getLogger()获取实例,不可new;须配置Handler和Level才能输出日志;推荐用类名命名并启用占位符避免无效拼接;注意父子级继承导致的重复输出问题。

Java里用Logger类记录日志,得先获取实例,不能直接new
Java标准库的java.util.logging.Logger是单例管理的,调用Logger.getLogger(String name)才能拿到有效实例。直接new Logger()会报java.lang.InstantiationException——因为Logger是抽象类,且构造函数是protected。
- 推荐用类名作为logger名称:
Logger.getLogger(MyClass.class.getName()),便于追踪来源 - 名称带点号(如
"com.example.service.UserService")会自动形成层级关系,影响日志级别继承 - 同一名称多次调用
getLogger()返回的是同一个实例,线程安全
Logger默认不输出日志,必须配置Handler和Level
刚拿到的Logger对象默认只继承root logger的配置,而JDK默认的root logger只把INFO及以上日志输出到ConsoleHandler,但很多环境(如IDE或容器)会屏蔽控制台输出,导致“写了日志却看不到”。
- 显式添加
ConsoleHandler:Logger logger = Logger.getLogger(MyClass.class.getName()); logger.addHandler(new ConsoleHandler());
- 设置日志级别(否则低于
INFO的日志会被过滤):logger.setLevel(Level.FINE); - 注意:
ConsoleHandler默认格式简陋,可配SimpleFormatter或自定义Formatter - 若用
FileHandler,路径需存在且有写权限,否则抛IOException
log(Level, String)和log(Level, String, Object)参数差异很关键
看似只是多一个参数,实际影响日志内容生成时机和性能:
-
log(Level.INFO, "User " + userId + " logged in"):字符串拼接在调用前就执行,无论日志是否启用都会消耗CPU和内存 -
log(Level.INFO, "User {0} logged in", userId):占位符模式,只有当前Logger实际启用该级别时才解析参数,更高效 - 支持多个参数:
log(Level.WARNING, "Failed to process {0}, retry={1}", new Object[]{fileName, retryCount}) - 异常必须用第三个参数传:
log(Level.SEVERE, "DB connection failed", ex),否则堆栈不会打印
别忽略Logger的层级继承与父级干扰
每个Logger都有父Logger(按名称点号分割向上找),默认开启setUseParentHandlers(true),意味着你的日志可能被父级(甚至root)重复输出两次。
立即学习“Java免费学习笔记(深入)”;
- 常见现象:一条
INFO日志在控制台出现两遍——大概率是子Logger没关父级处理器 - 关闭继承:
logger.setUseParentHandlers(false),再单独配自己的Handler - 修改
root logger级别会影响所有未显式设级的Logger,调试时可临时执行:Logger.getLogger("").setLevel(Level.ALL); - 层级关系也影响
Filter和Handler的生效范围,不是孤立配置
Logger足够轻量,但层级逻辑、默认行为和参数延迟求值这些细节,稍不注意就会让日志“静默消失”或“重复刷屏”。尤其在模块化项目里,不同jar包用相同命名空间的logger,父子继承容易串味。










