ceki 01/09/02 14:41:46 Added: src/java/org/apache/log4j Logger.java Log: New central class in the package. Revision Changes Path 1.1 jakarta-log4j/src/java/org/apache/log4j/Logger.java Index: Logger.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ // Contibutors: Alex Blewitt <[EMAIL PROTECTED]> // Markus Oestreicher <[EMAIL PROTECTED]> // Frank Hoering <[EMAIL PROTECTED]> // Nelson Minar <[EMAIL PROTECTED]> // Jim Cakalic <[EMAIL PROTECTED]> // Avy Sharell <[EMAIL PROTECTED]> // Ciaran Treanor <[EMAIL PROTECTED]> // Jeff Turner <[EMAIL PROTECTED]> // Michael Horwitz <[EMAIL PROTECTED]> // Calvin Chan <[EMAIL PROTECTED]> // Aaron Greenhouse <[EMAIL PROTECTED]> // Beat Meier <[EMAIL PROTECTED]> // Colin Sampaleanu <[EMAIL PROTECTED]> package org.apache.log4j; import org.apache.log4j.spi.RootCategory; import org.apache.log4j.spi.AppenderAttachable; import org.apache.log4j.spi.CategoryFactory; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.helpers.LogLog; import org.apache.log4j.helpers.NullEnumeration; import org.apache.log4j.helpers.OptionConverter; import org.apache.log4j.helpers.AppenderAttachableImpl; import org.apache.log4j.helpers.Loader; import org.apache.log4j.or.RendererMap; import org.apache.log4j.or.ObjectRenderer; import java.util.Enumeration; import java.util.Vector; import java.util.MissingResourceException; import java.text.MessageFormat; import java.util.ResourceBundle; import java.net.URL; import java.net.MalformedURLException; /** This is the central class in the log4j package. One of the distintive features of log4j are hierarchical categories and their evaluation. <p>See the <a href="../../../../manual.html">user manual</a> for an introduction on this class. @author Ceki Gülcü @author Anders Kristensen */ public class Logger implements AppenderAttachable { /** The hierarchy where categories are attached to by default. */ static public final Hierarchy defaultHierarchy = new Hierarchy(new RootCategory(Level.DEBUG)); /** This string constant is set to <b>log4j.properties</b> the name of the file that will be searched by default in classpath. If the file can be found, then it is fed to the {@link PropertyConfigurator}. See also {@link #DEFAULT_CONFIGURATION_KEY} for a more general alternative. <p>See also the full description of <a href="../../../../manual.html#defaultInit">default intialization</a> procedure. @since 0.8.5 */ static public final String DEFAULT_CONFIGURATION_FILE = "log4j.properties"; /** This string constant is set to <b>log4j.configuration</b>. <p>It corresponds to name of a system property that, if set, specifies the name of the resource containing the properties file or {@link URL} with which log4j should configure itself. See {@link OptionConverter#selectAndConfigure} for more detailed information on the processing of this option. <p>Setting the <b>log4j.configuration</b> system property overrides the default search for the file <b>log4j.properties</b>. <p>Note that all property keys are case sensitive. <p>See also the full description of <a href="../../../../manual.html#defaultInit">default intialization</a> procedure. @since 1.0 */ static final public String DEFAULT_CONFIGURATION_KEY="log4j.configuration"; /** This string constant is set to <b>log4j.configuratorClass</b>. <p>It corresponds to name of a system property that, if set, specifies the class name to use to automatically configure log4j. See {@link OptionConverter#selectAndConfigure} for more detailed information on the processing of this option. <p>Setting the <b>log4j.configuration</b> system property overrides the default search for the file <b>log4j.properties</b>. <p>Note that all property keys are case sensitive. <p>See also the full description of <a href="../../../../manual.html#defaultInit">default intialization</a> procedure. @since 1.2 */ static final public String CONFIGURATOR_CLASS_KEY="log4j.configuratorClass"; /** Setting the system property <b>log4j.defaultInitOverride</b> to "true" or any other value than "false" will skip default configuration process. <p>The current value of the DEFAULT_INIT_OVERRIDE_KEY string constant is <b>log4j.defaultInitOverride</b>. <p>See also the full description of <a href="../../../../manual.html#defaultInit">default intialization</a> procedure. <p>Note that all property keys are case sensitive. @since 0.8.5 */ public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride"; /** Search for the properties file log4j.properties in the CLASSPATH. */ static { String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY, null); // if there is no default init override, them get the resource // specified by the user or the default config file. if(override == null || "false".equalsIgnoreCase(override)) { String resource = OptionConverter.getSystemProperty( DEFAULT_CONFIGURATION_KEY, DEFAULT_CONFIGURATION_FILE); String configuratorClassName = OptionConverter.getSystemProperty( CONFIGURATOR_CLASS_KEY, null); URL url = null; try { // so, resource is not a URL: // attempt to get the resource from the class path url = new URL(resource); } catch (MalformedURLException ex) { url = Loader.getResource(resource); } // If we have a non-null url, then delegate the rest of the // configuration to the OptionConverter.selectAndConfigure // method. if(url != null) { LogLog.debug("Using URL ["+url+"] for automatic log4j configuration."); OptionConverter.selectAndConfigure(url, configuratorClassName, defaultHierarchy); } else { LogLog.debug("Could not find resource: ["+resource+"]."); } } } /** The name of this logger. */ protected String name; /** The assigned level of this logger. The <code>level</code> variable need not be assined a value in which case it is inherited form the hierarchy. */ volatile protected Level level; /** The parent of this logger. All categories have at least one ancestor which is the root logger. */ volatile protected Logger parent; /** The fully qualified name of the Logger class. See also the getFQCN method. */ private static final String FQCN = Logger.class.getName(); protected ResourceBundle resourceBundle; // Categories need to know what Hierarchy they are in protected Hierarchy hierarchy; AppenderAttachableImpl aai; /** Additivity is set to true by default, that is children inherit the appenders of their ancestors by default. If this variable is set to <code>false</code> then the appenders found in the ancestors of this logger are not used. However, the children of this logger will inherit its appenders, unless the children have their additivity flag set to <code>false</code> too. See the user manual for more details. */ protected boolean additive = true; /** This constructor created a new <code>Logger</code> instance and sets its name. <p>It is intended to be used by sub-classes only. You should not create categories directly. @param name The name of the logger. */ protected Logger(String name) { this.name = name; } /** Add <code>newAppender</code> to the list of appenders of this Logger instance. <p>If <code>newAppender</code> is already in the list of appenders, then it won't be added again. */ synchronized public void addAppender(Appender newAppender) { if(aai == null) { aai = new AppenderAttachableImpl(); } aai.addAppender(newAppender); hierarchy.fireAddAppenderEvent(this, newAppender); } /** If <code>assertion</code> parameter is <code>false</code>, then logs <code>msg</code> as an {@link #error(Object) error} statement. <p>The <code>assert</code> method has been renamed to <code>assertLog</code> because <code>assert</code> is a language reserved word in JDK 1.4. @param assertion @param msg The message to print if <code>assertion</code> is false. @since 1.2 */ public void assertLog(boolean assertion, String msg) { if(!assertion) this.error(msg); } /** Call the appenders in the hierrachy starting at <code>this</code>. If no appenders could be found, emit a warning. <p>This method calls all the appenders inherited from the hierarchy circumventing any evaluation of whether to log or not to log the particular log request. @param LoggingEvent the event to log. */ public void callAppenders(LoggingEvent event) { int writes = 0; for(Logger l = this; l != null; l=l.parent) { // Protected against simultaneous call to addAppender, removeAppender,... synchronized(l) { if(l.aai != null) { writes += l.aai.appendLoopOnAppenders(event); } if(!l.additive) { break; } } } // No appenders in hierarchy, warn user only once. if(!hierarchy.emittedNoAppenderWarning && writes == 0) { LogLog.error("No appenders could be found for logger (" + this.getName() + ")."); LogLog.error("Please initialize the log4j system properly."); hierarchy.emittedNoAppenderWarning = true; } } /** Close all attached appenders implementing the AppenderAttachable interface. @since 1.0 */ synchronized void closeNestedAppenders() { Enumeration enum = this.getAllAppenders(); if(enum != null) { while(enum.hasMoreElements()) { Appender a = (Appender) enum.nextElement(); if(a instanceof AppenderAttachable) { a.close(); } } } } /** Log a message object with the {@link Level#DEBUG DEBUG} level. <p>This method first checks if this logger is <code>DEBUG</code> enabled by comparing the level of this logger with the {@link Level#DEBUG DEBUG} level. If this logger is <code>DEBUG</code> enabled, then it converts the message object (passed as parameter) to a string by invoking the appropriate {@link ObjectRenderer}. It then proceeds to call all the registered appenders in this logger and also higher in the hierarchy depending on the value of the additivity flag. <p><b>WARNING</b> Note that passing a {@link Throwable} to this method will print the name of the <code>Throwable</code> but no stack trace. To print a stack trace use the {@link #debug(Object, Throwable)} form instead. @param message the message object to log. */ public void debug(Object message) { if(hierarchy.enableInt > Level.DEBUG_INT) return; if(Level.DEBUG.isGreaterOrEqual(this.getChainedLevel())) { forcedLog(FQCN, Level.DEBUG, message, null); } } /** Log a message object with the <code>DEBUG</code> level including the stack trace of the {@link Throwable} <code>t</code> passed as parameter. <p>See {@link #debug(Object)} form for more detailed information. @param message the message object to log. @param t the exception to log, including its stack trace. */ public void debug(Object message, Throwable t) { if(hierarchy.enableInt > Level.DEBUG_INT) return; if(Level.DEBUG.isGreaterOrEqual(this.getChainedLevel())) forcedLog(FQCN, Level.DEBUG, message, t); } //public //void dump() { // System.out.println("Logger " + name + " dump -----"); // for(Logger c = this; c != null; c=c.parent) // System.out.println("("+c.name+", "+c.level+") ->"); // System.out.println("---------------------------"); // //} /** Log a message object with the {@link Level#ERROR ERROR} Level. <p>This method first checks if this logger is <code>ERROR</code> enabled by comparing the level of this logger with {@link Level#ERROR ERROR} Level. If this logger is <code>ERROR</code> enabled, then it converts the message object passed as parameter to a string by invoking the appropriate {@link ObjectRenderer}. It proceeds to call all the registered appenders in this logger and also higher in the hierarchy depending on the value of the additivity flag. <p><b>WARNING</b> Note that passing a {@link Throwable} to this method will print the name of the <code>Throwable</code> but no stack trace. To print a stack trace use the {@link #error(Object, Throwable)} form instead. @param message the message object to log */ public void error(Object message) { if(hierarchy.enableInt > Level.ERROR_INT) return; if(Level.ERROR.isGreaterOrEqual(this.getChainedLevel())) forcedLog(FQCN, Level.ERROR, message, null); } /** Log a message object with the <code>ERROR</code> level including the stack trace of the {@link Throwable} <code>t</code> passed as parameter. <p>See {@link #error(Object)} form for more detailed information. @param message the message object to log. @param t the exception to log, including its stack trace. */ public void error(Object message, Throwable t) { if(hierarchy.enableInt > Level.ERROR_INT) return; if(Level.ERROR.isGreaterOrEqual(this.getChainedLevel())) forcedLog(FQCN, Level.ERROR, message, t); } /** If the named logger exists (in the default hierarchy) then it returns a reference to the logger, otherwise it returns <code>null</code>. <p>Contributed by Ciaran Treanor - [EMAIL PROTECTED] @version 0.8.5 */ public static Logger exists(String name) { return defaultHierarchy.exists(name); } /** Log a message object with the {@link Level#FATAL FATAL} Level. <p>This method first checks if this logger is <code>FATAL</code> enabled by comparing the level of this logger with {@link Level#FATAL FATAL} Level. If the logger is <code>FATAL</code> enabled, then it converts the message object passed as parameter to a string by invoking the appropriate {@link ObjectRenderer}. It proceeds to call all the registered appenders in this logger and also higher in the hierarchy depending on the value of the additivity flag. <p><b>WARNING</b> Note that passing a {@link Throwable} to this method will print the name of the Throwable but no stack trace. To print a stack trace use the {@link #fatal(Object, Throwable)} form instead. @param message the message object to log */ public void fatal(Object message) { if(hierarchy.enableInt > Level.FATAL_INT) return; if(Level.FATAL.isGreaterOrEqual(this.getChainedLevel())) forcedLog(FQCN, Level.FATAL, message, null); } /** Log a message object with the <code>FATAL</code> level including the stack trace of the {@link Throwable} <code>t</code> passed as parameter. <p>See {@link #fatal(Object)} for more detailed information. @param message the message object to log. @param t the exception to log, including its stack trace. */ public void fatal(Object message, Throwable t) { if(hierarchy.enableInt > Level.FATAL_INT) return; if(Level.FATAL.isGreaterOrEqual(this.getChainedLevel())) forcedLog(FQCN, Level.FATAL, message, t); } /** This method creates a new logging event and logs the event without further checks. */ protected void forcedLog(String fqcn, Level level, Object message, Throwable t) { callAppenders(new LoggingEvent(fqcn, this, level, message, t)); } /** Get the additivity flag for this Logger instance. */ public boolean getAdditivity() { return additive; } /** Get the appenders contained in this logger as an {@link Enumeration}. If no appenders can be found, then a {@link NullEnumeration} is returned. @return Enumeration An enumeration of the appenders in this logger. */ synchronized public Enumeration getAllAppenders() { if(aai == null) return NullEnumeration.getInstance(); else return aai.getAllAppenders(); } /** Look for the appender named as <code>name</code>. <p>Return the appender with that name if in the list. Return <code>null</code> otherwise. */ synchronized public Appender getAppender(String name) { if(aai == null || name == null) return null; return aai.getAppender(name); } /** Starting from this logger, search the logger hierarchy for a non-null level and return it. Otherwise, return the level of the root logger. <p>The Logger class is designed so that this method executes as quickly as possible. */ public Level getChainedLevel() { for(Logger c = this; c != null; c=c.parent) { if(c.level != null) return c.level; } return null; // If reached will cause an NullPointerException. } /** Returns all the currently defined categories in the default hierarchy as an {@link java.util.Enumeration Enumeration}. <p>The root logger is <em>not</em> included in the returned {@link Enumeration}. */ public static Enumeration getCurrentCategories() { return defaultHierarchy.getCurrentCategories(); } /** Return the default Hierarchy instance. @since 1.0 */ public static Hierarchy getDefaultHierarchy() { return defaultHierarchy; } /** Return the the {@link Hierarchy} where this <code>Logger</code> instance is attached. @since 1.1 */ public Hierarchy getHierarchy() { return hierarchy; } /** Retrieve a logger with named as the <code>name</code> parameter. If the named logger already exists, then the existing instance will be reutrned. Otherwise, a new instance is created. By default, categories do not have a set level but inherit it from the hierarchy. This is one of the central features of log4j. @param name The name of the logger to retrieve. */ public static Logger getLogger(String name) { return defaultHierarchy.getLogger(name); } /** Shorthand for <code>getLogger(clazz.getName())</code>. @param clazz The name of <code>clazz</code> will be used as the name of the logger to retrieve. See {@link #getInstance(String)} for more detailed information. */ public static Logger getLogger(Class clazz) { return getLogger(clazz.getName()); } /** Like {@link #getInstance(String)} except that the type of logger instantiated depends on the type returned by the {@link CategoryFactory#makeNewCategoryInstance} method of the <code>factory</code> parameter. <p>This method is intended to be used by sub-classes. @param name The name of the category to retrieve. @param factory A {@link CategoryFactory} implementation that will actually create a new Instance. @since 0.8.5 */ public static Logger getLogger(String name, CategoryFactory factory) { return defaultHierarchy.getLogger(name, factory); } /** Return the logger name. */ public final String getName() { return name; } /** Returns the parent of this logger. Note that the parent of a given logger may change during the lifetime of the logger. <p>The root logger will return <code>null</code>. @since 1.2 */ final public Logger getParent() { return this.parent; } /** Returns the assigned {@link Level}, if any, for this Logger. @return Level - the assigned Level, can be <code>null</code>. */ final public Level getLevel() { return this.level; } /** @deprecated Please use {@link #getLevel} instead. */ final public Level getPriority() { return this.level; } /** Return the root of the default logger hierrachy. <p>The root logger is always instantiated and available. It's name is "root". <p>Nevertheless, calling {@link #getInstance Category.getInstance("root")} does not retrieve the root category but a category just under root named "root". */ final public static Logger getRootLogger() { return defaultHierarchy.getRootLogger(); } /** Return the <em>inherited</em> {@link ResourceBundle} for this logger. <p>This method walks the hierarchy to find the appropriate resource bundle. It will return the resource bundle attached to the closest ancestor of this logger, much like the way priorities are searched. In case there is no bundle in the hierarchy then <code>null</code> is returned. @since 0.9.0 */ public ResourceBundle getResourceBundle() { for(Logger c = this; c != null; c=c.parent) { if(c.resourceBundle != null) return c.resourceBundle; } // It might be the case that there is no resource bundle return null; } /** Returns the string resource coresponding to <code>key</code> in this logger's inherited resource bundle. See also {@link #getResourceBundle}. <p>If the resource cannot be found, then an {@link #error error} message will be logged complaining about the missing resource. */ protected String getResourceBundleString(String key) { ResourceBundle rb = getResourceBundle(); // This is one of the rare cases where we can use logging in order // to report errors from within log4j. if(rb == null) { if(!hierarchy.emittedNoResourceBundleWarning) { error("No resource bundle has been set for logger "+name); hierarchy.emittedNoResourceBundleWarning = true; } return null; } else { try { return rb.getString(key); } catch(MissingResourceException mre) { error("No resource is associated with key \""+key+"\"."); return null; } } } /** Log a message object with the {@link Level#INFO INFO} Level. <p>This method first checks if this logger is <code>INFO</code> enabled by comparing the level of this logger with {@link Level#INFO INFO} Level. If the logger is <code>INFO</code> enabled, then it converts the message object passed as parameter to a string by invoking the appropriate {@link ObjectRenderer}. It proceeds to call all the registered appenders in this logger and also higher in the hierarchy depending on the value of the additivity flag. <p><b>WARNING</b> Note that passing a {@link Throwable} to this method will print the name of the Throwable but no stack trace. To print a stack trace use the {@link #info(Object, Throwable)} form instead. @param message the message object to log */ public void info(Object message) { if(hierarchy.enableInt > Level.INFO_INT) return; if(Level.INFO.isGreaterOrEqual(this.getChainedLevel())) forcedLog(FQCN, Level.INFO, message, null); } /** Log a message object with the <code>INFO</code> level including the stack trace of the {@link Throwable} <code>t</code> passed as parameter. <p>See {@link #info(Object)} for more detailed information. @param message the message object to log. @param t the exception to log, including its stack trace. */ public void info(Object message, Throwable t) { if(hierarchy.enableInt > Level.INFO_INT) return; if(Level.INFO.isGreaterOrEqual(this.getChainedLevel())) forcedLog(FQCN, Level.INFO, message, t); } /** * Check whether this logger is enabled for the <code>DEBUG</code> * Level. * * <p> This function is intended to lessen the computational cost of * disabled log debug statements. * * <p> For some <code>cat</code> Logger object, when you write, * <pre> * cat.debug("This is entry number: " + i ); * </pre> * * <p>You incur the cost constructing the message, concatenatiion in * this case, regardless of whether the message is logged or not. * * <p>If you are worried about speed, then you should write * <pre> * if(cat.isDebugEnabled()) { * cat.debug("This is entry number: " + i ); * } * </pre> * * <p>This way you will not incur the cost of parameter * construction if debugging is disabled for <code>cat</code>. On * the other hand, if the <code>cat</code> is debug enabled, you * will incur the cost of evaluating whether the logger is debug * enabled twice. Once in <code>isDebugEnabled</code> and once in * the <code>debug</code>. This is an insignificant overhead * since evaluating a logger takes about 1%% of the time it * takes to actually log. * * @return boolean - <code>true</code> if this logger is debug * enabled, <code>false</code> otherwise. * */ public boolean isDebugEnabled() { if(hierarchy.enableInt > Level.DEBUG_INT) return false; return Level.DEBUG.isGreaterOrEqual(this.getChainedLevel()); } /** Check whether this logger is enabled for a given {@link Level} passed as parameter. See also {@link #isDebugEnabled}. @return boolean True if this logger is enabled for <code>level</code>. */ public boolean isEnabledFor(Level level) { if(hierarchy.enableInt > level.level) return false; return level.isGreaterOrEqual(this.getChainedLevel()); } /** Check whether this logger is enabled for the info Level. See also {@link #isDebugEnabled}. @return boolean - <code>true</code> if this logger is enabled for level info, <code>false</code> otherwise. */ public boolean isInfoEnabled() { if(hierarchy.enableInt > Level.INFO_INT) return false; return Level.INFO.isGreaterOrEqual(this.getChainedLevel()); } /** Log a localized message. The user supplied parameter <code>key</code> is replaced by its localized version from the resource bundle. @see #setResourceBundle @since 0.8.4 */ public void l7dlog(Level level, String key, Throwable t) { if(hierarchy.enableInt > level.level) { return; } if(level.isGreaterOrEqual(this.getChainedLevel())) { String msg = getResourceBundleString(key); // if message corresponding to 'key' could not be found in the // resource bundle, then default to 'key'. if(msg == null) { msg = key; } forcedLog(FQCN, level, msg, t); } } /** Log a localized and parameterized message. First, the user supplied <code>key</code> is searched in the resource bundle. Next, the resulting pattern is formatted using {@link MessageFormat#format(String,Object[])} method with the user supplied object array <code>params</code>. @since 0.8.4 */ public void l7dlog(Level level, String key, Object[] params, Throwable t) { if(hierarchy.enableInt > level.level) { return; } if(level.isGreaterOrEqual(this.getChainedLevel())) { String pattern = getResourceBundleString(key); String msg; if(pattern == null) msg = key; else msg = java.text.MessageFormat.format(pattern, params); forcedLog(FQCN, level, msg, t); } } /** This generic form is intended to be used by wrappers. */ public void log(Level level, Object message, Throwable t) { if(hierarchy.enableInt > level.level) { return; } if(level.isGreaterOrEqual(this.getChainedLevel())) forcedLog(FQCN, level, message, t); } /** This generic form is intended to be used by wrappers. */ public void log(Level level, Object message) { if(hierarchy.enableInt > level.level) { return; } if(level.isGreaterOrEqual(this.getChainedLevel())) forcedLog(FQCN, level, message, null); } /** This is the most generic printing method. It is intended to be invoked by <b>wrapper</b> classes. @param callerFQCN The wrapper class' fully qualified class name. @param level The level of the logging request. @param message The message of the logging request. @param t The throwable of the logging request, may be null. */ public void log(String callerFQCN, Level level, Object message, Throwable t) { if(hierarchy.enableInt > level.level) { return; } if(level.isGreaterOrEqual(this.getChainedLevel())) { forcedLog(callerFQCN, level, message, t); } } /** Remove all previously added appenders from this Logger instance. <p>This is useful when re-reading configuration information. */ synchronized public void removeAllAppenders() { if(aai != null) { aai.removeAllAppenders(); aai = null; } } /** Remove the appender passed as parameter form the list of appenders. @since 0.8.2 */ synchronized public void removeAppender(Appender appender) { if(appender == null || aai == null) return; aai.removeAppender(appender); } /** Remove the appender with the name passed as parameter form the list of appenders. @since 0.8.2 */ synchronized public void removeAppender(String name) { if(name == null || aai == null) return; aai.removeAppender(name); } /** Set the additivity flag for this Logger instance. @since 0.8.1 */ public void setAdditivity(boolean additive) { this.additive = additive; } /** Only the Hiearchy class can set the hiearchy of a logger. Default package access is MANDATORY here. */ final void setHierarchy(Hierarchy hierarchy) { this.hierarchy = hierarchy; } /** Set the level of this Logger. <p>Null values are admitted. */ public void setLevel(Level level) { this.level = level; } /** Set the level of this Logger. <p>Null values are admitted. @deprecated Please use {@link #setLevel} instead. */ public void setPriority(Priority priority) { setLevel(priority); } /** Set the resource bundle to be used with localized logging methods {@link #l7dlog(Level,String,Throwable)} and {@link #l7dlog(Level,String,Object[],Throwable)}. @since 0.8.4 */ public void setResourceBundle(ResourceBundle bundle) { resourceBundle = bundle; } /** Calling this method will <em>safely</em> close and remove all appenders in all the categories including root contained in the default hierachy. <p>Some appenders such as {@link org.apache.log4j.net.SocketAppender} and {@link AsyncAppender} need to be closed before the application exists. Otherwise, pending logging events might be lost. <p>The <code>shutdown</code> method is careful to close nested appenders before closing regular appenders. This is allows configurations where a regular appender is attached to a logger and again to a nested appender. @since 1.0 */ public static void shutdown() { defaultHierarchy.shutdown(); } /** Log a message object with the {@link Level#WARN WARN} Level. <p>This method first checks if this logger is <code>WARN</code> enabled by comparing the level of this logger with {@link Level#WARN WARN} Level. If the logger is <code>WARN</code> enabled, then it converts the message object passed as parameter to a string by invoking the appropriate {@link ObjectRenderer}. It proceeds to call all the registered appenders in this logger and also higher in the hieararchy depending on the value of the additivity flag. <p><b>WARNING</b> Note that passing a {@link Throwable} to this method will print the name of the Throwable but no stack trace. To print a stack trace use the {@link #warn(Object, Throwable)} form instead. <p> @param message the message object to log. */ public void warn(Object message) { if(hierarchy.enableInt > Level.WARN_INT) return; if(Level.WARN.isGreaterOrEqual(this.getChainedLevel())) forcedLog(FQCN, Level.WARN, message, null); } /** Log a message with the <code>WARN</code> level including the stack trace of the {@link Throwable} <code>t</code> passed as parameter. <p>See {@link #warn(Object)} for more detailed information. @param message the message object to log. @param t the exception to log, including its stack trace. */ public void warn(Object message, Throwable t) { if(hierarchy.enableInt > Level.WARN_INT) return; if(Level.WARN.isGreaterOrEqual(this.getChainedLevel())) forcedLog(FQCN, Level.WARN, message, t); } } --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]