4

免責事項:この質問が非常に長いことをお詫び申し上げます。ここで行われた提案を調査し、元の質問をしてから追加の調査を行ったので、コードを追加しました。

私はJavaでオープンソースプロジェクトに取り組んでおり、ユーザーがアプリの問題を報告した場合にバグの追跡とデバッグを容易にするために、アプリケーションにログを実装したいと考えています。標準のJavaAPIのjava.util.loggingパッケージを見ています。例外がキャッチされたときに、すでにいくつかのロギングコードを追加しました。たとえば、私は

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

コードの各行の唯一の違いは、クラス名とメッセージ文字列です。

ロガーを使用する適切な方法は、クラス名の文字列ごとに1つを使用することであることを読みました。これは、ログメッセージのフィルタリングに多くの柔軟性をもたらすため、少し意味があります。

私の現在の問題は、すべてのログメッセージが現在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が閉じられているのではないかと思います。

4

2 に答える 2

4

編集:

元のコードの問題は、ハンドラーを早期に閉じていることです。

元のテキスト:

あなたの(元の)コードに2つの問題があります:

問題1はgetLogger("")、initを呼び出しgetLogger(xxx.class.getName())ていますが、実装を呼び出しています。これらのロガーはそれぞれ異なるロガーであり、1つのロガーにのみファイルハンドラーを設定します""

問題1によってマスクされていた問題2は、ハンドラーを早期に閉じています。

1つのファイルで複数のクラスを試すことができるものは次のとおりです。

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

これをクラスごとに1つのファイル、またはその他の基準でLoggingSSCCE.getLogger機能させる場合は、それぞれの場合に適切なファイル名を計算するように変更するだけです。

于 2012-09-10T00:28:35.517 に答える
4

すぐに頭に浮かぶのは、log4jを見てください。これは、あらゆる種類のライブラリやアプリケーションで非常に広く使用されているオープンソースのロギングフレームワークです。

編集:

java.util.loggingは基本的なAPIです。コードで明示的に実行するように指示していないことは、実際には何も実行しません。log4jは、完全な構成機能とその他の機能を備えたライブラリです。これはjava.util.loggingの上にあり、(比較的)低レベルのjava.util.loggingよりもはるかに使いやすい機能で拡張されています。

于 2012-09-09T22:47:41.393 に答える