在 Java 中,java.util.logging.Logger 是一个用于记录日志信息的重要工具。以下是对 Java Logger 的详细介绍:
Logger 用于在应用程序中生成不同级别的日志消息,以帮助开发人员和管理员了解应用程序的运行状态、诊断问题和跟踪事件。它提供了一种灵活的方式来控制日志的输出,包括日志级别、输出目的地和格式等。
以下是一个简单的 Logger 示例:
package com.hxstrive.jdk7.logger; import java.util.logging.Logger; /** * Logger 示例 * @author hxstrive.com */ public class LoggerDemo { private static final Logger logger = Logger.getLogger(LoggerDemo.class.getName()); public static void main(String[] args) { logger.info("输出日志信息"); } }
运行示例,输出如下:
8月 15, 2024 9:50:55 上午 com.hxstrive.jdk7.logger.LoggerDemo main 信息: 输出日志信息
在这个例子中,通过 Logger.getLogger() 方法获取了一个与当前类名相关联的 Logger,但实际上可以根据需要自定义一个全局的名称来获取全局的 Logger。
但是,推荐使用类名作为日志记录器(Logger)的名称,因为有以下几个好处:
(1)明确日志来源
易于定位:当查看日志时,通过类名可以快速确定日志消息的产生位置。这对于大型项目尤其重要,因为在复杂的系统中,可能有多个不同的模块和类在生成日志。如果没有明确的来源标识,很难确定问题到底出在哪个具体的部分。
问题排查:当出现错误或异常时,知道日志消息来自哪个类可以帮助开发人员更有针对性地进行问题排查。例如,如果在日志中看到一个错误消息,并且知道它来自特定的类,开发人员可以直接检查该类的代码以查找问题。
(2)方便分类管理
模块区分:对于大型项目,不同的模块通常由不同的团队或开发人员负责。使用类名作为日志名称可以自然地将日志按照模块进行分类。这样,各个团队可以更方便地关注自己负责的模块的日志,提高问题排查和维护的效率。
日志级别设置:可以根据不同的类或模块设置不同的日志级别。例如,对于核心业务模块,可以设置较低的日志级别以获取更多的详细信息,而对于一些辅助模块,可以设置较高的日志级别以减少日志输出量。
(3)提高可维护性
代码重构:如果在项目的生命周期中进行代码重构,使用类名作为日志名称可以更容易地跟踪日志的来源。即使类的名称或结构发生了变化,只要日志记录器的名称仍然基于类名,就可以相对容易地找到对应的日志消息。
团队协作:在团队开发中,大家都遵循使用类名作为日志名称的规范,可以提高代码的可读性和可维护性。新加入的开发人员也可以更容易地理解日志的结构和来源,从而更快地融入项目开发中。
(4)与其他工具集成
日志分析工具:许多日志分析工具可以根据日志的来源进行分类和统计。使用类名作为日志名称可以更好地与这些工具集成,使分析和监控更加方便。
错误跟踪系统:一些错误跟踪系统也可以利用日志中的类名信息来关联错误和具体的代码位置。这有助于更快速地响应和解决问题。
Logger 支持不同的日志级别,包括:
SEVERE(严重错误)
WARNING(警告)
INFO(信息)
CONFIG(配置信息)
FINE(一般调试信息)
FINER(更详细的调试信息)
FINEST(最详细的调试信息)
用户可以根据应用程序的需求设置不同的日志级别,以控制哪些日志消息会被输出。例如,在生产环境中可以设置为较高的级别(如:WARNING),只输出重要的信息和错误消息,而在开发和调试阶段可以设置为较低的级别(如:FINE),输出更多的详细信息。
Logger 可以通过配置文件(如 logging.properties)进行配置,也可以在代码中进行动态配置。
配置文件可以设置全局的日志级别、日志输出目的地(如文件、控制台)、日志格式等。在代码中,可以通过 Logger 类的方法来设置特定 Logger 的级别和添加处理程序。
例如,在项目 resources 目录下创建一个名为 logging.properties 的文件,来设置各种属性来控制日志的行为:
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler .level= INFO java.util.logging.FileHandler.level = INFO java.util.logging.FileHandler.pattern = myapp.log java.util.logging.FileHandler.append = true java.util.logging.ConsoleHandler.level = INFO java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
配置说明:
handlers 指定了日志的处理程序,这里同时使用了文件处理程序和控制台处理程序。
.level 设置了全局的日志级别为 INFO,表示只有 INFO 级别及以上的日志会被输出。
java.util.logging.FileHandler.level 设置了文件处理程序的日志级别。
java.util.logging.FileHandler.pattern 指定了日志文件的名称。
java.util.logging.FileHandler.append 表示是否追加到现有日志文件中。
java.util.logging.ConsoleHandler.level 设置了控制台处理程序的日志级别。
java.util.logging.ConsoleHandler.formatter 指定了日志的输出格式为简单格式。
Java 中有以下几种方式可以加载 logging.properties 文件:
(1)使用系统属性:在启动 Java 应用程序时,可以通过设置系统属性来指定配置文件的位置。例如:
java -Djava.util.logging.config.file=path/to/logging.properties MyApp
其中,path/to/logging.properties 是配置文件的实际路径,MyApp 是你的应用程序主类名。
(2)在代码中加载:可以在代码中使用 LogManager 来加载配置文件。例如:
import java.util.logging.LogManager; public class MyApp { static { try { LogManager.getLogManager().readConfiguration(MyApp.class.getResourceAsStream("/logging.properties")); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { // 你的应用程序代码 } }
在这个例子中,假设 logging.properties 文件在类路径的根目录下(如果是 maven 项目,则位于 src/main/resources 目录下),通过 getResourceAsStream() 方法获取配置文件的输入流,然后加载到日志管理器中。
可以为不同的模块或功能创建不同的 Logger,以便更好地组织和管理日志消息。每个 Logger 都有一个名称,通过名称可以区分不同的日志源。
例如,可以为一个 Web 应用程序的不同部分(如用户管理模块、订单处理模块等)创建不同的 Logger,这样在查看日志时可以更容易地定位问题所在的模块。
Logger 可以添加多个处理程序,每个处理程序负责将日志消息输出到不同的目的地,如文件、控制台、网络等。
处理程序可以使用不同的格式化器来控制日志消息的输出格式。默认情况下,java.util.logging 提供了一些简单的格式化器,但也可以自定义格式化器以满足特定的需求。
获取 Logger:
使用 Logger.getLogger(String name) 方法获取一个 Logger 实例,其中 name 是 Logger 的名称。通常使用类名作为 Logger 的名称,以便更好地跟踪日志消息的来源。例如:
Logger logger = Logger.getLogger(MyClass.class.getName());
记录日志消息:
Logger 提供了多个方法来记录不同级别的日志消息,如:
logger.severe(String message) logger.warning(String message) logger.info(String message)
例如:记录一条级别为 INFO 的日志信息:
logger.info("This is an informational message.");
设置日志级别:
可以使用 logger.setLevel(Level level) 方法设置 Logger 的日志级别。只有级别等于或高于设置的级别才会被输出。例如:
logger.setLevel(Level.INFO);
添加处理程序:
可以使用 logger.addHandler(Handler handler) 方法为 Logger 添加处理程序。处理程序负责将日志消息输出到特定的目的地。例如:
FileHandler fileHandler = new FileHandler("myapp.log"); logger.addHandler(fileHandler);
使用 src\main\resources\logging.properties 配置文件配置日志级别等,并通过 JVM 参数指定日志配置文件的位置。如下:
logging.properties:
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler .level= FINEST java.util.logging.FileHandler.level = FINEST java.util.logging.FileHandler.pattern = myapp.log java.util.logging.FileHandler.append = true java.util.logging.ConsoleHandler.level = FINEST java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
Java 代码:
package com.hxstrive.jdk7.logger; import java.util.logging.Logger; /** * Logger 示例 * @author hxstrive.com */ public class LoggerDemo1 { private static final Logger logger = Logger.getLogger(LoggerDemo1.class.getName()); public static void main(String[] args) { //SEVERE(严重错误) logger.severe("严重错误"); //WARNING(警告) logger.warning("警告"); //INFO(信息) logger.info("信息"); //CONFIG(配置信息) logger.config("配置信息"); //FINE(一般调试信息) logger.fine("一般调试信息"); //FINER(更详细的调试信息) logger.finer("更详细的调试信息"); //FINEST(最详细的调试信息) logger.finest("最详细的调试信息"); } }
执行如下命令:
java.exe -Djava.util.logging.config.file=E:\demo_jdk7\src\main\resources\logging.properties -classpath E:\demo_jdk7\target\classes com.hxstrive.jdk7.logger.LoggerDemo1
输出日志信息:
8月 15, 2024 9:28:25 上午 com.hxstrive.jdk7.logger.LoggerDemo1 main 严重: 严重错误 8月 15, 2024 9:28:25 上午 com.hxstrive.jdk7.logger.LoggerDemo1 main 警告: 警告 8月 15, 2024 9:28:25 上午 com.hxstrive.jdk7.logger.LoggerDemo1 main 信息: 信息 8月 15, 2024 9:28:25 上午 com.hxstrive.jdk7.logger.LoggerDemo1 main 配置: 配置信息 8月 15, 2024 9:28:25 上午 com.hxstrive.jdk7.logger.LoggerDemo1 main 详细: 一般调试信息 8月 15, 2024 9:28:25 上午 com.hxstrive.jdk7.logger.LoggerDemo1 main 较详细: 更详细的调试信息 8月 15, 2024 9:28:25 上午 com.hxstrive.jdk7.logger.LoggerDemo1 main 非常详细: 最详细的调试信息
使用 src\main\resources\logging.properties 配置文件配置日志级别等,并使用 LogManager.getLogManager().readConfiguration(InputStream in) 方法加载配置文件。如下:
logging.properties:
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler .level= FINEST java.util.logging.FileHandler.level = FINEST java.util.logging.FileHandler.pattern = myapp.log java.util.logging.FileHandler.append = true java.util.logging.ConsoleHandler.level = FINEST java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
Java 代码:
package com.hxstrive.jdk7.logger; import java.util.logging.LogManager; import java.util.logging.Logger; /** * Logger 示例 * @author hxstrive.com */ public class LoggerDemo2 { private static final Logger logger = Logger.getLogger(LoggerDemo2.class.getName()); static { try { LogManager.getLogManager().readConfiguration(LoggerDemo2.class.getResourceAsStream("/logging.properties")); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { //SEVERE(严重错误) logger.severe("严重错误"); //WARNING(警告) logger.warning("警告"); //INFO(信息) logger.info("信息"); //CONFIG(配置信息) logger.config("配置信息"); //FINE(一般调试信息) logger.fine("一般调试信息"); //FINER(更详细的调试信息) logger.finer("更详细的调试信息"); //FINEST(最详细的调试信息) logger.finest("最详细的调试信息"); } }
执行如下命令:
java.exe -Djava.util.logging.config.file=E:\demo_jdk7\src\main\resources\logging.properties -classpath E:\demo_jdk7\target\classes com.hxstrive.jdk7.logger.LoggerDemo2
输出日志信息:
8月 15, 2024 9:31:17 上午 com.hxstrive.jdk7.logger.LoggerDemo2 main 严重: 严重错误 8月 15, 2024 9:31:17 上午 com.hxstrive.jdk7.logger.LoggerDemo2 main 警告: 警告 8月 15, 2024 9:31:17 上午 com.hxstrive.jdk7.logger.LoggerDemo2 main 信息: 信息 8月 15, 2024 9:31:17 上午 com.hxstrive.jdk7.logger.LoggerDemo2 main 配置: 配置信息 8月 15, 2024 9:31:17 上午 com.hxstrive.jdk7.logger.LoggerDemo2 main 详细: 一般调试信息 8月 15, 2024 9:31:17 上午 com.hxstrive.jdk7.logger.LoggerDemo2 main 较详细: 更详细的调试信息 8月 15, 2024 9:31:17 上午 com.hxstrive.jdk7.logger.LoggerDemo2 main 非常详细: 最详细的调试信息
为了鼓励在一些简单的程序中使用日志框架,Logger 类现在提供了一个全局的 Logger 实例。因为它为了尽可能地简化使用,所以你可以在任何时候都使用
Logger.global.finest("x=" + x);
来代替
System.out.println("x=" + x);
遗憾的是,在 Java6 中不建议再使用 Logger.global,已经被标记为过时(已废弃)。
该字段的初始化容易造成死锁。该字段必须由日志记录器类初始化进行初始化,这可能会导致日志管理器类初始化出现死锁。在这种情况下,两个类的初始化会互相等待完成。获取全局日志记录器对象的首选方法是调用 Logger.getGlobal()。在旧版本的 JDK 中,Logger.getGlobal() 不可用,为了与旧版本兼容,请调用 Logger.getLogger(Logger.GLOBAL_LOGGER_NAME) 或 Logger.getLogger("global")。
而调用 Logger.getLogger(Logger.GLOBAL_LOGGER_NAME) 或 Logger.getLogger("global") 又比较麻烦。为此,Java7 中提供了一种更简单的形式:
Logger.getGlobal(),例如:
package com.hxstrive.jdk7.logger; import java.util.logging.LogManager; import java.util.logging.Logger; /** * Logger 示例 * @author hxstrive.com */ public class LoggerDemo5 { static { try { LogManager.getLogManager().readConfiguration(LoggerDemo5.class.getResourceAsStream("/logging.properties")); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { Logger.getGlobal().severe("Hello World"); Logger.getGlobal().warning("Hello World"); Logger.getGlobal().info("Hello World"); } }
运行示例,输出如下:
8月 15, 2024 10:26:49 上午 com.hxstrive.jdk7.logger.LoggerDemo5 main 严重: Hello World 8月 15, 2024 10:26:49 上午 com.hxstrive.jdk7.logger.LoggerDemo5 main 警告: Hello World 8月 15, 2024 10:26:49 上午 com.hxstrive.jdk7.logger.LoggerDemo5 main 信息: Hello World
上例中的,Logger.getGlobal() 其实就是 Logger.getLogger("global"),笔者还是推荐使用这种方式:
private static final Logger logger = Logger.getLogger(LoggerDemo2.class.getName());