A simple log wrapper that allows samskivert itself and libraries that depend on

samskivert to use logging services without creating a dependency on a logging
implementation.

This is basically what JCL (Jakarta Commons Logging) does except they do
auto-detection based on what's in the classpath and we're only going to
auto-detect log4j and only use it if we see that a log4j configuration has been
provided via a system property (or maybe not at all, I'm not sure yet). The
point is avoid the major gripe about JCL which is that when you stick some
logging system's jar file into your classpath for some other dependency (or
it's there for unknown reasons like maybe your servlet container uses it), all
of a sudden your log output disappears or breaks.

I had an idea while doing this and couldn't resist slipping it in: all of the
logging methods take varargs additional parameters which are a set of key/value
pairs to append to the log message in our standard format with an optional
final exception to be logged as well.

So instead of:

  log.warning("Oh crap, the fibbleminscher has exploded [state=" + 15 + 
              ", monkeys=" + StringUtil.toString(_monkeys) + "].");

You do:

  log.warning("Oh crap, the fibbleminscher has exploded.", "state", 15,
              "monkeys", _monkeys);

The formatter automatically does the StringUtil.toString magic on the arguments
(and does it safely so that if toString throws an exception the log message
isn't lost). You can also throw an exception in the mix:

  log.warning("Oh crap, the fibbleminscher has exploded.", "state", 15,
              "monkeys", _monkeys, ioe);

Or supply one with no parameters:

  log.warning("Oh mammy!", ioe);

Or even just log an exception if you have nothing pithy to add:

  log.warning(ioe);

Some research shows that other people have started doing this in their logging
systems as well under the fancier name of parameterized logging. The big
benefit is that you can pass objects that are expensive to toString() and avoid
toString()ing them unless you're actually going to generate the log message.
In our case we also get the benefit of our standard formatting and less
cluttered code (begone lots of + ", ...=" + typing).

I'm going to be doing away with the old samskivert Log code and converting all
of our libraries (and projects) to use this new shim so that we can easily
switch to using log4j, which for other reasons we want to use instead of Java's
built-in logging.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@2311 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2008-05-27 18:36:32 +00:00
parent ec0069f802
commit 4af8953a19
2 changed files with 278 additions and 0 deletions
@@ -0,0 +1,97 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2008 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.util;
import java.util.logging.Level;
/**
* A factory for creating JDK14 logger implementations. Automatically configures a formatter to
* display log messages on a single line unless the following system property is set:
* <pre>com.samskivert.util.JDK14Logger.noFormatter=true</pre>
*/
public class JDK14Logger implements Logger.Factory
{
// from interface Logger.Factory
public void init ()
{
try {
if (!Boolean.getBoolean("com.samskivert.util.JDK14Logger.noFormatter")) {
OneLineLogFormatter.configureDefaultHandler();
}
} catch (SecurityException se) {
// running in sandbox; no custom logging; no problem!
}
}
// from interface Logger.Factory
public Logger getLogger (String name)
{
return new Impl(java.util.logging.Logger.getLogger(name));
}
// from interface Logger.Factory
public Logger getLogger (Class clazz)
{
return getLogger(clazz.getName());
}
protected static class Impl extends Logger
{
public Impl (java.util.logging.Logger impl)
{
_impl = impl;
}
@Override // from Logger
public void debug (Object message, Object... args)
{
if (_impl.isLoggable(Level.FINE)) {
_impl.log(Level.FINE, format(message, args), getException(message, args));
}
}
@Override // from Logger
public void info (Object message, Object... args)
{
if (_impl.isLoggable(Level.INFO)) {
_impl.log(Level.INFO, format(message, args), getException(message, args));
}
}
@Override // from Logger
public void warning (Object message, Object... args)
{
if (_impl.isLoggable(Level.WARNING)) {
_impl.log(Level.WARNING, format(message, args), getException(message, args));
}
}
@Override // from Logger
public void error (Object message, Object... args)
{
if (_impl.isLoggable(Level.SEVERE)) {
_impl.log(Level.SEVERE, format(message, args), getException(message, args));
}
}
protected java.util.logging.Logger _impl;
}
}
+181
View File
@@ -0,0 +1,181 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2008 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.util;
/**
* Provides logging services to this library and others which depend on this library in such a way
* that they can be used in a larger project and easily be made to log to the logging framework in
* use by that project.
*
* <p> The default implementation uses log4j if it is available and falls back to the Java logging
* services if not. A specific (or custom) logging implementation can be configured like so:
* <pre>-Dcom.samskivert.util.Logger=com.samskivert.util.Log4Logger</pre>
*
* <p> One additional enhancement is the use of varargs log methods to allow for some handy
* automatic formatting. The methods take a log message and list of zero or more additional
* parameters. Those parameters should be key/value pairs which will be logged like so:
* <pre>message [key=value, key=value, key=value]</pre>
*
* <p> The final parameter can optionally be a Throwable, which will be supplied to the underlying
* log system as an exception to accompany the log message.
*/
public abstract class Logger
{
/**
* Obtains a logger with the specified name.
*/
public static Logger getLogger (String name)
{
return _factory.getLogger(name);
}
/**
* Obtains a logger with a name equal to the supplied class's name.
*/
public static Logger getLogger (Class clazz)
{
return _factory.getLogger(clazz);
}
/**
* Logs a debug message.
*
* @param message the message to be logged.
* @param args a list of key/value pairs and an optional final Throwable.
*/
public abstract void debug (Object message, Object... args);
/**
* Logs an info message.
*
* @param message the message to be logged.
* @param args a list of key/value pairs and an optional final Throwable.
*/
public abstract void info (Object message, Object... args);
/**
* Logs a warning message.
*
* @param message the message to be logged.
* @param args a list of key/value pairs and an optional final Throwable.
*/
public abstract void warning (Object message, Object... args);
/**
* Logs an error message.
*
* @param message the message to be logged.
* @param args a list of key/value pairs and an optional final Throwable.
*/
public abstract void error (Object message, Object... args);
/**
* Format messages and arguments. For use by logger implementations.
*/
protected String format (Object message, Object[] args)
{
StringBuilder buf = new StringBuilder();
buf.append(message);
if (args != null && args.length > 1) {
buf.append(" [");
for (int ii = 0, nn = args.length/2; ii < nn; ii++) {
if (ii > 0) {
buf.append(", ");
}
buf.append(args[2*ii]).append("=");
try {
StringUtil.toString(buf, args[2*ii+1]);
} catch (Throwable t) {
buf.append("<toString() failure: " + t + ">");
}
}
buf.append("]");
}
return buf.toString();
}
/**
* Extracts the exception from the message and arguments (if there is one). For use by logger
* implementations.
*/
protected Throwable getException (Object message, Object[] args)
{
if (message instanceof Throwable) {
return (Throwable)message;
} else if (args.length % 2 == 1 && args[args.length-1] instanceof Throwable) {
return (Throwable)args[args.length-1];
} else {
return null;
}
}
/**
* Called at static initialization time. Selects and initializes our logging backend.
*/
protected static void initLogger ()
{
// if a custom class was specified as a system property, use that
_factory = createConfiguredFactory();
// TODO: create and try a log4j logger
// lastly, fall back to the Java logging system
if (_factory == null) {
_factory = new JDK14Logger();
}
// and finally initialize our factory
_factory.init();
}
/**
* A helper function for {@link #initLogger}.
*/
protected static Factory createConfiguredFactory ()
{
String implClass = System.getProperty("com.samskivert.util.Log");
if (!StringUtil.isBlank(implClass)) {
try {
return (Factory)Class.forName(implClass).newInstance();
} catch (Throwable t) {
System.err.println("Unable to instantiate logging implementation: " + implClass);
t.printStackTrace(System.err);
}
}
return null;
}
/**
* Used to create logger instances.
*/
protected static interface Factory
{
public void init ();
public Logger getLogger (String name);
public Logger getLogger (Class clazz);
}
protected static Factory _factory;
static {
initLogger();
}
}