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

Save This Page
Home » apache-log4j-1.2.15 » org.apache » log4j » [javadoc | source]