The Implementation
My ExtendedLogger
Starting at the beginning, I spent (probably way too much) time thinking about whether
to call this class Loggerlog4jLoggerLoggerLoggerLoggerimportExtendedLoggerLoggerLogger
ExtendedLoggerLoggerExtendedLoggernewprivatestaticpublicgetLogger()wrapped.
The other critical thing that the factory methods do is ensure that log4j is configured properly.
They all call configureIfNecessary()configure()LoggerLoggergetLogger()configure()Places.CONFIGPlaces enum last month), and then calls one of the standard log4j configuration
methods, depending on the file type (xml or properties).
You obviously have to use ExtendedLogger
The bulk of the ExtendedLogger
The one oddity is in the low-level call that does the actual logging (in print()
wrapped.log( fullyQualifiedClassName, level, message, e );
That fullyQualifiedClassName
private static final String fullyQualifiedClassName = ExtendedLogger.class.getName();
Log4j uses class name to get the location information (file name, line number, etc.) that it prints in the log message. To see how, consider the following short program:
package com.holub;
class log4jLogger
{ public void log(String message)
{ try
{ throw new Exception("foo");
}
catch( Exception e )
{ StackTraceElement[] elements = e.getStackTrace();
int i = 0;
for( StackTraceElement current : elements )
{ System.out.printf("elements[%2d]: %24s %4s [%s, line %d]\n",
i++,
current.getClassName(),
current.getMethodName(),
current.getFileName(),
current.getLineNumber() );
}
}
}
}
class Wrapper
{ log4jLogger wrapped = new log4jLogger();
public void log( String message )
{ wrapped.log(message);
}
}
public class LogWrapperDemo
{ public static void main( String[] args )
{ a();
}
static void a(){ B.b(); }
}
class B
{ static void b(){ C.c(); }
}
class C
{
static final Wrapper log = new Wrapper();
static void c(){ log.log("hello"); }
}
The log4jLoggerWrapperExtendedLoggermain()LogWrapperDemoa()B.b()C.c()
elements[ 0]: com.holub.log4jLogger log [LogWrapperDemo.java, line 6]
elements[ 1]: com.holub.Wrapper log [LogWrapperDemo.java, line 26]
elements[ 2]: com.holub.C c [LogWrapperDemo.java, line 42]
elements[ 3]: com.holub.B b [LogWrapperDemo.java, line 37]
elements[ 4]: com.holub.LogWrapperDemo a [LogWrapperDemo.java, line 34]
elements[ 5]: com.holub.LogWrapperDemo main [LogWrapperDemo.java, line 32]
This output is, of course, just the runtime stack trace—the sequence of calls from main(...)elements[5]elements[0]
Putting ourselves in the place of log4j, the log()java.lang.StackTraceElmentfullyQualifiedClassNamelog(...)com.holub.LogWrapperfullyQualifiedClassName
Moving on, the new, printf()printf()PrintWriter.printf()log()
Exception e;
//...
log.debug("The stack trace is: [%128.128s]\n", e );
To output the same text you'd get when you called e.printStackTrace()
log.debug("The message is: %s\n", e.getMessage() );
but the ThrowableExceptiongetStackTrace()printif()ThrowableStringThrowableString...)
in a method definition's argument list are just semantic sugar.
Argument lists are actually passed into "varargs" methods as arrays,
so just mentally replace the ... with []. A call to
System.out.printf("%s %s", hello, world );
is translated by the compiler into something like this:
System.out.printf("%s %s", new Object[]{hello, world} );
The final programming issue has to do with handling an ambiguity in the method overloads. The problem is that I wanted to support both the old-style exception logging:
Throwable e;
//...
log4jlogger.debug("Message", e);
and printf-style
Throwable e;
//...
log4jlogger.debug("The stack trace is: %s\n", e);
The compiler looks at those two calls and sees an ambiguity:
There are two overloads of debug()
String debug( String message, Throwable t ); String debug( String format, Object... args );
Java resolves the ambiguity in favor of an exact match, so debug(String,Throwable)printf()
I solve the problem in print()printf()"%3$-10.5s". is legal), so simple string matching doesn't work.
The regular expression that matches them is on line 360, and the time consuming
operation (compiling the regular expression) is preformed only once when the class is loaded, so there isn't a huge
amount of overhead, here.
Finishing up, note that ExtendedLoggersynchronizedstaticwrappedfinalwrappedExtendedLoggerExtendedLgoger


