繁体   English   中英

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

[英]Implementing logging in a Java application

免责声明:我很抱歉这个问题太长了。 自从问了我最初的问题以来,我已经添加了代码,因为我已经探索了这里提出的建议并进行了进一步的研究。

我正在使用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可能被垃圾收集器关闭了吗?

立即想到的是,看看log4j 这是一个开放源码的日志框架是非常广泛的应用在各种库和应用程序的。

编辑:

java.util.logging是基本的API。 在代码中,它确实不会做任何您没有告诉它要做的事情。 log4j是一个具有完整配置功能和其他功能的库。 它位于java.util.logging之上,并通过比(相对)低级java.util.logging更易于使用的功能对其进行了扩展。

编辑:

原始代码中的问题是您要提前关闭处理程序。

原文:

我看到您的(原始)代码有两个问题:

问题一,您在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适用于每个类一个文件或其他标准,只需修改LoggingSSCCE.getLogger以为每种情况计算一个合适的文件名。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM