Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Write custom appenders for log4j

Extend log4j to support lightweight over-the-network logging

  • Print
  • Feedback

Page 4 of 6

Listing 3 shows a simple viewer application for my RemoteAppender. It's just a simple socket-based client application that waits in a loop until it can open a socket to the server application that logs the messages. (See Resources for a discussion of sockets and Java's socket APIs). The port number, which is hard-coded into this simple example (as 1234) is passed to the server via the configuration file in Listing 2. Here's the relevant line:

 log4j.appender.REMOTE.Port=1234



The client application waits in a loop until it can connect to the server, and then it just reads messages from the server and prints them to the console. Nothing earth shattering. The client knows nothing about log4j—it just reads strings and prints them—so the coupling to the log4j systems is nonexistent. Launch the client with java Client and terminate it with a Ctrl-C.

Listing 3. Client.java: A client for viewing logging messages

  1  import java.net.*;
   2  import java.io.*;
   3  
   4  public class Client
   5  {
   6      public static void main(String[] args) throws Exception
   7      {
   8          Socket s;
   9          while( true )
  10          {   try
  11              {
  12                  s = new Socket( "localhost", 1234 );
  13                  break;
  14              }
  15              catch( java.net.ConnectException e )
  16              {   // Assume that the host isn't available yet, wait
  17                  // a moment, then try again.
  18                  Thread.currentThread().sleep(50);
  19              }
  20          }
  21  
  22          BufferedReader in = new BufferedReader(
  23                                  new InputStreamReader( s.getInputStream() ) );
  24  
  25          String line;
  26          while( (line = in.readLine()) != null )
  27              System.err.println( line );
  28      }
  29  }



Note, by the way, that the client in Listing 3 is a great example of when not to use Java's NIO (new input/output) classes. There's no need for asynchronous reading here, and NIO would complicate the application considerably.

The remote appender

All that's left is the appender itself, which manages the server-side socket and writes the output to the clients that connect to it. (Several clients can receive logging messages from the same appender simultaneously.) The code is in Listing 4.

Starting with the basic structure, the RemoteAppender extends log4j's AppenderSkeleton class, which does all of the boilerplate work of creating an appender for you. You must do two things to make an appender: First, if your appender needs to be passed arguments from the configuration file (like the port number), you need to provide a getter/setter function with the names getXxx() and setXxx() for a property named Xxx. I've done that for the Port property on line 41 of Listing 4.

Note that both the getter and setter methods are private. They're provided strictly for use by the log4j system when it creates and initializes this appender, and no other object in my program has any business accessing them. Making getPort() and setPort() private guarantees that normal code can't access the methods. Since log4j accesses these methods via the introspection APIs, it can ignore the private attribute. Unfortunately, I've noticed that private getters and setters work only in some systems. I have to redefine these fields as public to get the appender to work correctly under Linux, for example.

  • Print
  • Feedback

Resources