Doing Some Logging
The code below is grabbed from the sample class SimpleLogger, which is included in the source code download
for this article. The first thing I've done below is to get a Logger instance by using the getLogger
static method of the org.apache.log4j.Logger class. It is this Logger object (which I called myLogger
) that allows me to log.
Logger myLogger = Logger.getLogger("com.example.SimpleLogger");
// log events using the logger object
// note that since we have associated a
// level of WARN for our logger, we will not see the
// INFO log statement
// remember: DEBUG < INFO < WARN < ERROR < FATAL
myLogger.info("I have dry mouth.");
myLogger.warn("I am very thirsty");
myLogger.error("I am dehydrated.");
myLogger.fatal("I have died of thirst");
As you can see, logging is as simple as using the debug, info, warn, error, or fatal printing methods of the Logger object we get.
The output of the SimpleLogger class execution is shown below:
0 [main] WARN com.example.SimpleLogger - I am very thirsty
0 [main] ERROR com.example.SimpleLogger - I am dehydrated.
0 [main] FATAL com.example.SimpleLogger - I have died of thirst
Developers are provided with different printing methods because Log4j allows us to set the level of logging that we want to see in our logs. In the code above, I set the level of the Logger programmatically using the setLevel
method. However, this is usually done by grabbing the level from a configuration file, which I'll demonstrate in just a bit.
The possible levels of logging are ordered as follows:
DEBUG < INFO < WARN < ERROR < FATAL
|Author's Note: Note that in Log4j, you are given the extensibility to create your own levels by subclassing the org.apache.log4j.Level class. However, Log4j proponents do not suggest it.
Log requests of a given level will find their way into the logs only if the printing method used corresponds with a log level greater than or equal to the level set for the Logger. Let's say, for example, I set the Logger's level to WARN. In this case, debug and info statements will not find their way into the logs, but warn, error, and fatal statements will. Similarly, if I set the Logger's level to FATAL, only fatal requests will be found in the logs; debug, info, warn, and error log requests will not be enabled.
Juxtapose this ability to pick and choose which log requests make it into your logs with the all-or-nothing approach of using System.out statements. It is not hard to see the value add of Log4j.
Naming Your Loggers and Understanding Logger Lineage
It is up to each programmer to establish a convention for naming his/her Logger instances. It is common Log4j practice to use the package-delineated name of the class for the name of the Logger. This way, when you scrutinize your logs, it is easier to pinpoint which class was responsible for the writing of a given log statement.
|Author's Note: Logger names are case-sensitive.
Loggers follow a hierarchical naming rule. One Logger is an ancestor of another Logger if its name, predicated by a dot, is a prefix of another Logger. For example, com.ibm.test would be an ancestor of the so-called "descendant" Loggers ibm.test and test. Similarly, com.ibm.test is an ancestor of the descendant Logger test. If there are no ancestors between a given ancestor and a descendant, then those Loggers are referred to as parent and child Loggers, respectively.
Why should you care about this family lineage? In Log4j, logging levels are inherited. If you have not explicitly set a given Logger's logging level, the ancestry of the Logger in question will be scrutinized and moved, one by one, in the parent hierarchy until the first non-null level in the Logger hierarchy is reached. The Logger will then inherit this ancestor's level of logging.
Just as all objects in Java derive from the Object base class, all Loggers in Log4j can trace their ancestry back to the RootLogger Logger. The RootLogger always has an assigned level to ensure that if none of the descendant Loggers you create have their logging level explicitly specified, you can fall back on the logging level of the RootLogger (which is by default assigned to Level.DEBUG).
Interestingly enough, in the world of Log4j, the expression "I was born before my grandfather is valid." An example might best shed light on the subject. In short, I can create a Logger com.Grandfather.Grandson before I create the Logger com.Grandfather in my code. The parent Logger (com.Grandfather, in this example) will link to its descendent(s) (com.Grandfather.Grandson) when it is created.