blob: 4888c08dbaed8a4bc176029999e0188085a2a54f [file] [log] [blame]
/*
* Copyright (c) 2003 The Visigoth Software Society. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowledgement:
* "This product includes software developed by the
* Visigoth Software Society (http://www.visigoths.org/)."
* Alternately, this acknowledgement may appear in the software itself,
* if and wherever such third-party acknowledgements normally appear.
*
* 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
* project contributors may be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact visigoths@visigoths.org.
*
* 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
* nor may "FreeMarker" or "Visigoth" appear in their names
* without prior written permission of the Visigoth Software Society.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Visigoth Software Society. For more
* information on the Visigoth Software Society, please see
* http://www.visigoths.org/
*/
package freemarker.log;
import java.util.HashMap;
import java.util.Map;
import freemarker.template.utility.ClassUtil;
/**
* The FreeMarker logging facility. This is a polymorphic implementation
* that will use whatever logging package it can find on the system:
* Apache Log4J, Apache Avalon LogKit, <tt>java.util.logging</tt> (in this
* order). If it fails to find any of the above, logging will be suppressed
* and a short notice will be printed to <tt>System.err</tt>.
*
* <p>You can use the {@link #selectLoggerLibrary(int)} static method to force
* use of a specific logger package, or to turn off logging.
*
* <p>SLF4J or Apache Commons Logging are supported (since FreeMarker 2.3.17)
* and recommended, but are never selected automatically (until FreeMarker 2.4)
* due to backward compatibility constraints; you have to select them
* explicitly with {@link #selectLoggerLibrary(int)}.
*
* @author Attila Szegedi
*/
public abstract class Logger
{
/**
* Constant used with {@link #selectLoggerLibrary(int)} that indicates the
* engine should automatically lookup and use any available logger library.
*/
public static final int LIBRARY_AUTO = -1;
/**
* Constant used with {@link #selectLoggerLibrary(int)} that indicates the
* engine should use no logger package (i.e. turn off logging).
*/
public static final int LIBRARY_NONE = 0;
/**
* Constant used with {@link #selectLoggerLibrary(int)} that indicates the
* engine should use the <tt>java.util.logging</tt> logger package.
*/
public static final int LIBRARY_JAVA = 1;
/**
* Constant used with {@link #selectLoggerLibrary(int)} that indicates the
* engine should use the Apache Jakarta Avalon LogKit logger package.
*/
public static final int LIBRARY_AVALON = 2;
/**
* Constant used with {@link #selectLoggerLibrary(int)} that indicates the
* engine should use the Apache Jakarta Log4J logger package.
*/
public static final int LIBRARY_LOG4J = 3;
/**
* Constant used with {@link #selectLoggerLibrary(int)} that indicates the
* engine should use the Apache commons-logging logger adapter package.
*/
public static final int LIBRARY_COMMONS = 4;
/**
* Constant used with {@link #selectLoggerLibrary(int)} that indicates the
* engine should use the SLF4J logger adapter package.
*/
public static final int LIBRARY_SLF4J = 5;
/**
* Order matters! Starts with the lowest priority.
*/
private static final String[] LIBINIT =
{
"freemarker.log.Logger", "_Null",
"java.util.logging.Logger", "_JDK14",
"org.apache.log.Logger", "_Avalon",
"org.apache.log4j.Logger", "_Log4J",
/* In 2.3.x this two is skipped by LIBRARY_AUTO: */
"org.apache.commons.logging.Log", "CommonsLogging",
"org.slf4j.Logger", "SLF4J",
};
private static int logLibrary;
private static LoggerFactory factory;
private static String categoryPrefix = "";
private static final Map loggers = new HashMap();
/**
* Selects the logger library to use.
* If you want to change the default setting, do it early in application
* initialization phase, before calling any other FreeMarker API since once
* various parts of the FreeMarker library bind to the logging subsystem,
* the change in this value will have no effect on them.
* @param library one of <tt>LIBRARY_XXX</tt> constants. By default,
* {@link #LIBRARY_AUTO} is used.
* @throws ClassNotFoundException if an explicit logging library is asked for
* (that is, neither NONE, nor AUTO), and it's not found in the classpath.
*/
public static void selectLoggerLibrary(int library)
throws
ClassNotFoundException
{
synchronized (Logger.class) {
if(library < -1 || (library*2) >= LIBINIT.length)
{
throw new IllegalArgumentException();
}
logLibrary = library;
factory = createFactory();
}
}
/**
* Sets a category prefix. This prefix is prepended to any logger category
* name. This makes it possible to have different FreeMarker logger categories
* on a per-application basis (better said, per-classloader basis). By default
* the category prefix is the empty string. If you set a non-empty category
* prefix, be sure to include the trailing separator dot (i.e. "MyApp.")
* If you want to change the default setting, do it early in application
* initialization phase, before calling any other FreeMarker API since once
* various parts of the FreeMarker library bind to the logging subsystem,
* the change in this value will have no effect on them.
*/
public static void setCategoryPrefix(String prefix)
{
synchronized (Logger.class) {
if(prefix == null)
{
throw new IllegalArgumentException();
}
categoryPrefix = prefix;
}
}
/**
* Logs a debugging message.
*/
public abstract void debug(String message);
/**
* Logs a debugging message with accompanying throwable.
*/
public abstract void debug(String message, Throwable t);
/**
* Logs an informational message.
*/
public abstract void info(String message);
/**
* Logs an informational message with accompanying throwable.
*/
public abstract void info(String message, Throwable t);
/**
* Logs a warning message.
*/
public abstract void warn(String message);
/**
* Logs a warning message with accompanying throwable.
*/
public abstract void warn(String message, Throwable t);
/**
* Logs an error message.
*/
public abstract void error(String message);
/**
* Logs an error message with accompanying throwable.
*/
public abstract void error(String message, Throwable t);
/**
* Returns true if this logger will log debug messages.
*/
public abstract boolean isDebugEnabled();
/**
* Returns true if this logger will log informational messages.
*/
public abstract boolean isInfoEnabled();
/**
* Returns true if this logger will log warning messages.
*/
public abstract boolean isWarnEnabled();
/**
* Returns true if this logger will log error messages.
*/
public abstract boolean isErrorEnabled();
/**
* Returns true if this logger will log fatal error messages.
*/
public abstract boolean isFatalEnabled();
/**
* Returns a logger for the specified category.
* @param category a dot separated hierarchical category name. If a category
* prefix is in effect, it's prepended to the category name.
*/
public static Logger getLogger(String category)
{
if (factory == null) {
synchronized (Logger.class) {
if (factory == null) {
try
{
selectLoggerLibrary(LIBRARY_AUTO);
}
catch(ClassNotFoundException e)
{
// This can't happen, really
throw new RuntimeException(e.getMessage());
}
}
}
}
category = categoryPrefix + category;
synchronized(loggers)
{
Logger logger = (Logger)loggers.get(category);
if(logger == null)
{
logger = factory.getLogger(category);
loggers.put(category, logger);
}
return logger;
}
}
private static LoggerFactory createFactory()
throws
ClassNotFoundException
{
if(logLibrary == LIBRARY_AUTO)
{
for(int i = LIBINIT.length / 2 - 1; i > 0; --i)
{
// For backward-compatibility we skip these in 2.3.x:
if (i == LIBRARY_SLF4J || i == LIBRARY_COMMONS) continue;
try
{
return createFactory(i);
}
catch(ClassNotFoundException e)
{
;//Intentionally ignored
}
}
System.err.println("*** WARNING: FreeMarker logging suppressed.");
return new _NullLoggerFactory();
}
else
{
return createFactory(logLibrary);
}
}
private static LoggerFactory createFactory(int library)
throws
ClassNotFoundException
{
String loggerClassName = LIBINIT[library * 2];
String factoryType = LIBINIT[library * 2 + 1];
try
{
ClassUtil.forName(loggerClassName);
return (LoggerFactory)Class.forName("freemarker.log." + factoryType + "LoggerFactory").newInstance();
}
catch(IllegalAccessException e)
{
// This can't happen, really
throw new IllegalAccessError(e.getMessage());
}
catch(InstantiationException e)
{
// This can't happen, really
throw new InstantiationError(e.getMessage());
}
}
}