Java 记录器使用
Java Logger usage
我使用 java.util.logging
:
为我的项目制作了一个自定义记录器
public class SpotifyLogger {
private static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
public SpotifyLogger(String loggerFilePath) throws IOException {
Logger myLogger = Logger.getLogger("");
// suppress console messaging
Handler[] handlers = myLogger.getHandlers();
if (handlers[0] instanceof ConsoleHandler) { //exception occurs here
myLogger.removeHandler(handlers[0]);
}
// set level
LOGGER.setLevel(Level.SEVERE);
// create a txt handler
FileHandler textFileHandler = new FileHandler(loggerFilePath);
SimpleFormatter simpleFormatter = new SimpleFormatter();
textFileHandler.setFormatter(simpleFormatter);
LOGGER.addHandler(textFileHandler);
}
public void log(String user, Exception e) {
LOGGER.log(Level.SEVERE, user, e);
}
}
对于程序的客户端和服务器部分,我创建了两个单独的 Logger 对象:
// class member initialized as null, because of exception handling
private SpotifyLogger logger = null;
//...
//in constructor:
this.logger = new SpotifyLogger(LOGGER_FILE_NAME); // the LOGGER_FILE_NAME is different for the client and the server
当我手动测试我的程序时,记录器似乎可以工作(两个日志文件包含我造成的异常)。然后,我编写了自动测试。对于我正在测试的每个 class(总共 5 个),我创建了一个具有不同目标路径的单独记录器对象。测试(以先出现的 class 为准)工作正常。所有其他测试都失败了,因为当我为那个特定 class 初始化记录器时,我得到一个 ArrayIndexOutOfBoundsException
。原因是我试图访问 handlers[0]
,而 handlers
的长度为 0。根据我在网上搜索后的理解,这是因为记录器正在使用父处理程序。我试过这个:
public SpotifyLogger(String loggerFilePath) throws IOException {
Logger myLogger = Logger.getLogger("");
// suppress console messaging
myLogger.setUseParentHandlers(false);
Handler[] handlers = myLogger.getHandlers();
if (handlers.length > 0) {
if (handlers[0] instanceof ConsoleHandler) {
myLogger.removeHandler(handlers[0]);
}
}
//etc
}
我没有再遇到异常,但日志记录不起作用。我做错了什么?
这对我来说意义不大
Logger myLogger = Logger.getLogger("")
如果您想要不同的 Logger
,您需要为每个提供不同的名称。因此,您的这一行代码(在 SpotifyLogger
构造函数中)始终 returns 相同 Logger
.
Logger myLogger = Logger.getLogger("");
这实际上 returns java.util.logging.LogManager.RootLogger
有一个 Handler
,它是 ConsoleLogger
的一个实例。随后,您在 SpotifyLogger
构造函数的第一次调用中删除了 Handler
,因此在每次后续调用中,方法 getHandlers
returns 一个空数组。
由于您只将 Handler
添加到全局 Logger
,因此每次调用 SpotifyLogger
构造函数时都会将另一个 FileHandler
添加到全局记录器。我尚未验证,但我相信 Logger
将使用方法 getHandlers
返回的数组中第一个适当的 Handler
,因此您看到的行为只有第一个日志文件是正在写入,即您传递给 SpotifyLogger
构造函数的第一次调用的文件。
请注意,您没有提供 reproducible example,因此我无法根据您的上下文验证上述任何内容。我只是测试了你问题中的代码才能得出上述结果。
考虑以下对 class SpotifyLogger
的重写——包括仅用于测试目的的 main
方法。
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class SpotifyLogger {
private static final String[] NAMES = {"First", "Second"};
private static int count;
private int index;
public SpotifyLogger(String loggerFilePath) throws IOException {
index = count;
Logger myLogger = Logger.getLogger(NAMES[count++]);
myLogger.setUseParentHandlers(false);
// set level
myLogger.setLevel(Level.SEVERE);
// create a txt handler
FileHandler textFileHandler = new FileHandler(loggerFilePath);
SimpleFormatter simpleFormatter = new SimpleFormatter();
textFileHandler.setFormatter(simpleFormatter);
myLogger.addHandler(textFileHandler);
}
public void log(String user, Exception e) {
Logger myLogger = Logger.getLogger(NAMES[index]);
myLogger.log(Level.SEVERE, user, e);
}
public static void main(String[] args) {
try {
SpotifyLogger x = new SpotifyLogger("spotifyx.log");
SpotifyLogger y = new SpotifyLogger("spotifyy.log");
x.log("George", new Exception());
y.log("Martha", new RuntimeException());
}
catch (IOException x) {
x.printStackTrace();
}
}
}
请注意,您对父 Handler
的看法是正确的,因此上面代码中的以下行:
myLogger.setUseParentHandlers(false);
经过运行以上代码,文件spotifyx.log的内容为:
Feb 12, 2022 2:12:28 PM javalogp.SpotifyLogger log
SEVERE: George
java.lang.Exception
at javalogp/javalogp.SpotifyLogger.main(SpotifyLogger.java:38)
文件spotifyy.log的内容是:
Feb 12, 2022 2:12:28 PM javalogp.SpotifyLogger log
SEVERE: Martha
java.lang.RuntimeException
at javalogp/javalogp.SpotifyLogger.main(SpotifyLogger.java:39)
并且没有 日志消息被写入控制台。
我使用 java.util.logging
:
public class SpotifyLogger {
private static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
public SpotifyLogger(String loggerFilePath) throws IOException {
Logger myLogger = Logger.getLogger("");
// suppress console messaging
Handler[] handlers = myLogger.getHandlers();
if (handlers[0] instanceof ConsoleHandler) { //exception occurs here
myLogger.removeHandler(handlers[0]);
}
// set level
LOGGER.setLevel(Level.SEVERE);
// create a txt handler
FileHandler textFileHandler = new FileHandler(loggerFilePath);
SimpleFormatter simpleFormatter = new SimpleFormatter();
textFileHandler.setFormatter(simpleFormatter);
LOGGER.addHandler(textFileHandler);
}
public void log(String user, Exception e) {
LOGGER.log(Level.SEVERE, user, e);
}
}
对于程序的客户端和服务器部分,我创建了两个单独的 Logger 对象:
// class member initialized as null, because of exception handling
private SpotifyLogger logger = null;
//...
//in constructor:
this.logger = new SpotifyLogger(LOGGER_FILE_NAME); // the LOGGER_FILE_NAME is different for the client and the server
当我手动测试我的程序时,记录器似乎可以工作(两个日志文件包含我造成的异常)。然后,我编写了自动测试。对于我正在测试的每个 class(总共 5 个),我创建了一个具有不同目标路径的单独记录器对象。测试(以先出现的 class 为准)工作正常。所有其他测试都失败了,因为当我为那个特定 class 初始化记录器时,我得到一个 ArrayIndexOutOfBoundsException
。原因是我试图访问 handlers[0]
,而 handlers
的长度为 0。根据我在网上搜索后的理解,这是因为记录器正在使用父处理程序。我试过这个:
public SpotifyLogger(String loggerFilePath) throws IOException {
Logger myLogger = Logger.getLogger("");
// suppress console messaging
myLogger.setUseParentHandlers(false);
Handler[] handlers = myLogger.getHandlers();
if (handlers.length > 0) {
if (handlers[0] instanceof ConsoleHandler) {
myLogger.removeHandler(handlers[0]);
}
}
//etc
}
我没有再遇到异常,但日志记录不起作用。我做错了什么?
这对我来说意义不大
Logger myLogger = Logger.getLogger("")
如果您想要不同的 Logger
,您需要为每个提供不同的名称。因此,您的这一行代码(在 SpotifyLogger
构造函数中)始终 returns 相同 Logger
.
Logger myLogger = Logger.getLogger("");
这实际上 returns java.util.logging.LogManager.RootLogger
有一个 Handler
,它是 ConsoleLogger
的一个实例。随后,您在 SpotifyLogger
构造函数的第一次调用中删除了 Handler
,因此在每次后续调用中,方法 getHandlers
returns 一个空数组。
由于您只将 Handler
添加到全局 Logger
,因此每次调用 SpotifyLogger
构造函数时都会将另一个 FileHandler
添加到全局记录器。我尚未验证,但我相信 Logger
将使用方法 getHandlers
返回的数组中第一个适当的 Handler
,因此您看到的行为只有第一个日志文件是正在写入,即您传递给 SpotifyLogger
构造函数的第一次调用的文件。
请注意,您没有提供 reproducible example,因此我无法根据您的上下文验证上述任何内容。我只是测试了你问题中的代码才能得出上述结果。
考虑以下对 class SpotifyLogger
的重写——包括仅用于测试目的的 main
方法。
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class SpotifyLogger {
private static final String[] NAMES = {"First", "Second"};
private static int count;
private int index;
public SpotifyLogger(String loggerFilePath) throws IOException {
index = count;
Logger myLogger = Logger.getLogger(NAMES[count++]);
myLogger.setUseParentHandlers(false);
// set level
myLogger.setLevel(Level.SEVERE);
// create a txt handler
FileHandler textFileHandler = new FileHandler(loggerFilePath);
SimpleFormatter simpleFormatter = new SimpleFormatter();
textFileHandler.setFormatter(simpleFormatter);
myLogger.addHandler(textFileHandler);
}
public void log(String user, Exception e) {
Logger myLogger = Logger.getLogger(NAMES[index]);
myLogger.log(Level.SEVERE, user, e);
}
public static void main(String[] args) {
try {
SpotifyLogger x = new SpotifyLogger("spotifyx.log");
SpotifyLogger y = new SpotifyLogger("spotifyy.log");
x.log("George", new Exception());
y.log("Martha", new RuntimeException());
}
catch (IOException x) {
x.printStackTrace();
}
}
}
请注意,您对父 Handler
的看法是正确的,因此上面代码中的以下行:
myLogger.setUseParentHandlers(false);
经过运行以上代码,文件spotifyx.log的内容为:
Feb 12, 2022 2:12:28 PM javalogp.SpotifyLogger log
SEVERE: George
java.lang.Exception
at javalogp/javalogp.SpotifyLogger.main(SpotifyLogger.java:38)
文件spotifyy.log的内容是:
Feb 12, 2022 2:12:28 PM javalogp.SpotifyLogger log
SEVERE: Martha
java.lang.RuntimeException
at javalogp/javalogp.SpotifyLogger.main(SpotifyLogger.java:39)
并且没有 日志消息被写入控制台。