/** *
* Copyrights: No copyrights. Use however you want. * *
* Warranty: Comes with absolutely no * warranty. As Dijkstra said, "I have only proved the code * correct, I haven't actually tested it." * *
* Credits: Credits are appreciated, not required. */ package edu.gwu.logging; import java.net.*; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.ObjectOutputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.logging.LogRecord; import java.util.logging.Handler; import java.util.logging.StreamHandler; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.spi.LoggingEvent; /** * An adapter for using Log4j Chainsaw with JDK logging infrastructure. * The JDK logging publishes {@link LogRecord} objects, this adapter * creates a LoggingEvent from a LogRecord and writes it to Chainsaw socket. * * @author Amrinder Arora */ public class ChainsawHandler extends StreamHandler { /** Number of consecutive errors, exceeding which this handler is disabled */ public static int MAX_ERRORS = 10; /** Number of consecutive errors that have occurred since starting this handler */ protected static int numErrors = 0; /** Is this handler disabled? */ protected static boolean disabled = false; /** Chainsaw socket */ protected static Socket targetSocket = null; /** Chainsaw Inet address String representation */ protected static String targetInetAddressProp = "localhost"; /** Chainsaw inet address (value derived from {@link #targetInetAddressProp}) */ protected static InetAddress targetInetAddress = null; /** Chainsaw port number, as a String */ protected static String chainsawPortProp = "4445"; /** MAX_ERRORS, as a String */ protected static String MAX_ERRORS_prop = "10"; /** Chainsaw port number (value derived from {@link #chainsawPortProp}) */ protected static int chainsawPort = 4445; /** * Object OutputStream that this ChainsawHandler writes to. A chainsaw GUI * application is hopefully listening at the other end of this stream. */ protected static ObjectOutputStream objectOutputStream = null; /** * Creates an instance of ChainsawHandler object. */ public ChainsawHandler () { System.err.println ("An instance of Chainsaw Handler is being created"); try { renewTarget (); } catch (Throwable t) { System.err.println ("Target was NOT successfully lookedup"); } } /** * Renews the object output stream. It does the following steps:
* If there is a problem in publishing this log record, it tries to
* renew the object output stream using
* {@link #renewTarget}, and increments the number of errors.
* If this log record is published successfully, {@link #numErrors} is reset to 0,
* since it holds the number of consecutive errors.
*
* @param logRecord The LogRecord to be published
*/
public void publish (LogRecord logRecord)
{
try {
if (disabled)
return;
// System.out.println ("Writing: '" + logRecord + "' on the chainsaw socket");
// (You cant log logging of a log. You would really be a log if you did that.)
LoggingEvent loggingEvent = getLoggingEvent (logRecord);
objectOutputStream.writeObject (loggingEvent);
numErrors = 0;
} catch (Throwable e) {
// Enclose the exception handling in another try catch block
// to make sure publish() does not throw exception back at user
try {
// Increments the number of consecutive errors
numErrors++;
System.err.println (e + "(Error # " + numErrors + ") writing this logRecord: " + logRecord);
// Gives up if too many errors
if (numErrors > MAX_ERRORS) {
System.err.println ("Number of consecutive errors has exceeded threshold, disabling this handler");
disabled = true;
}
// If this is the first error, prints the stack trace of the exception
// next consecutive errors will be ignored.
if (numErrors == 1)
e.printStackTrace();
// Attempts to renew the target (maybe GUI is running now/network
// is now up, etc, other retry conditions)
renewTarget ();
} catch (Throwable ignored) {
// ignores this Throwable, doesn't even log it, to give adequate guarantee that
// publish() does not throw exception back at user
}
}
}
/**
* Converts a {@link java.util.logger.LogRecord} to a
* {@link org.apache.log4j.spi.LoggingEvent}
*/
protected LoggingEvent getLoggingEvent (LogRecord logRecord)
{
String fqn = logRecord.getLoggerName();
Logger logger = Logger.getLogger (fqn);
Level level = (Level) levelConversionMap.get (logRecord.getLevel());
long timestamp = logRecord.getMillis();
Object message = logRecord.getMessage();
Throwable t = logRecord.getThrown();
return new LoggingEvent (fqn, logger, timestamp, level, message, t);
}
/**
* Closes the resources held by this handler (socket and object output stream).
*/
public static void closeResources()
{
if (objectOutputStream != null) {
try {objectOutputStream.close();}
catch (Throwable t) {System.err.println ("Exception closing oos");}
}
if (targetSocket != null) {
try {targetSocket.close();}
catch (Throwable t) {System.err.println ("Exception closing socket");}
}
}
/**
* A level conversion map is used to translate levels from one logging
* infrastructure to another logging infrastructure.
*/
public static Map levelConversionMap = new HashMap();
static {
levelConversionMap.put (java.util.logging.Level.OFF, Level.OFF);
levelConversionMap.put (java.util.logging.Level.SEVERE, Level.FATAL);
levelConversionMap.put (java.util.logging.Level.WARNING, Level.WARN);
levelConversionMap.put (java.util.logging.Level.INFO, Level.INFO);
levelConversionMap.put (java.util.logging.Level.CONFIG, Level.INFO);
levelConversionMap.put (java.util.logging.Level.FINE, Level.DEBUG);
levelConversionMap.put (java.util.logging.Level.FINER, Level.DEBUG);
levelConversionMap.put (java.util.logging.Level.FINEST, Level.DEBUG);
levelConversionMap.put (java.util.logging.Level.ALL, Level.ALL);
}
/**
* Sets the chainsaw port.
*/
public void setChainsawPortProp (String argChainsawPortProp)
{
System.err.println ("setting chainsawPortProp to: " + argChainsawPortProp);
this.chainsawPortProp = argChainsawPortProp;
}
/**
* Sets the level.
*/
public void setLevel (String argLevel)
{
System.err.println ("setting Level to: " + argLevel);
this.chainsawPortProp = argLevel;
}
/**
* Initializes a chainsaw handler with configurable properties.
*
*
* Possible values for inetAddress property are the following: *