001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 * 
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 * 
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.log4j;
019
020import org.apache.log4j.spi.LoggerRepository;
021import org.apache.log4j.spi.LoggerFactory;
022import org.apache.log4j.spi.RepositorySelector;
023import org.apache.log4j.spi.DefaultRepositorySelector;
024import org.apache.log4j.spi.RootLogger;
025import org.apache.log4j.spi.NOPLoggerRepository;
026import org.apache.log4j.helpers.Loader;
027import org.apache.log4j.helpers.OptionConverter;
028import org.apache.log4j.helpers.LogLog;
029
030import java.net.URL;
031import java.net.MalformedURLException;
032
033
034import java.util.Enumeration;
035import java.io.StringWriter;
036import java.io.PrintWriter;
037
038/**
039 * Use the <code>LogManager</code> class to retreive {@link Logger}
040 * instances or to operate on the current {@link
041 * LoggerRepository}. When the <code>LogManager</code> class is loaded
042 * into memory the default initalzation procedure is inititated. The
043 * default intialization procedure</a> is described in the <a
044 * href="../../../../manual.html#defaultInit">short log4j manual</a>.
045 *
046 * @author Ceki G&uuml;lc&uuml; */
047public class LogManager {
048
049  /**
050   * @deprecated This variable is for internal use only. It will
051   * become package protected in future versions.
052   * */
053  static public final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
054  
055  static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";  
056   
057  /**
058   * @deprecated This variable is for internal use only. It will
059   * become private in future versions.
060   * */
061  static final public String DEFAULT_CONFIGURATION_KEY="log4j.configuration";
062
063  /**
064   * @deprecated This variable is for internal use only. It will
065   * become private in future versions.
066   * */
067  static final public String CONFIGURATOR_CLASS_KEY="log4j.configuratorClass";
068
069  /**
070  * @deprecated This variable is for internal use only. It will
071  * become private in future versions.
072  */
073  public static final String DEFAULT_INIT_OVERRIDE_KEY = 
074                                                 "log4j.defaultInitOverride";
075
076
077  static private Object guard = null;
078  static private RepositorySelector repositorySelector;
079
080  static {
081    // By default we use a DefaultRepositorySelector which always returns 'h'.
082    Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
083    repositorySelector = new DefaultRepositorySelector(h);
084
085    /** Search for the properties file log4j.properties in the CLASSPATH.  */
086    String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
087                                                       null);
088
089    // if there is no default init override, then get the resource
090    // specified by the user or the default config file.
091    if(override == null || "false".equalsIgnoreCase(override)) {
092
093      String configurationOptionStr = OptionConverter.getSystemProperty(
094                                                          DEFAULT_CONFIGURATION_KEY, 
095                                                          null);
096
097      String configuratorClassName = OptionConverter.getSystemProperty(
098                                                   CONFIGURATOR_CLASS_KEY, 
099                                                   null);
100
101      URL url = null;
102
103      // if the user has not specified the log4j.configuration
104      // property, we search first for the file "log4j.xml" and then
105      // "log4j.properties"
106      if(configurationOptionStr == null) {      
107        url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
108        if(url == null) {
109          url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
110        }
111      } else {
112        try {
113          url = new URL(configurationOptionStr);
114        } catch (MalformedURLException ex) {
115          // so, resource is not a URL:
116          // attempt to get the resource from the class path
117          url = Loader.getResource(configurationOptionStr); 
118        }       
119      }
120      
121      // If we have a non-null url, then delegate the rest of the
122      // configuration to the OptionConverter.selectAndConfigure
123      // method.
124      if(url != null) {
125            LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
126        try {
127            OptionConverter.selectAndConfigure(url, configuratorClassName,
128                                           LogManager.getLoggerRepository());
129        } catch (NoClassDefFoundError e) {
130            LogLog.warn("Error during default initialization", e);
131        }
132      } else {
133            LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");
134      }
135    } else {
136        LogLog.debug("Default initialization of overridden by " + 
137            DEFAULT_INIT_OVERRIDE_KEY + "property."); 
138    }  
139  } 
140
141  /**
142     Sets <code>LoggerFactory</code> but only if the correct
143     <em>guard</em> is passed as parameter.
144     
145     <p>Initally the guard is null.  If the guard is
146     <code>null</code>, then invoking this method sets the logger
147     factory and the guard. Following invocations will throw a {@link
148     IllegalArgumentException}, unless the previously set
149     <code>guard</code> is passed as the second parameter.
150
151     <p>This allows a high-level component to set the {@link
152     RepositorySelector} used by the <code>LogManager</code>.
153     
154     <p>For example, when tomcat starts it will be able to install its
155     own repository selector. However, if and when Tomcat is embedded
156     within JBoss, then JBoss will install its own repository selector
157     and Tomcat will use the repository selector set by its container,
158     JBoss.  */
159  static
160  public
161  void setRepositorySelector(RepositorySelector selector, Object guard) 
162                                                 throws IllegalArgumentException {
163    if((LogManager.guard != null) && (LogManager.guard != guard)) {
164      throw new IllegalArgumentException(
165           "Attempted to reset the LoggerFactory without possessing the guard.");
166    }
167
168    if(selector == null) {
169      throw new IllegalArgumentException("RepositorySelector must be non-null.");
170    }
171
172    LogManager.guard = guard;
173    LogManager.repositorySelector = selector;
174  }
175
176
177    /**
178     * This method tests if called from a method that
179     * is known to result in class members being abnormally
180     * set to null but is assumed to be harmless since the
181     * all classes are in the process of being unloaded.
182     *
183     * @param ex exception used to determine calling stack.
184     * @return true if calling stack is recognized as likely safe.
185     */
186  private static boolean isLikelySafeScenario(final Exception ex) {
187      StringWriter stringWriter = new StringWriter();
188      ex.printStackTrace(new PrintWriter(stringWriter));
189      String msg = stringWriter.toString();
190      return msg.indexOf("org.apache.catalina.loader.WebappClassLoader.stop") != -1;
191  }
192
193  static
194  public
195  LoggerRepository getLoggerRepository() {
196    if (repositorySelector == null) {
197        repositorySelector = new DefaultRepositorySelector(new NOPLoggerRepository());
198        guard = null;
199        Exception ex = new IllegalStateException("Class invariant violation");
200        String msg =
201                "log4j called after unloading, see http://logging.apache.org/log4j/1.2/faq.html#unload.";
202        if (isLikelySafeScenario(ex)) {
203            LogLog.debug(msg, ex);
204        } else {
205            LogLog.error(msg, ex);
206        }
207    }
208    return repositorySelector.getLoggerRepository();
209  }
210
211  /**
212     Retrieve the appropriate root logger.
213   */
214  public
215  static 
216  Logger getRootLogger() {
217     // Delegate the actual manufacturing of the logger to the logger repository.
218    return getLoggerRepository().getRootLogger();
219  }
220
221  /**
222     Retrieve the appropriate {@link Logger} instance.  
223  */
224  public
225  static 
226  Logger getLogger(final String name) {
227     // Delegate the actual manufacturing of the logger to the logger repository.
228    return getLoggerRepository().getLogger(name);
229  }
230
231 /**
232     Retrieve the appropriate {@link Logger} instance.  
233  */
234  public
235  static 
236  Logger getLogger(final Class clazz) {
237     // Delegate the actual manufacturing of the logger to the logger repository.
238    return getLoggerRepository().getLogger(clazz.getName());
239  }
240
241
242  /**
243     Retrieve the appropriate {@link Logger} instance.  
244  */
245  public
246  static 
247  Logger getLogger(final String name, final LoggerFactory factory) {
248     // Delegate the actual manufacturing of the logger to the logger repository.
249    return getLoggerRepository().getLogger(name, factory);
250  }  
251
252  public
253  static
254  Logger exists(final String name) {
255    return getLoggerRepository().exists(name);
256  }
257
258  public
259  static
260  Enumeration getCurrentLoggers() {
261    return getLoggerRepository().getCurrentLoggers();
262  }
263
264  public
265  static
266  void shutdown() {
267    getLoggerRepository().shutdown();
268  }
269
270  public
271  static
272  void resetConfiguration() {
273    getLoggerRepository().resetConfiguration();
274  }
275}
276