Monday, March 31, 2008

Pretty Logging...

Apache Chainsaw: A GUI Log Viewer

Introduction

Logging is an essential part of any software program. Logging provides a way to capture information about the operation of an application. Once captured, the information can be used for many purposes, but it is particularly useful for debugging, troubleshooting, and auditing. However, parsing a huge log file manually might be tedious, especially for server applications. Apache Chainsaw is a GUI based log viewer utility that makes things easier for us. To know more about Chainsaw visit http://logging.apache.org/chainsaw/index.html.


Motivation


Every once in a while we have to go through such scenarios. Often during internal development, customer testing and even in running products, log files are our only way of debugging any problem. Writing logs in a local file can be sufficient in single person development or testing. However, when we work in a team, distributed logging becomes necessary. The Apache log4j provides as options to send log messages to remote addresses, even broadcast them. Using tools like Chainsaw, we can observe the logs from a remote machine at real time. This can save a lot of time and effort.

Document Scope

In this document, I will describe the necessary steps for testing out Chainsaw with a simple logging program. The tutorial is divided into two parts. The first part shows how to configure log4j with SocketAppender. The latter part describes ZeroConf configuration.


Download Chainsaw v2 Bundle

Download chainsaw-bundle.zip from http://logging.apache.org/log4j/docs/webstart/chainsaw and extract it in CHAINSAW_HOME (In Apache Chainsaw’s homepage, another link is provided, but that didn’t work with ZeroConf).

Create Test Project

Create a new project ChainsawTest and add the following source file:


package ih.test.chainsaw;

import org.apache.log4j.Logger;

import org.apache.log4j.PropertyConfigurator;

public class ChainsawTest

{

// declare a static logger

private static final Logger log = Logger.getLogger(ChainsawTest.class);

public static void main(String[] args) throws InterruptedException

{

// load the log4j configuration file

PropertyConfigurator.configure("log4j_config.properties");

while(true)

{

log.debug("Testing DEBUG");

log.error("Testing ERROR");

// sleep for 1 second

Thread.sleep(1000);

}

}

}


Add log4j-1.2.8.jar to the project classpath.

Log4j Configuration (SocketAppender)

First, we will communicate with Chainsaw using a SocketAppender. The SocketAppender sends LoggingEvent objects to a remote a log server. In our case, Chainsaw will catch these objects and display in a GUI.


Add the following log4j_config.properties file:


log4j.rootCategory=DEBUG, zeroconf, chainsaw, stdout

# Socket Appender

log4j.appender.chainsaw=org.apache.log4j.net.SocketAppender

log4j.appender.chainsaw.remoteHost=localhost

log4j.appender.chainsaw.port=4445

log4j.appender.chainsaw.locationInfo=true

# Stdout Appender

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d %p [%t] %C{1} - %mn


Here we have configured 2 appenders. For the SocketAppender we need to specify a remote host address and port. This is the address where logs will be sent. Since we will be running Chainsaw in the same pc, we use localhost. Port 4445 is default for SocketAppender (of course, any port number can be used).


We have also specified a Console Appender just for testing.

Running Chainsaw

Run chainsaw.bat from CHAINSAW_HOME. You should see the following window:



Select the second option (simple receiver on port 4445) and click OK.

View Logs

Run your project. The code will keep on writing two log messages. Now you can see that a new tab will appear beside “chainsaw-log” tab in the Chainsaw window. Open it and BINGO!!! There are your logs. Bright and shiny like the autumn morning.




For more information about how to filter and customize the UI, see the tutorial provided with Chainsaw.

Chainsaw with ZeroConf

One of the immediate disadvantages of the above SocketAppender is that we have to specify the address of the receiver of the logging events. If we want to have multiple receivers, we have to specify multiple appenders. Also we need to know the address of each of the receivers. Would not it be nice if we could just broadcast our logs to everyone listening? In that way everyone interested (testers, customers and other developers) could observe the logs using Chainsaw. ZeroConf is the solution.


To broadcast using ZeroConf, we need to use ZeroConfSocketHubAppender. It is a sub-class of SocketHubAppender that broadcasts its configuration via Zeroconf. This allows Zeroconf aware applications such as Chainsaw to be able to detect them, and automatically configure themselves to be able to connect to them.


To use ZeroConfSocketHubAppender copy jmdns.jar and log4j-zeroconf.jar from CHAINSAW_HOME and add them to the project classpath. Then add the following lines to log4j_configuration.properties:


# ZeroConf Socket Hub Appender

log4j.appender.zeroconf=org.apache.log4j.net.ZeroConfSocketHubAppender

log4j.appender.zeroconf.name=MyZeroConfSockeHubAppender

log4j.appender.zeroconf.port=4560

Once configured and your application re-started, you should be able to click on the Zeroconf tab inside Chainsaw, and see the "MyZeroConfSocketHubAppender" listed. If you double click on the row, Chainsaw will automatically connect to your application and start receiving events. You can tick the 'auto-connect' option to have Chainsaw immediately connect as soon as it sees your application started. Great for Dev/QA environment.

The Zeroconf-enabled SocketHubAppender broadcasts its existence via a multicast protocol, passing enough information for Chainsaw to be able to connect to it.

ZeroConf and Firewall

Multicast protocols generally don't pass through firewall, so in a production environment Zeroconf won't work.

Conclusion

As you can already see, Chainsaw can greatly simplify development and testing. No more ‘Dear Customer, please send us the 300MB log file and you will take a look’ or ‘Hey my log file shows ERROR, come take a look’. With a little bit of configuration we can sit on our chairs and watch logs from a remote machine in a nice GUI.

11 comments:

Unknown said...

Nice and excillent topic for discission :)
Thanks for shareing this information with us.

As I don’t know much about log4j or the viewer, I like to ask you one question. Is that viewer will also work with .NET project? I mean log4net option.

Ishtiaq said...

@kingshaef

Check out this link:

http://geekswithblogs.net/kobush/archive/2005/07/15/46627.aspx

Ishtiaq said...

@kingshaef

Another link (preferable)

http://logging.apache.org/log4net/release/howto/chainsaw.html

Pierre Rougier said...

Hi,

I try your tutorial and it dont works with me.

and extract it in CHAINSAW_HOME

Dont make sens to me.

I am under Windows XP
Got this messages from console :

log4j:ERROR Could not find value for key log4j.appender.zeroconf
log4j:ERROR Could not instantiate appender named "zeroconf".
log4j:ERROR Could not connect to remote log4j server at [localhost]. We will try again later.
Testing DEBUG
Testing ERROR

Anonymous said...

Try logFaces :
http:// www.moonlit-software.com.

Its client can work in a mode similar to chainsaw, yet it's much more powerful.

D.

sohel said...

Oh jishan its you,, I just had gone through your blogs and at the end i found the name jishan then i discovered its you,, Nice to have it, its really a nice article and helpful.. Thanks.. keep it up..

Anonymous said...

thank you, this is pretty useful!

Anonymous said...

awesome!

Anonymous said...

Any idea why I would get the following error? I followed your example.

log4j:ERROR Could not instantiate appender named "zeroconf".
log4j:ERROR Could not instantiate class [org.apache.log4j.net.SocketAppender].
java.lang.SecurityException: class "org.apache.log4j.net.SocketAppender"'s signer information does not match signer information of other classes in the same package
at java.lang.ClassLoader.checkCerts(ClassLoader.java:787)
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:502)
at java.lang.ClassLoader.defineClass(ClassLoader.java:628)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:277)
at java.net.URLClassLoader.access$000(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:212)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:186)
at org.apache.log4j.helpers.Loader.loadClass(Loader.java:179)
at org.apache.log4j.helpers.OptionConverter.instantiateByClassName(OptionConverter.java:320)
at org.apache.log4j.helpers.OptionConverter.instantiateByKey(OptionConverter.java:121)
at org.apache.log4j.PropertyConfigurator.parseAppender(PropertyConfigurator.java:664)
at org.apache.log4j.PropertyConfigurator.parseCategory(PropertyConfigurator.java:647)
at org.apache.log4j.PropertyConfigurator.configureRootCategory(PropertyConfigurator.java:544)
at org.apache.log4j.PropertyConfigurator.doConfigure(PropertyConfigurator.java:440)
at org.apache.log4j.PropertyConfigurator.doConfigure(PropertyConfigurator.java:476)
at org.apache.log4j.helpers.OptionConverter.selectAndConfigure(OptionConverter.java:471)
at org.apache.log4j.LogManager.(LogManager.java:125)
at org.apache.log4j.Logger.getLogger(Logger.java:118)
at com.fisglobal.ChainsawTest.(ChainsawTest.java:7)

Anonymous said...

I am also getting the same exception

Stefan said...

I was using Chainsaw for a few year but recently I have changed log viewer to OtrosLogViewer (http://code.google.com/p/otroslogviewer/). It supports browsing logs from remote servers :)