001: /*
002: * Copyright (c) 2003 The Visigoth Software Society. All rights
003: * reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * 2. Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowledgement:
019: * "This product includes software developed by the
020: * Visigoth Software Society (http://www.visigoths.org/)."
021: * Alternately, this acknowledgement may appear in the software itself,
022: * if and wherever such third-party acknowledgements normally appear.
023: *
024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
025: * project contributors may be used to endorse or promote products derived
026: * from this software without prior written permission. For written
027: * permission, please contact visigoths@visigoths.org.
028: *
029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
030: * nor may "FreeMarker" or "Visigoth" appear in their names
031: * without prior written permission of the Visigoth Software Society.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of the Visigoth Software Society. For more
049: * information on the Visigoth Software Society, please see
050: * http://www.visigoths.org/
051: */
052:
053: package freemarker.log;
054:
055: import java.util.HashMap;
056: import java.util.Map;
057:
058: import freemarker.template.utility.ClassUtil;
059:
060: /**
061: * The FreeMarker logging facility. This is a polymorphic implementation
062: * that will use whatever logging package it can find on the system:
063: * Apache Jakarta Log4J, Apache Jakarta Avalon LogKit, JDK1.4 logging
064: * (in this order). If it fails to find any of the above, logging will
065: * be suppressed and a short notice output to System.err. You can use the
066: * {@link #selectLoggerLibrary(int)} static method to force use of a specific
067: * logger package, or to turn off logging.
068: * @version $Id: Logger.java,v 1.24 2003/10/03 15:35:10 stephanmueller Exp $
069: * @author Attila Szegedi
070: */
071: public abstract class Logger {
072: /**
073: * Constant used with {@link #selectLoggerLibrary(int)} that indicates the
074: * engine should automatically lookup and use any available logger library.
075: */
076: public static final int LIBRARY_AUTO = -1;
077: /**
078: * Constant used with {@link #selectLoggerLibrary(int)} that indicates the
079: * engine should use no logger package (i.e. turn off logging).
080: */
081: public static final int LIBRARY_NONE = 0;
082: /**
083: * Constant used with {@link #selectLoggerLibrary(int)} that indicates the
084: * engine should use the <tt>java.util.logging</tt> logger package.
085: */
086: public static final int LIBRARY_JAVA = 1;
087: /**
088: * Constant used with {@link #selectLoggerLibrary(int)} that indicates the
089: * engine should use the Apache Jakarta Avalon LogKit logger package.
090: */
091: public static final int LIBRARY_AVALON = 2;
092: /**
093: * Constant used with {@link #selectLoggerLibrary(int)} that indicates the
094: * engine should use the Apache Jakarta Log4J logger package.
095: */
096: public static final int LIBRARY_LOG4J = 3;
097:
098: private static final String[] LIBINIT = { "freemarker.log", "Null",
099: "java.util.logging", "JDK14", "org.apache.log", "Avalon",
100: "org.apache.log4j", "Log4J" };
101:
102: private static int logLibrary;
103: private static LoggerFactory factory;
104: private static String categoryPrefix = "";
105:
106: private static final Map loggers = new HashMap();
107:
108: /**
109: * Selects the logger library to use.
110: * If you want to change the default setting, do it early in application
111: * initialization phase, before calling any other FreeMarker API since once
112: * various parts of the FreeMarker library bind to the logging subsystem,
113: * the change in this value will have no effect on them.
114: * @param library one of <tt>LIBRARY_XXX</tt> constants. By default,
115: * {@link #LIBRARY_AUTO} is used.
116: * @throws ClassNotFoundException if an explicit logging library is asked for
117: * (that is, neither NONE, nor AUTO), and it is not found in the classpath.
118: */
119: public static void selectLoggerLibrary(int library)
120: throws ClassNotFoundException {
121: synchronized (Logger.class) {
122: if (library < -1 || (library * 2) >= LIBINIT.length) {
123: throw new IllegalArgumentException();
124: }
125: logLibrary = library;
126: factory = createFactory();
127: }
128: }
129:
130: /**
131: * Sets a category prefix. This prefix is prepended to any logger category
132: * name. This makes it possible to have different FreeMarker logger categories
133: * on a per-application basis (better said, per-classloader basis). By default
134: * the category prefix is the empty string. If you set a non-empty category
135: * prefix, be sure to include the trailing separator dot (i.e. "MyApp.")
136: * If you want to change the default setting, do it early in application
137: * initialization phase, before calling any other FreeMarker API since once
138: * various parts of the FreeMarker library bind to the logging subsystem,
139: * the change in this value will have no effect on them.
140: */
141: public static void setCategoryPrefix(String prefix) {
142: synchronized (Logger.class) {
143: if (prefix == null) {
144: throw new IllegalArgumentException();
145: }
146: categoryPrefix = prefix;
147: }
148: }
149:
150: /**
151: * Logs a debugging message.
152: */
153: public abstract void debug(String message);
154:
155: /**
156: * Logs a debugging message with accompanying throwable.
157: */
158: public abstract void debug(String message, Throwable t);
159:
160: /**
161: * Logs an informational message.
162: */
163: public abstract void info(String message);
164:
165: /**
166: * Logs an informational message with accompanying throwable.
167: */
168: public abstract void info(String message, Throwable t);
169:
170: /**
171: * Logs a warning message.
172: */
173: public abstract void warn(String message);
174:
175: /**
176: * Logs a warning message with accompanying throwable.
177: */
178: public abstract void warn(String message, Throwable t);
179:
180: /**
181: * Logs an error message.
182: */
183: public abstract void error(String message);
184:
185: /**
186: * Logs an error message with accompanying throwable.
187: */
188: public abstract void error(String message, Throwable t);
189:
190: /**
191: * Returns true if this logger will log debug messages.
192: */
193: public abstract boolean isDebugEnabled();
194:
195: /**
196: * Returns true if this logger will log informational messages.
197: */
198: public abstract boolean isInfoEnabled();
199:
200: /**
201: * Returns true if this logger will log warning messages.
202: */
203: public abstract boolean isWarnEnabled();
204:
205: /**
206: * Returns true if this logger will log error messages.
207: */
208: public abstract boolean isErrorEnabled();
209:
210: /**
211: * Returns true if this logger will log fatal error messages.
212: */
213: public abstract boolean isFatalEnabled();
214:
215: /**
216: * Returns a logger for the specified category.
217: * @param category a dot separated hierarchical category name. If a category
218: * prefix is in effect, it is prepended to the category name.
219: */
220: public static Logger getLogger(String category) {
221: if (factory == null) {
222: synchronized (Logger.class) {
223: if (factory == null) {
224: try {
225: selectLoggerLibrary(LIBRARY_AUTO);
226: } catch (ClassNotFoundException e) {
227: // This can't happen, really
228: throw new RuntimeException(e.getMessage());
229: }
230: }
231: }
232: }
233:
234: category = categoryPrefix + category;
235:
236: synchronized (loggers) {
237: Logger logger = (Logger) loggers.get(category);
238: if (logger == null) {
239: logger = factory.getLogger(category);
240: loggers.put(category, logger);
241: }
242: return logger;
243: }
244: }
245:
246: private static LoggerFactory createFactory()
247: throws ClassNotFoundException {
248: if (logLibrary == LIBRARY_AUTO) {
249: for (int i = LIBINIT.length / 2 - 1; i > 0; --i) {
250: try {
251: return createFactory(i);
252: } catch (ClassNotFoundException e) {
253: ;//Intentionally ignored
254: }
255: }
256: System.err
257: .println("*** WARNING: FreeMarker logging suppressed.");
258: return new NullLoggerFactory();
259: } else {
260: return createFactory(logLibrary);
261: }
262: }
263:
264: private static LoggerFactory createFactory(int library)
265: throws ClassNotFoundException {
266: String requiredPackage = LIBINIT[library * 2];
267: String factoryType = LIBINIT[library * 2 + 1];
268:
269: try {
270: ClassUtil.forName(requiredPackage + ".Logger");
271: return (LoggerFactory) Class.forName(
272: "freemarker.log." + factoryType + "LoggerFactory")
273: .newInstance();
274: } catch (IllegalAccessException e) {
275: // This can't happen, really
276: throw new IllegalAccessError(e.getMessage());
277: } catch (InstantiationException e) {
278: // This can't happen, really
279: throw new InstantiationError(e.getMessage());
280: }
281: }
282: }
|