在Java应用程序中实现日志记录 [英] Implementing logging in a Java application

查看:115
本文介绍了在Java应用程序中实现日志记录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我正在开发一个Java开源项目,并希望实现登录我的应用程序,以便在用户报告应用程序问题时进行错误跟踪和调试。我正在查看标准Java API中的java.util.logging包。我已经在捕获异常时添加了一些日志记录代码。例如,我有

  Logger.getLogger(FindCardsPanel.class.getName())。log(Level.SEVERE,Storage I / O错误,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;

公共类LoggingSSCCE {
private static String LOG_FILE_NAME =loggingsscce.log;

public static void main(String [] args)抛出IOException {
LoggingSSCCE.initLogger();

LogTest lta = new LogTestA();
lta.doLog();

LogTest ltb = new LogTestB();
ltb.doLog();
}

private static void initLogger()抛出IOException {
FileHandler handler = null;

try {
boolean append = true;
handler = new FileHandler(LoggingSSCCE.LOG_FILE_NAME,追加);
handler.setFormatter(new SimpleFormatter());

Logger logger = Logger.getLogger();
logger.setLevel(Level.ALL);
logger.addHandler(handler);
} finally {
handler.close();
}
}
}

LogTest.java

  package loggingsscce; 

接口LogTest {
public void doLog();
}

LogTestA.java

  package loggingsscce; 

import java.util.logging.Level;
import java.util.logging.Logger;

类LogTestA实现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;

类LogTestB实现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(总时间:0秒)

但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;

公共类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);

//根记录器的处理程序默认为INFO。我们必须以
//来支持它们。如果我们想要的话,我们可以只提供其中一些
//,但我们会将它们全部调高。
Handler [] handlers = Logger.getLogger()。getHandlers();
for(int index = 0; index< handlers.length; index ++){
handlers [index] .setLevel(Level.FINE);
}

//我们还必须设置我们的记录器来记录更精细的
//消息
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
罚款:你好记录!
Hello world!

$ b lib_lab_ref08 @ LBP-REF87XVMDP1 / e / devel / src / java / stackoverflow
$ cat hello.log
Sep 09,2012 6:13:33 PM HelloWorld sayHello
精细:你好记录!

lib_lab_ref08 @ LBP-REF87XVMDP1 / e / devel / src / java / stackoverflow
$

所以这个单一类的例子运行得很好。如我之前的代码中有多个文件中的多个类有什么区别?



对LoggingSSCCE类的修改:



我添加了一个静态字段:

  private static FileHandler HANDLER = null; 

并更改了 initLogger()方法:

  private static void initLogger()抛出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是否被垃圾收集器关闭?

解决方案

编辑:



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



原文:



我发现您的(原始)代码有两个问题:



问题一,你呢在init中调用 getLogger(),但调用 getLogger(xxx.class.getName()) in你的实现。这些记录器中的每一个都是不同的记录器,您只需为设置文件处理程序。



<问题二,问题一被掩盖,你正在提早关闭处理程序。



这里有一些你可以在一个文件中尝试多个类的东西:



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;

公共类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,追加);
handler.setFormatter(new SimpleFormatter());
}

Logger logger = Logger.getLogger(loggerName);
logger.setLevel(Level.ALL);
logger.addHandler(handler);
loggers.put(loggerName,logger);
返回记录器;
}
}

LogTest.java

  package loggingsscce; 

接口LogTest {
public void doLog();
}

LogTestA.java

  package loggingsscce; 

import java.util.logging.Level;
import java.util.logging.Logger;

类LogTestA实现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;

类LogTestB实现LogTest {
@Override
public void doLog(){
LoggingSSCCE.getLogger(LogTestB.class.getName())。log(Level。 INFO,LogTestB.doLog());
}
}

如果你想让它适用于每个班级的一个文件或其他一些标准,只需修改 LoggingSSCCE.getLogger 即可为每种情况计算一个合适的文件名。


Disclaimer: I apologize that this question is so long. I have added code as I have explored suggestions made here and done additional research since asking my original question.

I am working on an open source project in Java and want to implement logging in my application to facilitate bug tracking and debugging when/if users report problems with the app. I am looking at the java.util.logging package from the standard Java API. I've already added some logging code when exceptions are caught. For example, I have

Logger.getLogger(FindCardsPanel.class.getName()).log(Level.SEVERE, "Storage I/O error", ex);

The only difference in each line of code is the class name and the message string.

I have read that the proper way to use a logger is using one for each class name string. This makes a little bit of sense since it allows a lot of flexibility in filtering log messages.

My current problem is that all of the logging messages currently go to stderr. I need to write them to a file instead. I have found the FileHandler class which facilitates this. However, since I have dozens of classes, I have dozens of logger names. Do I need to add the FileHandler to each of these? That seems like it will take a lot of work, especially when I decide to add another class to my code base.

I understand that there is some kind of tree hierarchy of loggers. Does this happen automatically? If not, how do I create my own hierarchy? And whether or not it's automatic, where in the hierarchy do I add my FileHandler so that all logging goes to the same file?

Edit:

Based on the link given by @spdaley, I have created the following 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()");
    }
}

When I run this in NetBeans, the output window shows

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)

but the "loggingsscce.log" file is empty.

So what am I missing?

Another edit:

I found an example at http://onjava.com/pub/a/onjava/2002/06/19/log.html?page=2 which I modified:

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);
  }
}

This produces the following output from the command-line:

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
$

So this single class example works just fine. What is the difference when I have multiple classes in multiple files as in my previous code?

Modifications to LoggingSSCCE class:

I added a static field:

private static FileHandler HANDLER = null;

And changed the initLogger() method:

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);
}

This works great with the following contents of "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()

I'm still having trouble making this all work in my more complicated Swing project. I suspect my FileHandler is being closed, perhaps by the garbage collector?

解决方案

Edit:

The problem in your original code is that you're closing the handler early.

Original Text:

I see two problems with your (original) code:

Problem one, you're calling getLogger("") in your init, but calling getLogger(xxx.class.getName()) in your implementations. Each of those loggers is a different logger, and you're only setting the file handler for the "" one.

Problem two, which was being masked by problem one, you're closing the handler early.

Here's something you can try for multiple classes in one file:

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()");
    }
}

If you want this to work for one file per class, or some other criteria, simply modify LoggingSSCCE.getLogger to compute an appropriate file name for each case.

这篇关于在Java应用程序中实现日志记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆