Tuesday, April 9, 2013

Logging Frameworks

In any complex application comprising of several components working together, tracking failures effectively becomes more challenging. Even though the application is separated by individual components, a trace of operation is required to investigate potential failures. In such circumstances, logging individual component activities comes in handy and provides a great depth of insight over periodic operations. Logging using system.out and filewriter in Java was prevalent but now with more sophisticated frameworks available, such techniques have become a thing of the past. There are three major logging frameworks which are dominant in the java world apart from countless others. They are Log4J, Slf4J and Logback frameworks.

Java Logging API
The java logging API contains a basic set of logging capabilities in the java.util.logging package using the Logger class. The Logger actually is a hierarchy of Loggers, and a . (dot) in the hierarchy indicates a level in the hierarchy. If we get a Logger for the com.example then the logger is a child of the com Logger and the com Logger is child of the Logger for the empty String. We can configure the main logger which affects all its children. The log levels such as SEVERE, WARNING, INFO etc define the severity of a message. The Level class is used to define which messages should be written to the log. The levels OFF and ALL to turn the logging of or to log everything. Each logger can access several handlers which receives the log messages from the logger and exports it to a target file (FileHandler) or console (ConsoleHandler). Each handlers output can be configured with formatters such as SimpleFormatter to generate messages in text or XMLFormatter to generate messages in XML format. The log manager is responsible for creating and managing the logger and the maintenance of the configuration.

The logging can be configured using the log.properties file with the below sample configuration.

# Logging
handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler.level = ALL

# File Logging
java.util.logging.FileHandler.pattern = %h/myApp.log
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.FileHandler.level = INFO

# Console Logging
java.util.logging.ConsoleHandler.level = ALL


The "-Djava.util.logging.config.file=/absolute-path/logging.properties" parameter is used to load a custom log.properties for java util logging. It works with following cases:
  • Move the file log.properties to the default package (the root folder for your sources)
  • add it directly to the classpath (just like a JAR)
  • You can specify the package in which the file is, replacing "." with "/": -Djava.util.logging.config.file=com/company/package/log.properties
  • You can specify the absolute path

The most famous way to disable all the logging for any frameworks is by setting the error output to NULL as follows:
  static {
    //Windows style
    try {
        PrintStream nps = new PrintStream(new FileOutputStream("NUL:"));
        System.setErr(nps);
        System.setOut(nps);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
  }


Log4J Framework
Log4J is the oldest of the above frameworks, and widely used due its simplicity of usage. It defines various log levels and messages. Log4j is thread safe and optimized for speed. It is based on a named logger hierarchy. It supports multiple output appenders per logger and internationalization.
Log4j is not restricted to a predefined set of facilities. Its logging behavior can be set at runtime using a configuration file. It is  designed to handle Java Exceptions from the start. Log4j uses multiple levels, namely ALL, TRACE, DEBUG, INFO, WARN, ERROR and FATAL to denote log levels. The format of the log output can be easily changed by extending the Layout class. The target of the log output as well as the writing strategy can be altered by implementations of the Appender interface. Log4j is fail-stop but it does not guarantee that each log statement will be delivered to its destination.
   Below is the sample log4j property file: log4j.properties

#suppress logging from spring and hibernate to warn
log4j.logger.org.hibernate=WARN
log4j.logger.org.springframework=WARN

# Set root logger level to DEBUG and its only appender to Appender1.
log4j.rootLogger=INFO, Appender1,Appender2
# Appender1 is set to be a ConsoleAppender.
log4j.appender.Appender1=org.apache.log4j.ConsoleAppender
log4j.appender.Appender2=org.apache.log4j.RollingFileAppender
log4j.appender.Appender2.File=sample.log
# Appender2 uses PatternLayout.
log4j.appender.Appender1.layout=org.apache.log4j.PatternLayout
log4j.appender.Appender1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
log4j.appender.Appender2.layout=org.apache.log4j.PatternLayout
log4j.appender.Appender2.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

Log4j sample code is as follows:
      try {
            Properties props = new Properties();
            props.load(TestHTTP.class.getResourceAsStream("/log4j.properties"));
            System.out.println("props = " + props.toString());
            PropertyConfigurator.configure(props);
      } catch (IOException e) {
            e.printStackTrace();
      }

      LogManager.getRootLogger().setLevel(Level.OFF);

      // Pavan's Code
      Logger log = Logger.getLogger("myApp");
      log.setLevel(Level.ALL);
      log.info("initializing - trying to load configuration file ...");

      Properties preferences = new Properties();
      try {
          FileInputStream configFile = new FileInputStream("/path/to/app.properties");
          preferences.load(configFile);
          LogManager.getLogManager().readConfiguration(configFile);
      } catch (IOException ex)  {
          System.out.println("WARNING: Could not open configuration file");
          System.out.println("WARNING: Logging not configured (console output only)");
      }

      log.info("starting myApp");

Logback Framework
Logback framework is a successor to the log4j framework providing Slf4J Api implementation natively. Logging configuration can be provided either in xml or groovy. It provides a SiftingAppender which enables to maintain seperate the logfiles based on the user session instance and the ability to switch the loglevel for individual users. Logback automatically reloads upon configuration changes and provides a better I/O failover in case of server failure.

Logback delegates the task of writing a logging event to components called appenders.
Appenders must implement the ch.qos.logback.core.Appender interface, which contains doAppend() method which is responsible for outputting the logging events in a suitable format to the appropriate output device.

Sample configuration for logback framework is as follows:


  
    
  

  
     
        
     
  

  
    
      
      
      
      
      
      
      
    
  

  
    PERFORMANCE
    ALLOW
  

  
    
      %date [%thread] %mdc %-5level %logger %msg %n %ex
    
  

 
    
      unknown
    
    
      
        
          ERROR
          ACCEPT
          DENY
        
        ${logdir}/${contextName}Error.log
        true
        
          ${logdir}/${contextName}Error%d{yyyy-MM-dd}.%i.log
          
            
            10MB
          
          
          30

          
          30
        

        
          %date [%thread] %mdc %-5level %logger %msg %n
        

        

      

    
  

  
    
      
        
        
        
      
    
  






In any complex application