How to develop a new Logger Implementation

The ILogWrapper interface

The Logger API is defined by the ILogWrapper interface.

All logger functionality is defined by this interface and all implementations must implement it. For a list of all the features this interface defines refer to it's JavaDoc documentation.

The BaseLogWrapper abstract class

The BaseLogWrapper is a standard base implementation of the logger API as specified by the ILogWrapper interface. The goal of this class is to facilitate the development of new Implementation classes of the logger API by providing default implementations of most of it's methods. These implementations represent the tasks that are normally handled the same way in any Logging platform we may want to implement.

This class is provided as a helper in the implementation of a new Logger Implementation. Using the BaseLogWrapper as a starting point to our Implementation one no longer needs to implement all methods of the API ILogWrapper, since BaseLogWrapper takes care of the large majority of them for us.

All that's left for us to do is:

  • logger instantiation: It's up to the implementation do determine how to instantiate the logger platform, through the constructor.
  • LogLevel getter and setter, since the LogLevel is managed by the underlying platform.
  • Log action: The log method that's called either directly of by the helper methods (trace, debug, warn, etc.)
    • Note: The Log method is not actually implemented, since BaseLogWrapper implements this for message parsing purposes. For actual writing of the Log message a new abstract method writeLog is added by this base implementation for us to override.

The BaseLogWrapper abstract class

For a sample implementation let's create an implementation that simply logs messages to the standard output stream, System.out. We will use BaseLogWrapper as a starting point for our implementation since it will save us the trouble of implementing all the helper methods and standard features like, for instance, indentation.

The empty class

The newly created class that extends BaseLogWrapper:

 1  public class LogWrapperSimpleImpl extends BaseLogWrapper {
 2  }

This of course is a class with a handful of compilation errors. We must implement several methods before it is ready. Let's continue.

Attributes and initialization

Let's give our class a constructor and a private attribute for the Log level management.

 1  public class LogWrapperSimpleImpl extends BaseLogWrapper {
 2  
 3      /** The current log level */
 4      private LogLevel logLevel;
 5      
 6      /**
 7       * Default constructor
 8       */
 9      public LogWrapperSimpleImpl() {
10  
11              // the default log level
12              this.logLevel = LogLevel.INFO;
13      }
14  }

Our constructor initializes our log level with the Info level. One could of course extend this with support for a persistent log level configuration, by reading a properties file or a XML configuration file from the disk. For now this is more than enough!

Implementing required methods

We must next implement three methods. These three methods are the only ones BaseLogWrapper could not provide for us.

writeLog: the Log Writer method

A simple implementation that concats the required log level with the desired log message and prints it to System.out

15      /**
16       * @see pt.digitalis.log.BaseLogWrapper#writeLog(pt.digitalis.log.LogLevel, java.lang.Object)
17       */
18      @Override
19      protected void writeLog(LogLevel level, Object message) {
20              System.out.println("[" + level.toString() + "]" + message);
21  
22      }
LogLevel getters and setters

Just plain ordinary getter and setter implementations for our log level attribute.

24      /**
25       * @see pt.digitalis.log.ILogWrapper#getLevel()
26       */
27      public LogLevel getLevel() {
28              return this.logLevel;
29      }
30
31      /**
32       * @see pt.digitalis.log.ILogWrapper#setLevel(pt.digitalis.log.LogLevel)
33       */
34      public void setLevel(LogLevel level) {
35              this.logLevel = level;
36
37      }
38
39  }

And this ends our implementation.

You can see how easy it is create your own logger implementation, if you need to.

One could easilly change this implementation to... for instance... construct a fully functional logger implementation that writes logs to the database. We would just have to change the writeLog method with the necessary database insert code and that was all. We could for instance take advantage of the relational features of the database to provide a log level field and a log message parentID. With these we could search for messages by level, and could build a log browsing interface that implemented collapsed log messages for easier drill-down-like browsing.

For a better view we will present the complete listing of our example implementation bellow. If you would like to play with the code, you can download the java source file here.

 1  public class LogWrapperSimpleImpl extends BaseLogWrapper {
 2  
 3      /** The current log level */
 4      private LogLevel logLevel;
 5      
 6      /**
 7       * Default constructor
 8       */
 9      public LogWrapperSimpleImpl() {
10  
11              // the default log level
12              this.logLevel = LogLevel.INFO;
13      }
14      
15      /**
16       * @see pt.digitalis.log.BaseLogWrapper#writeLog(pt.digitalis.log.LogLevel, java.lang.Object)
17       */
18      @Override
19      protected void writeLog(LogLevel level, Object message) {
20              System.out.println("[" + level.toString() + "]" + message);
21  
22      }
23
24      /**
25       * @see pt.digitalis.log.ILogWrapper#getLevel()
26       */
27      public LogLevel getLevel() {
28              return this.logLevel;
29      }
30
31      /**
32       * @see pt.digitalis.log.ILogWrapper#setLevel(pt.digitalis.log.LogLevel)
33       */
34      public void setLevel(LogLevel level) {
35              this.logLevel = level;
36
37      }
38
39  }