免責事項:この質問が非常に長いことをお詫び申し上げます。ここで行われた提案を調査し、元の質問をしてから追加の調査を行ったので、コードを追加しました。
私は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が閉じられているのではないかと思います。