/ org.apache.log4j / source-bundle / org / apache / log4j / Hierarchy.java
Hierarchy.java
  1  /*
  2   * Copyright 1999-2005 The Apache Software Foundation.
  3   * 
  4   * Licensed under the Apache License, Version 2.0 (the "License");
  5   * you may not use this file except in compliance with the License.
  6   * You may obtain a copy of the License at
  7   * 
  8   *      http://www.apache.org/licenses/LICENSE-2.0
  9   * 
 10   * Unless required by applicable law or agreed to in writing, software
 11   * distributed under the License is distributed on an "AS IS" BASIS,
 12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13   * See the License for the specific language governing permissions and
 14   * limitations under the License.
 15   */
 16  
 17  // WARNING This class MUST not have references to the Category or
 18  // WARNING RootCategory classes in its static initiliazation neither
 19  // WARNING directly nor indirectly.
 20  
 21  // Contributors:
 22  //                Luke Blanshard <luke@quiq.com>
 23  //                Mario Schomburg - IBM Global Services/Germany
 24  //                Anders Kristensen
 25  //                Igor Poteryaev
 26  
 27  package org.apache.log4j;
 28  
 29  
 30  import java.util.Hashtable;
 31  import java.util.Enumeration;
 32  import java.util.Vector;
 33  
 34  import org.apache.log4j.spi.LoggerFactory;
 35  import org.apache.log4j.spi.HierarchyEventListener;
 36  import org.apache.log4j.spi.LoggerRepository;
 37  import org.apache.log4j.spi.RendererSupport;
 38  import org.apache.log4j.Appender;
 39  import org.apache.log4j.or.RendererMap;
 40  import org.apache.log4j.or.ObjectRenderer;
 41  import org.apache.log4j.helpers.LogLog;
 42  
 43  /**
 44     This class is specialized in retrieving loggers by name and also
 45     maintaining the logger hierarchy.
 46  
 47     <p><em>The casual user does not have to deal with this class
 48     directly.</em>
 49  
 50     <p>The structure of the logger hierarchy is maintained by the
 51     {@link #getLogger} method. The hierarchy is such that children link
 52     to their parent but parents do not have any pointers to their
 53     children. Moreover, loggers can be instantiated in any order, in
 54     particular descendant before ancestor.
 55  
 56     <p>In case a descendant is created before a particular ancestor,
 57     then it creates a provision node for the ancestor and adds itself
 58     to the provision node. Other descendants of the same ancestor add
 59     themselves to the previously created provision node.
 60  
 61     @author Ceki G&uuml;lc&uuml;
 62  
 63  */
 64  public class Hierarchy implements LoggerRepository, RendererSupport {
 65  
 66    private LoggerFactory defaultFactory;
 67    private Vector listeners;
 68  
 69    Hashtable ht;
 70    Logger root;
 71    RendererMap rendererMap;
 72  
 73    int thresholdInt;
 74    Level threshold;
 75  
 76    boolean emittedNoAppenderWarning = false;
 77    boolean emittedNoResourceBundleWarning = false;
 78  
 79    /**
 80       Create a new logger hierarchy.
 81  
 82       @param root The root of the new hierarchy.
 83  
 84     */
 85    public
 86    Hierarchy(Logger root) {
 87      ht = new Hashtable();
 88      listeners = new Vector(1);
 89      this.root = root;
 90      // Enable all level levels by default.
 91      setThreshold(Level.ALL);
 92      this.root.setHierarchy(this);
 93      rendererMap = new RendererMap();
 94      defaultFactory = new DefaultCategoryFactory();
 95    }
 96  
 97    /**
 98       Add an object renderer for a specific class.
 99     */
100    public
101    void addRenderer(Class classToRender, ObjectRenderer or) {
102      rendererMap.put(classToRender, or);
103    }
104  
105    public
106    void addHierarchyEventListener(HierarchyEventListener listener) {
107      if(listeners.contains(listener)) {
108        LogLog.warn("Ignoring attempt to add an existent listener.");
109      } else {
110        listeners.addElement(listener);
111      }
112    }
113  
114    /**
115       This call will clear all logger definitions from the internal
116       hashtable. Invoking this method will irrevocably mess up the
117       logger hierarchy.
118  
119       <p>You should <em>really</em> know what you are doing before
120       invoking this method.
121  
122       @since 0.9.0 */
123    public
124    void clear() {
125      //System.out.println("\n\nAbout to clear internal hash table.");
126      ht.clear();
127    }
128  
129    public
130    void emitNoAppenderWarning(Category cat) {
131      // No appenders in hierarchy, warn user only once.
132      if(!this.emittedNoAppenderWarning) {
133        LogLog.warn("No appenders could be found for logger (" +
134  		   cat.getName() + ").");
135        LogLog.warn("Please initialize the log4j system properly.");
136        this.emittedNoAppenderWarning = true;
137      }
138    }
139  
140    /**
141       Check if the named logger exists in the hierarchy. If so return
142       its reference, otherwise returns <code>null</code>.
143  
144       @param name The name of the logger to search for.
145  
146    */
147    public
148    Logger exists(String name) {
149      Object o = ht.get(new CategoryKey(name));
150      if(o instanceof Logger) {
151        return (Logger) o;
152      } else {
153        return null;
154      }
155    }
156  
157    /**
158       The string form of {@link #setThreshold(Level)}.
159    */
160    public
161    void setThreshold(String levelStr) {
162      Level l = (Level) Level.toLevel(levelStr, null);
163      if(l != null) {
164        setThreshold(l);
165      } else {
166        LogLog.warn("Could not convert ["+levelStr+"] to Level.");
167      }
168    }
169  
170  
171    /**
172       Enable logging for logging requests with level <code>l</code> or
173       higher. By default all levels are enabled.
174  
175       @param l The minimum level for which logging requests are sent to
176       their appenders.  */
177    public
178    void setThreshold(Level l) {
179      if(l != null) {
180        thresholdInt = l.level;
181        threshold = l;
182      }
183    }
184  
185    public
186    void fireAddAppenderEvent(Category logger, Appender appender) {
187      if(listeners != null) {
188        int size = listeners.size();
189        HierarchyEventListener listener;
190        for(int i = 0; i < size; i++) {
191  	listener = (HierarchyEventListener) listeners.elementAt(i);
192  	listener.addAppenderEvent(logger, appender);
193        }
194      }
195    }
196  
197    void fireRemoveAppenderEvent(Category logger, Appender appender) {
198      if(listeners != null) {
199        int size = listeners.size();
200        HierarchyEventListener listener;
201        for(int i = 0; i < size; i++) {
202  	listener = (HierarchyEventListener) listeners.elementAt(i);
203  	listener.removeAppenderEvent(logger, appender);
204        }
205      }
206    }
207  
208    /**
209       Returns a {@link Level} representation of the <code>enable</code>
210       state.
211  
212       @since 1.2 */
213    public
214    Level getThreshold() {
215      return threshold;
216    }
217  
218    /**
219       Returns an integer representation of the this repository's
220       threshold.
221  
222       @since 1.2 */
223    //public
224    //int getThresholdInt() {
225    //  return thresholdInt;
226    //}
227  
228  
229    /**
230       Return a new logger instance named as the first parameter using
231       the default factory.
232  
233       <p>If a logger of that name already exists, then it will be
234       returned.  Otherwise, a new logger will be instantiated and
235       then linked with its existing ancestors as well as children.
236  
237       @param name The name of the logger to retrieve.
238  
239   */
240    public
241    Logger getLogger(String name) {
242      return getLogger(name, defaultFactory);
243    }
244  
245   /**
246       Return a new logger instance named as the first parameter using
247       <code>factory</code>.
248  
249       <p>If a logger of that name already exists, then it will be
250       returned.  Otherwise, a new logger will be instantiated by the
251       <code>factory</code> parameter and linked with its existing
252       ancestors as well as children.
253  
254       @param name The name of the logger to retrieve.
255       @param factory The factory that will make the new logger instance.
256  
257   */
258    public
259    Logger getLogger(String name, LoggerFactory factory) {
260      //System.out.println("getInstance("+name+") called.");
261      CategoryKey key = new CategoryKey(name);
262      // Synchronize to prevent write conflicts. Read conflicts (in
263      // getChainedLevel method) are possible only if variable
264      // assignments are non-atomic.
265      Logger logger;
266  
267      synchronized(ht) {
268        Object o = ht.get(key);
269        if(o == null) {
270  	logger = factory.makeNewLoggerInstance(name);
271  	logger.setHierarchy(this);
272  	ht.put(key, logger);
273  	updateParents(logger);
274  	return logger;
275        } else if(o instanceof Logger) {
276  	return (Logger) o;
277        } else if (o instanceof ProvisionNode) {
278  	//System.out.println("("+name+") ht.get(this) returned ProvisionNode");
279  	logger = factory.makeNewLoggerInstance(name);
280  	logger.setHierarchy(this);
281  	ht.put(key, logger);
282  	updateChildren((ProvisionNode) o, logger);
283  	updateParents(logger);
284  	return logger;
285        }
286        else {
287  	// It should be impossible to arrive here
288  	return null;  // but let's keep the compiler happy.
289        }
290      }
291    }
292  
293    /**
294       Returns all the currently defined categories in this hierarchy as
295       an {@link java.util.Enumeration Enumeration}.
296  
297       <p>The root logger is <em>not</em> included in the returned
298       {@link Enumeration}.  */
299    public
300    Enumeration getCurrentLoggers() {
301      // The accumlation in v is necessary because not all elements in
302      // ht are Logger objects as there might be some ProvisionNodes
303      // as well.
304      Vector v = new Vector(ht.size());
305  
306      Enumeration elems = ht.elements();
307      while(elems.hasMoreElements()) {
308        Object o = elems.nextElement();
309        if(o instanceof Logger) {
310  	v.addElement(o);
311        }
312      }
313      return v.elements();
314    }
315  
316    /**
317       @deprecated Please use {@link #getCurrentLoggers} instead.
318     */
319    public
320    Enumeration getCurrentCategories() {
321      return getCurrentLoggers();
322    }
323  
324  
325    /**
326       Get the renderer map for this hierarchy.
327    */
328    public
329    RendererMap getRendererMap() {
330      return rendererMap;
331    }
332  
333  
334    /**
335       Get the root of this hierarchy.
336  
337       @since 0.9.0
338     */
339    public
340    Logger getRootLogger() {
341      return root;
342    }
343  
344    /**
345       This method will return <code>true</code> if this repository is
346       disabled for <code>level</code> object passed as parameter and
347       <code>false</code> otherwise. See also the {@link
348       #setThreshold(Level) threshold} emthod.  */
349    public
350    boolean isDisabled(int level) {
351      return thresholdInt > level;
352    }
353  
354    /**
355       @deprecated Deprecated with no replacement.
356    */
357    public
358    void overrideAsNeeded(String override) {
359      LogLog.warn("The Hiearchy.overrideAsNeeded method has been deprecated.");
360    }
361  
362    /**
363       Reset all values contained in this hierarchy instance to their
364       default.  This removes all appenders from all categories, sets
365       the level of all non-root categories to <code>null</code>,
366       sets their additivity flag to <code>true</code> and sets the level
367       of the root logger to {@link Level#DEBUG DEBUG}.  Moreover,
368       message disabling is set its default "off" value.
369  
370       <p>Existing categories are not removed. They are just reset.
371  
372       <p>This method should be used sparingly and with care as it will
373       block all logging until it is completed.</p>
374  
375       @since 0.8.5 */
376    public
377    void resetConfiguration() {
378  
379      getRootLogger().setLevel((Level) Level.DEBUG);
380      root.setResourceBundle(null);
381      setThreshold(Level.ALL);
382  
383      // the synchronization is needed to prevent JDK 1.2.x hashtable
384      // surprises
385      synchronized(ht) {
386        shutdown(); // nested locks are OK
387  
388        Enumeration cats = getCurrentLoggers();
389        while(cats.hasMoreElements()) {
390  	Logger c = (Logger) cats.nextElement();
391  	c.setLevel(null);
392  	c.setAdditivity(true);
393  	c.setResourceBundle(null);
394        }
395      }
396      rendererMap.clear();
397    }
398  
399    /**
400       Does mothing.
401  
402       @deprecated Deprecated with no replacement.
403     */
404    public
405    void setDisableOverride(String override) {
406      LogLog.warn("The Hiearchy.setDisableOverride method has been deprecated.");
407    }
408  
409  
410  
411    /**
412       Used by subclasses to add a renderer to the hierarchy passed as parameter.
413     */
414    public
415    void setRenderer(Class renderedClass, ObjectRenderer renderer) {
416      rendererMap.put(renderedClass, renderer);
417    }
418  
419  
420    /**
421       Shutting down a hierarchy will <em>safely</em> close and remove
422       all appenders in all categories including the root logger.
423  
424       <p>Some appenders such as {@link org.apache.log4j.net.SocketAppender}
425       and {@link AsyncAppender} need to be closed before the
426       application exists. Otherwise, pending logging events might be
427       lost.
428  
429       <p>The <code>shutdown</code> method is careful to close nested
430       appenders before closing regular appenders. This is allows
431       configurations where a regular appender is attached to a logger
432       and again to a nested appender.
433  
434  
435       @since 1.0 */
436    public
437    void shutdown() {
438      Logger root = getRootLogger();
439  
440      // begin by closing nested appenders
441      root.closeNestedAppenders();
442  
443      synchronized(ht) {
444        Enumeration cats = this.getCurrentLoggers();
445        while(cats.hasMoreElements()) {
446  	Logger c = (Logger) cats.nextElement();
447  	c.closeNestedAppenders();
448        }
449  
450        // then, remove all appenders
451        root.removeAllAppenders();
452        cats = this.getCurrentLoggers();
453        while(cats.hasMoreElements()) {
454  	Logger c = (Logger) cats.nextElement();
455  	c.removeAllAppenders();
456        }
457      }
458    }
459  
460  
461    /**
462       This method loops through all the *potential* parents of
463       'cat'. There 3 possible cases:
464  
465       1) No entry for the potential parent of 'cat' exists
466  
467          We create a ProvisionNode for this potential parent and insert
468          'cat' in that provision node.
469  
470       2) There entry is of type Logger for the potential parent.
471  
472          The entry is 'cat's nearest existing parent. We update cat's
473          parent field with this entry. We also break from the loop
474          because updating our parent's parent is our parent's
475          responsibility.
476  
477       3) There entry is of type ProvisionNode for this potential parent.
478  
479          We add 'cat' to the list of children for this potential parent.
480     */
481    final
482    private
483    void updateParents(Logger cat) {
484      String name = cat.name;
485      int length = name.length();
486      boolean parentFound = false;
487  
488      //System.out.println("UpdateParents called for " + name);
489  
490      // if name = "w.x.y.z", loop thourgh "w.x.y", "w.x" and "w", but not "w.x.y.z"
491      for(int i = name.lastIndexOf('.', length-1); i >= 0;
492  	                                 i = name.lastIndexOf('.', i-1))  {
493        String substr = name.substring(0, i);
494  
495        //System.out.println("Updating parent : " + substr);
496        CategoryKey key = new CategoryKey(substr); // simple constructor
497        Object o = ht.get(key);
498        // Create a provision node for a future parent.
499        if(o == null) {
500  	//System.out.println("No parent "+substr+" found. Creating ProvisionNode.");
501  	ProvisionNode pn = new ProvisionNode(cat);
502  	ht.put(key, pn);
503        } else if(o instanceof Category) {
504  	parentFound = true;
505  	cat.parent = (Category) o;
506  	//System.out.println("Linking " + cat.name + " -> " + ((Category) o).name);
507  	break; // no need to update the ancestors of the closest ancestor
508        } else if(o instanceof ProvisionNode) {
509  	((ProvisionNode) o).addElement(cat);
510        } else {
511  	Exception e = new IllegalStateException("unexpected object type " +
512  					o.getClass() + " in ht.");
513  	e.printStackTrace();
514        }
515      }
516      // If we could not find any existing parents, then link with root.
517      if(!parentFound)
518        cat.parent = root;
519    }
520  
521    /**
522        We update the links for all the children that placed themselves
523        in the provision node 'pn'. The second argument 'cat' is a
524        reference for the newly created Logger, parent of all the
525        children in 'pn'
526  
527        We loop on all the children 'c' in 'pn':
528  
529           If the child 'c' has been already linked to a child of
530           'cat' then there is no need to update 'c'.
531  
532  	 Otherwise, we set cat's parent field to c's parent and set
533  	 c's parent field to cat.
534  
535    */
536    final
537    private
538    void updateChildren(ProvisionNode pn, Logger logger) {
539      //System.out.println("updateChildren called for " + logger.name);
540      final int last = pn.size();
541  
542      for(int i = 0; i < last; i++) {
543        Logger l = (Logger) pn.elementAt(i);
544        //System.out.println("Updating child " +p.name);
545  
546        // Unless this child already points to a correct (lower) parent,
547        // make cat.parent point to l.parent and l.parent to cat.
548        if(!l.parent.name.startsWith(logger.name)) {
549  	logger.parent = l.parent;
550  	l.parent = logger;
551        }
552      }
553    }
554  
555  }
556  
557