Question about loggers
I'm working my way through Stephen Stelting's book Robust Java and have
some questions about the use of the Logging API (java.util.logging) in
Chapter 5.
My impression from the "Aspect Questions" thread was that the "best
practices" approach to logging was to use named loggers, where, in essence,
each method that needs to write to the log creates a named logger via
Logger logger = Logger.getLogger(getClass().getName());
and then writes to the log.
Did I get that right? Stelting mentions anonymous loggers and the global
logger starting on page 71.I see from the API (and by actually trying it in
the sample program below) that the global logger is no longer a recommended
technique but are there any common circumstances where a professional
program would use anonymous loggers? Or are they more a case of something
that might be used in a "sandbox" type program to try something out?
Also, Stelting mentions that you can use the LogManager to "cache Logger
objects for repeat use". What circumstances would justify using LogManager?
I was under the impression that simply doing Logger.getLogger() was
perfectly adequate for a professional quality program. Why would LogManager
be any better than simply making the logger a class variable?
I tried a little sample program that combined LogManager, an anonymous
logger and the global logger and came up with this:
================================================================
package sandbox;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
public class LoggingTest {
public static void main(String[] args) {
LoggingTest test = new LoggingTest();
}
public LoggingTest() {
Logger logger = Logger.getLogger(getClass().getName());
logger.log(Level.INFO, "Starting the constructor");
System.out.println("1+1=" + (1+1));
calculation2();
calculation3();
LogManager logManager = LogManager.getLogManager();
Logger logger2 = Logger.getLogger(getClass().getName() + ".foo");
logManager.addLogger(logger2);
calculation4();
}
private static void calculation2() {
Logger anonymousLogger = Logger.getAnonymousLogger();
anonymousLogger.log(Level.INFO, "Starting the calculation2() method");
System.out.println("2+2=" + (2+2));
}
private static void calculation3() {
@SuppressWarnings("deprecation")
Logger globalLogger = Logger.global;
globalLogger.log(Level.INFO, "Starting the calculation3() method");
System.out.println("3+3=" + (3+3));
}
private void calculation4() {
LogManager logManager = LogManager.getLogManager();
Logger logger = logManager.getLogger(getClass().getName() + ".foo");
logger.log(Level.INFO, "Starting the calculation4() method");
System.out.println("4+4=" + (4+4));
}
}
================================================================
The log records looked like this:
Date/Time
(YYYY-MM-DD-HH:MM:SS) Sequence Logger Level Class Method Thread
Programmer Message Exception Message Exception Class Exception
Method Exception Line
2012-03-07-16:27:52 0 sandbox.LoggingTest INFO sandbox.LoggingTest
<init> 10 Starting the constructor N/A
2012-03-07-16:27:52 1 INFO sandbox.LoggingTest calculation2
10 Starting the calculation2() method N/A
2012-03-07-16:27:52 2 global INFO sandbox.LoggingTest
calculation3 10 Starting the calculation3() method N/A
2012-03-07-16:27:52 3 sandbox.LoggingTest.foo INFO
sandbox.LoggingTest calculation4 10 Starting the calculation4()
method N/A
Then, I wrote a version using a named logger and a single class variable
and the whole thing looks a lot simpler and cleaner:
====================================================================
package sandbox;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggingTest2 {
Logger logger = null;
public static void main(String[] args) {
LoggingTest2 test = new LoggingTest2();
}
public LoggingTest2() {
this.logger = Logger.getLogger(getClass().getName());
this.logger.log(Level.INFO, "Starting the constructor");
System.out.println("1+1=" + (1+1));
calculation2();
calculation3();
calculation4();
}
private void calculation2() {
this.logger.log(Level.INFO, "Starting the calculation2() method");
System.out.println("2+2=" + (2+2));
}
private void calculation3() {
this.logger.log(Level.INFO, "Starting the calculation3() method");
System.out.println("3+3=" + (3+3));
}
private void calculation4() {
this.logger.log(Level.INFO, "Starting the calculation4() method");
System.out.println("4+4=" + (4+4));
}
}
====================================================================
The log records from that look very similar except that the logger name is
always "sandbox.LoggingTest2":
Date/Time
(YYYY-MM-DD-HH:MM:SS) Sequence Logger Level Class Method Thread
Programmer Message Exception Message Exception Class Exception
Method Exception Line
2012-03-07-16:31:13 0 sandbox.LoggingTest2 INFO sandbox.LoggingTest2
<init> 10 Starting the constructor N/A
2012-03-07-16:31:13 1 sandbox.LoggingTest2 INFO sandbox.LoggingTest2
calculation2 10 Starting the calculation2() method N/A
2012-03-07-16:31:13 2 sandbox.LoggingTest2 INFO sandbox.LoggingTest2
calculation3 10 Starting the calculation3() method N/A
2012-03-07-16:31:13 3 sandbox.LoggingTest2 INFO sandbox.LoggingTest2
calculation4 10 Starting the calculation4() method N/A
For my money, my way is at least as good as Stelting's way and considerably
more concise. The only obvious negative to my approach was the class
variable named 'logger'. I know that class variables are something we want
to avoid whenever we can. But just how bad is it for me to create a single
logger as a class variable? Wouldn't I even be helping my performance to
have the logger be a class variable, especially as the number of methods
that were logging increased? After all, I'd only be creating one logger per
class rather than one for each method that was logging.
I'm worried that I'm fooling myself here and missing some big objection to
doing logging the way LoggingTest2 does it. After all, Stelting has
presumably been writing error handling code correctly for years while I
have only been doing it for days. But I'm not seeing any obvious problems
to my approach, which is essentially the same as what everyone recommended
in the other thread except that I've made the logger a class variable.
Can someone enlighten me? I'm leaning towards implementing either the
technique shown in LoggingTest2 or, if the class variable really is a very
bad idea, using Logger.getLogger() locally in each method to get the
logger.
--
Novice