title: Implementing logging in a Java application
body: <p><strong>Disclaimer:</strong> 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.</p>
  
  <p>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</p>
  
  <pre><code>Logger.getLogger(FindCardsPanel.class.getName()).log(Level.SEVERE, "Storage I/O error", ex);
  </code></pre>
  
  <p>The only difference in each line of code is the class name and the message string.</p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>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?</p>
  
  <p><strong>Edit:</strong></p>
  
  <p>Based on the link given by @spdaley, I have created the following SSCCE:</p>
  
  <p><em>LoggingSSCCE.java</em>:</p>
  
  <pre><code>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();
          }
      }
  }
  </code></pre>
  
  <p><em>LogTest.java</em>:</p>
  
  <pre><code>package loggingsscce;
  
  interface LogTest {
      public void doLog();
  }
  </code></pre>
  
  <p><em>LogTestA.java</em>:</p>
  
  <pre><code>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()");
      }
  }
  </code></pre>
  
  <p><em>LogTestB.java</em>:</p>
  
  <pre><code>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()");
      }
  }
  </code></pre>
  
  <p>When I run this in NetBeans, the output window shows</p>
  
  <pre><code>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)
  </code></pre>
  
  <p>but the "loggingsscce.log" file is empty. </p>
  
  <p>So what am I missing?</p>
  
  <p><strong>Another edit:</strong></p>
  
  <p>I found an example at <a href="http://onjava.com/pub/a/onjava/2002/06/19/log.html?page=2" rel="nofollow">http://onjava.com/pub/a/onjava/2002/06/19/log.html?page=2</a> which I modified:</p>
  
  <pre><code>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 &lt; 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);
    }
  }
  </code></pre>
  
  <p>This produces the following output from the command-line:</p>
  
  <pre><code>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
  $
  </code></pre>
  
  <p>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?</p>
  
  <p><strong>Modifications to LoggingSSCCE class:</strong></p>
  
  <p>I added a static field:</p>
  
  <pre><code>private static FileHandler HANDLER = null;
  </code></pre>
  
  <p>And changed the <code>initLogger()</code> method:</p>
  
  <pre><code>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);
  }
  </code></pre>
  
  <p>This works great with the following contents of "loggingsscce.log":</p>
  
  <pre><code>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()
  </code></pre>
  
  <p>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?</p>