免责声明:我很抱歉这个问题太久了。我已经添加了代码,因为我已经探讨了这里提出的建议,并在询问我的原始问题之后进行了额外的研究。
我正在研究Java中的一个开源项目,并希望在我的应用程序中实现日志记录,以便在用户报告应用程序问题时进行错误跟踪和调试。我正在查看标准Java API中的java.util.logging包。我已经在捕获异常时添加了一些日志代码。例如,我有
Logger.getLogger(FindCardsPanel.class.getName()).log(Level.SEVERE, "Storage I/O error", ex);
每行代码的唯一区别是类名和消息字符串。
我已经读过使用记录器的正确方法是为每个类名字符串使用一个。这有点意义,因为它可以在过滤日志消息时提供很大的灵活性。
我目前的问题是所有日志消息当前都转到stderr。我需要将它们写入文件。我找到了FileHandler类来促进这一点。但是,由于我有几十个类,我有几十个记录器名称。我是否需要将FileHandler添加到其中每个?这似乎需要做很多工作,特别是当我决定在我的代码库中添加另一个类时。
我知道记录器有某种树层次结构。这会自动发生吗?如果没有,我如何创建自己的层次结构?无论它是否是自动的,我在层次结构中添加FileHandler以便所有日志记录都转到同一个文件?
修改
根据@spdaley给出的链接,我创建了以下SSCCE:
LoggingSSCCE.java :
package loggingsscce;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggingSSCCE {
private static String LOG_FILE_NAME = "loggingsscce.log";
public static void main(String[] args) throws IOException {
LoggingSSCCE.initLogger();
LogTest lta = new LogTestA();
lta.doLog();
LogTest ltb = new LogTestB();
ltb.doLog();
}
private static void initLogger() throws IOException {
FileHandler handler = null;
try {
boolean append = true;
handler = new FileHandler(LoggingSSCCE.LOG_FILE_NAME, append);
handler.setFormatter(new SimpleFormatter());
Logger logger = Logger.getLogger("");
logger.setLevel(Level.ALL);
logger.addHandler(handler);
} finally {
handler.close();
}
}
}
LogTest.java :
package loggingsscce;
interface LogTest {
public void doLog();
}
LogTestA.java :
package loggingsscce;
import java.util.logging.Level;
import java.util.logging.Logger;
class LogTestA implements LogTest {
@Override
public void doLog() {
Logger.getLogger(LogTestA.class.getName()).log(Level.INFO, "LogTestA.doLog()");
}
}
LogTestB.java :
package loggingsscce;
import java.util.logging.Level;
import java.util.logging.Logger;
class LogTestB implements LogTest {
@Override
public void doLog() {
Logger.getLogger(LogTestA.class.getName()).log(Level.INFO, "LogTestB.doLog()");
}
}
当我在NetBeans中运行它时,输出窗口显示
run:
Sep 09, 2012 5:36:56 PM loggingsscce.LogTestA doLog
INFO: LogTestA.doLog()
Sep 09, 2012 5:36:56 PM loggingsscce.LogTestB doLog
INFO: LogTestB.doLog()
BUILD SUCCESSFUL (total time: 0 seconds)
但“loggingsscce.log”文件为空。
那么我错过了什么?
另一个编辑:
我在http://onjava.com/pub/a/onjava/2002/06/19/log.html?page=2找到了一个我修改过的例子:
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogManager;
import java.util.logging.SimpleFormatter;
public class HelloWorld {
private static Logger theLogger = Logger.getLogger(HelloWorld.class.getName());
public static void main( String[] args ) throws IOException {
Logger rootLogger = Logger.getLogger("");
Handler handler = new FileHandler("hello.log");
handler.setFormatter(new SimpleFormatter());
rootLogger.addHandler(handler);
// The root logger's handlers default to INFO. We have to
// crank them up. We could crank up only some of them
// if we wanted, but we will turn them all up.
Handler[] handlers = Logger.getLogger( "" ).getHandlers();
for ( int index = 0; index < handlers.length; index++ ) {
handlers[index].setLevel( Level.FINE );
}
// We also have to set our logger to log finer-grained
// messages
theLogger.setLevel(Level.FINE);
HelloWorld hello = new HelloWorld("Hello world!");
hello.sayHello();
}
private String theMessage;
public HelloWorld(String message) {
theMessage = message;
}
public void sayHello() {
theLogger.fine("Hello logging!");
System.err.println(theMessage);
}
}
这将从命令行生成以下输出:
lib_lab_ref08@LBP-REF87XVMDP1 /e/devel/src/java/stackoverflow
$ javac HelloWorld.java
lib_lab_ref08@LBP-REF87XVMDP1 /e/devel/src/java/stackoverflow
$ java HelloWorld
Sep 09, 2012 6:13:33 PM HelloWorld sayHello
FINE: Hello logging!
Hello world!
lib_lab_ref08@LBP-REF87XVMDP1 /e/devel/src/java/stackoverflow
$ cat hello.log
Sep 09, 2012 6:13:33 PM HelloWorld sayHello
FINE: Hello logging!
lib_lab_ref08@LBP-REF87XVMDP1 /e/devel/src/java/stackoverflow
$
所以这个单一类的例子运行得很好。如我之前的代码中有多个文件中的多个类有什么区别?
对LoggingSSCCE类的修改:
我添加了一个静态字段:
private static FileHandler HANDLER = null;
并更改了initLogger()
方法:
private static void initLogger() throws IOException {
LoggingSSCCE.HANDLER = new FileHandler(LoggingSSCCE.LOG_FILE_NAME);
LoggingSSCCE.HANDLER.setFormatter(new SimpleFormatter());
Logger logger = Logger.getLogger("");
logger.setLevel(Level.ALL);
logger.addHandler(LoggingSSCCE.HANDLER);
}
这适用于“loggingsscce.log”的以下内容:
Sep 09, 2012 6:50:16 PM loggingsscce.LogTestA doLog
INFO: LogTestA.doLog()
Sep 09, 2012 6:50:16 PM loggingsscce.LogTestB doLog
INFO: LogTestB.doLog()
我仍然无法在我更复杂的Swing项目中完成所有工作。我怀疑我的FileHandler正在被垃圾收集器关闭?
答案 0 :(得分:4)
想到的最直接的事情是,看看log4j。它是一个开源的日志记录框架,非常广泛用于各种库和应用程序。
修改强>
java.util.logging是基本API。在代码中,它实际上没有做任何你没有告诉它做的事情。 log4j是一个具有完整配置功能和其他好东西的库。它位于java.util.logging之上,并使用比(相对)低级java.util.logging更容易使用的功能扩展它。
答案 1 :(得分:2)
修改强>
原始代码中的问题是您提前关闭了处理程序。
原文:
我发现您的(原始)代码存在两个问题:
问题一,你在init中调用getLogger("")
,但在实现中调用getLogger(xxx.class.getName())
。每个记录器都是不同的记录器,您只需为""
设置文件处理程序。
问题二,问题一被掩盖,你早早关闭了处理程序。
您可以在一个文件中尝试多个类:
LoggingSSCCE.java :
package loggingsscce;
import java.io.IOException;
import java.util.Hashtable;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggingSSCCE {
private static String LOG_FILE_NAME = "loggingsscce.log";
Hashtable<String, Logger> loggers = new Hastable<String, Logger>();
FileHandler handler = null;
public static void main(String[] args) throws IOException {
LogTest lta = new LogTestA();
lta.doLog();
LogTest ltb = new LogTestB();
ltb.doLog();
}
public static Logger getLogger(String loggerName) throws IOException {
if ( loggers.get(loggerName) != null )
return loggers.get(loggerName);
if ( handler == null ) {
boolean append = true;
handler = new FileHandler(LoggingSSCCE.LOG_FILE_NAME, append);
handler.setFormatter(new SimpleFormatter());
}
Logger logger = Logger.getLogger(loggerName);
logger.setLevel(Level.ALL);
logger.addHandler(handler);
loggers.put(loggerName, logger);
return logger;
}
}
LogTest.java :
package loggingsscce;
interface LogTest {
public void doLog();
}
LogTestA.java :
package loggingsscce;
import java.util.logging.Level;
import java.util.logging.Logger;
class LogTestA implements LogTest {
@Override
public void doLog() {
LoggingSSCCE.getLogger(LogTestA.class.getName()).log(Level.INFO, "LogTestA.doLog()");
}
}
LogTestB.java :
package loggingsscce;
import java.util.logging.Level;
import java.util.logging.Logger;
class LogTestB implements LogTest {
@Override
public void doLog() {
LoggingSSCCE.getLogger(LogTestB.class.getName()).log(Level.INFO, "LogTestB.doLog()");
}
}
如果您希望每个类或一些其他条件适用于一个文件,只需修改LoggingSSCCE.getLogger
以为每个案例计算适当的文件名。