Logging
by
, May 30th, 2011 at 12:22 PM (7315 Views)
Logging is a great method to monitor the progress of a program while at the same time maintaining flexibility to control the output. Simply put, logging is just printing out statements during the runtime of an application. In another blog I described a simplistic method for logging - using System.out.println. More advanced logging libraries however go beyond just printing to the console - their flexibility allows control of when, how, and where to log.
Many logging libraries exist, including one within the java API (java.util.logging package) and Apache's open source log4j (Apache Logging Services ). A good logging library should provide the following major elements:
1) Levels - controls when logging should take place. One can place logging statements within code, defining a certain level for each statement, then define a runtime level, resulting in statements only of that level or higher being logged. This is very useful to differentiate between development and deployment.
2) Formats - controls the format of the logged statement - for instance one can format a logging statement to print out the level, time, class, and in some circumstances even the line number in the code.
3) Output (eg log4j Appender, java.util.logging.Handler) - where the logging statements will be printed. For instance one can specify the standard console IO or err stream, a file stream, a socket, etc…
4) Lastly, one should be able to specify the above three simply via properties or configuration file. Thus, one can change any one or all of these values simply by modifying an external file - without modifying any code or redeploying an application.
In the following example, I'll demonstrate the use of the java API logging. Below is a simple code snippet showing some very simple logging statements.
import java.util.logging.*; public class Test{ private static final Logger logger = Logger.getLogger(Test.class.toString()); public static void main(String[] args){ logger.setLevel(Level.INFO); printLogging(); logger.setLevel(Level.WARNING); printLogging(); logger.setLevel(Level.SEVERE); printLogging(); } /** * Prints 3 standard logging messages at different levels. */ private static void printLogging(){ logger.info("Info"); logger.warning("Warning"); logger.severe("Severe"); } }
The above code demonstrates the java API and how LEVEL's can be changed. In this simple example, the level for the logger is changed from INFO, to WARNING, to SEVERE - while logging messages are printed after each modification. Running this code, one can see that the for each level set, logging statements get printed for that level and those above, whereas those below the level do not. This can be extremely handy in large applications, where one can print debugging statements while writing and debugging an application, after which one can change to a higher level to omit those statements without removing any code.
While this is a simple example, there are other ways in which to change the output, for example the following code adds a new Handler to the Logger which outputs the logging statements to a file
(Note the this code still prints the logging to the console as well as the file, see below)import java.util.logging.*; public class Test{ private static final Logger logger = Logger.getLogger(Test.class.toString()); private static final String PATH = "myfilepath"; public static void main(String[] args) throws Exception{ logger.addHandler(new FileHandler(PATH)); printLogging(); } private static void printLogging(){ logger.info("Info"); logger.warning("Warning"); logger.severe("Severe"); } }
The following is a more complex example
import java.util.logging.*; public class Test{ private static final Logger logger = Logger.getLogger(Test.class.toString()); public static void main(String[] args) throws Exception{ logger.setUseParentHandlers(false);//prevent parent handlers from logging logger.addHandler(new ConsoleHandler()); for ( Handler h : logger.getHandlers() ){ h.setFormatter(new MyFormatter()); } logger.setLevel(Level.INFO); printLogging(); } private static void printLogging(){ logger.info("Info"); logger.warning("Warning"); logger.severe("Severe"); } private static class MyFormatter extends Formatter{ @Override public String format(LogRecord arg0) { StringBuilder sb = new StringBuilder(); sb.append(arg0.getLoggerName()); sb.append(" "); sb.append(arg0.getLevel()); sb.append(" "); sb.append(arg0.getMessage()); sb.append("\n"); return sb.toString(); } } }
In the above example several things were done. First, logging statements may get passed up to parent handlers (the reason why our file output example still prints to the console), thus setting the logger to not use any parent handlers allows our one handler to be the sole handler for this logger. Next, we created a ConsoleHandler and added this Handler to our Logger - this essentially logs messages to the System.err stream (note that one can readily configure this to the System.out stream). Lastly, we created a Formatter which customizes the output of the logging statements based upon a LogRecord.
These are just a few very simple examples to demonstrate the simplicity of logging. The flexibility of a logging system within an application takes the System.out.println to a whole new level, providing detailed information for the inner workings of an application in all phases of development and deployment. Of course, logging is useless unless one prints out details which are informative enough to understand and useful enough to help further develop the application.
Links:
Apache log4j 1.2 - log4j 1.2
Java TM Logging Overview