KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > log4j > Hierarchy


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 JavaDoc;
31 import java.util.Enumeration JavaDoc;
32 import java.util.Vector JavaDoc;
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 JavaDoc listeners;
68
69   Hashtable JavaDoc 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 JavaDoc();
88     listeners = new Vector JavaDoc(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 JavaDoc 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 JavaDoc name) {
149     Object JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc v = new Vector JavaDoc(ht.size());
305
306     Enumeration JavaDoc elems = ht.elements();
307     while(elems.hasMoreElements()) {
308       Object JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc substr = name.substring(0, i);
494
495       //System.out.println("Updating parent : " + substr);
496
CategoryKey key = new CategoryKey(substr); // simple constructor
497
Object JavaDoc 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 JavaDoc e = new IllegalStateException JavaDoc("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
558
Popular Tags