在Java应用程序中实现日志记录

时间:2012-09-09 22:42:50

标签: java logging java.util.logging

免责声明:我很抱歉这个问题太久了。我已经添加了代码,因为我已经探讨了这里提出的建议,并在询问我的原始问题之后进行了额外的研究。

我正在研究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正在被垃圾收集器关闭?

2 个答案:

答案 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以为每个案例计算适当的文件名。