You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
577 lines
16 KiB
577 lines
16 KiB
/* |
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
|
* contributor license agreements. See the NOTICE file distributed with |
|
* this work for additional information regarding copyright ownership. |
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|
* (the "License"); you may not use this file except in compliance with |
|
* the License. You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
// WARNING This class MUST not have references to the Category or |
|
// WARNING RootCategory classes in its static initiliazation neither |
|
// WARNING directly nor indirectly. |
|
|
|
// Contributors: |
|
// Luke Blanshard <luke@quiq.com> |
|
// Mario Schomburg - IBM Global Services/Germany |
|
// Anders Kristensen |
|
// Igor Poteryaev |
|
|
|
package com.fr.third.apache.log4j; |
|
|
|
|
|
import com.fr.third.apache.log4j.helpers.LogLog; |
|
import com.fr.third.apache.log4j.net.SocketAppender; |
|
import com.fr.third.apache.log4j.or.ObjectRenderer; |
|
import com.fr.third.apache.log4j.or.RendererMap; |
|
import com.fr.third.apache.log4j.spi.HierarchyEventListener; |
|
import com.fr.third.apache.log4j.spi.LoggerFactory; |
|
import com.fr.third.apache.log4j.spi.LoggerRepository; |
|
import com.fr.third.apache.log4j.spi.RendererSupport; |
|
import com.fr.third.apache.log4j.spi.ThrowableRenderer; |
|
import com.fr.third.apache.log4j.spi.ThrowableRendererSupport; |
|
import java.util.Hashtable; |
|
import java.util.Enumeration; |
|
import java.util.Vector; |
|
|
|
/** |
|
This class is specialized in retrieving loggers by name and also |
|
maintaining the logger hierarchy. |
|
|
|
<p><em>The casual user does not have to deal with this class |
|
directly.</em> |
|
|
|
<p>The structure of the logger hierarchy is maintained by the |
|
{@link #getLogger} method. The hierarchy is such that children link |
|
to their parent but parents do not have any pointers to their |
|
children. Moreover, loggers can be instantiated in any order, in |
|
particular descendant before ancestor. |
|
|
|
<p>In case a descendant is created before a particular ancestor, |
|
then it creates a provision node for the ancestor and adds itself |
|
to the provision node. Other descendants of the same ancestor add |
|
themselves to the previously created provision node. |
|
|
|
@author Ceki Gülcü |
|
|
|
*/ |
|
public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport { |
|
|
|
private LoggerFactory defaultFactory; |
|
private Vector listeners; |
|
|
|
Hashtable ht; |
|
Logger root; |
|
RendererMap rendererMap; |
|
|
|
int thresholdInt; |
|
Level threshold; |
|
|
|
boolean emittedNoAppenderWarning = false; |
|
boolean emittedNoResourceBundleWarning = false; |
|
|
|
private ThrowableRenderer throwableRenderer = null; |
|
|
|
/** |
|
Create a new logger hierarchy. |
|
|
|
@param root The root of the new hierarchy. |
|
|
|
*/ |
|
public |
|
Hierarchy(Logger root) { |
|
ht = new Hashtable(); |
|
listeners = new Vector(1); |
|
this.root = root; |
|
// Enable all level levels by default. |
|
setThreshold(Level.ALL); |
|
this.root.setHierarchy(this); |
|
rendererMap = new RendererMap(); |
|
defaultFactory = new DefaultCategoryFactory(); |
|
} |
|
|
|
/** |
|
Add an object renderer for a specific class. |
|
*/ |
|
public |
|
void addRenderer(Class classToRender, ObjectRenderer or) { |
|
rendererMap.put(classToRender, or); |
|
} |
|
|
|
public |
|
void addHierarchyEventListener(HierarchyEventListener listener) { |
|
if(listeners.contains(listener)) { |
|
LogLog.warn("Ignoring attempt to add an existent listener."); |
|
} else { |
|
listeners.addElement(listener); |
|
} |
|
} |
|
|
|
/** |
|
This call will clear all logger definitions from the internal |
|
hashtable. Invoking this method will irrevocably mess up the |
|
logger hierarchy. |
|
|
|
<p>You should <em>really</em> know what you are doing before |
|
invoking this method. |
|
|
|
@since 0.9.0 */ |
|
public |
|
void clear() { |
|
//System.out.println("\n\nAbout to clear internal hash table."); |
|
ht.clear(); |
|
} |
|
|
|
public |
|
void emitNoAppenderWarning(Category cat) { |
|
// No appenders in hierarchy, warn user only once. |
|
if(!this.emittedNoAppenderWarning) { |
|
LogLog.warn("No appenders could be found for logger (" + |
|
cat.getName() + ")."); |
|
LogLog.warn("Please initialize the log4j system properly."); |
|
LogLog.warn("See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info."); |
|
this.emittedNoAppenderWarning = true; |
|
} |
|
} |
|
|
|
/** |
|
Check if the named logger exists in the hierarchy. If so return |
|
its reference, otherwise returns <code>null</code>. |
|
|
|
@param name The name of the logger to search for. |
|
|
|
*/ |
|
public |
|
Logger exists(String name) { |
|
Object o = ht.get(new CategoryKey(name)); |
|
if(o instanceof Logger) { |
|
return (Logger) o; |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
/** |
|
The string form of {@link #setThreshold(Level)}. |
|
*/ |
|
public |
|
void setThreshold(String levelStr) { |
|
Level l = (Level) Level.toLevel(levelStr, null); |
|
if(l != null) { |
|
setThreshold(l); |
|
} else { |
|
LogLog.warn("Could not convert ["+levelStr+"] to Level."); |
|
} |
|
} |
|
|
|
|
|
/** |
|
Enable logging for logging requests with level <code>l</code> or |
|
higher. By default all levels are enabled. |
|
|
|
@param l The minimum level for which logging requests are sent to |
|
their appenders. */ |
|
public |
|
void setThreshold(Level l) { |
|
if(l != null) { |
|
thresholdInt = l.level; |
|
threshold = l; |
|
} |
|
} |
|
|
|
public |
|
void fireAddAppenderEvent(Category logger, Appender appender) { |
|
if(listeners != null) { |
|
int size = listeners.size(); |
|
HierarchyEventListener listener; |
|
for(int i = 0; i < size; i++) { |
|
listener = (HierarchyEventListener) listeners.elementAt(i); |
|
listener.addAppenderEvent(logger, appender); |
|
} |
|
} |
|
} |
|
|
|
void fireRemoveAppenderEvent(Category logger, Appender appender) { |
|
if(listeners != null) { |
|
int size = listeners.size(); |
|
HierarchyEventListener listener; |
|
for(int i = 0; i < size; i++) { |
|
listener = (HierarchyEventListener) listeners.elementAt(i); |
|
listener.removeAppenderEvent(logger, appender); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
Returns a {@link Level} representation of the <code>enable</code> |
|
state. |
|
|
|
@since 1.2 */ |
|
public |
|
Level getThreshold() { |
|
return threshold; |
|
} |
|
|
|
/** |
|
Returns an integer representation of the this repository's |
|
threshold. |
|
|
|
@since 1.2 */ |
|
//public |
|
//int getThresholdInt() { |
|
// return thresholdInt; |
|
//} |
|
|
|
|
|
/** |
|
Return a new logger instance named as the first parameter using |
|
the default factory. |
|
|
|
<p>If a logger of that name already exists, then it will be |
|
returned. Otherwise, a new logger will be instantiated and |
|
then linked with its existing ancestors as well as children. |
|
|
|
@param name The name of the logger to retrieve. |
|
|
|
*/ |
|
public |
|
Logger getLogger(String name) { |
|
return getLogger(name, defaultFactory); |
|
} |
|
|
|
/** |
|
Return a new logger instance named as the first parameter using |
|
<code>factory</code>. |
|
|
|
<p>If a logger of that name already exists, then it will be |
|
returned. Otherwise, a new logger will be instantiated by the |
|
<code>factory</code> parameter and linked with its existing |
|
ancestors as well as children. |
|
|
|
@param name The name of the logger to retrieve. |
|
@param factory The factory that will make the new logger instance. |
|
|
|
*/ |
|
public |
|
Logger getLogger(String name, LoggerFactory factory) { |
|
//System.out.println("getInstance("+name+") called."); |
|
CategoryKey key = new CategoryKey(name); |
|
// Synchronize to prevent write conflicts. Read conflicts (in |
|
// getChainedLevel method) are possible only if variable |
|
// assignments are non-atomic. |
|
Logger logger; |
|
|
|
synchronized(ht) { |
|
Object o = ht.get(key); |
|
if(o == null) { |
|
logger = factory.makeNewLoggerInstance(name); |
|
logger.setHierarchy(this); |
|
ht.put(key, logger); |
|
updateParents(logger); |
|
return logger; |
|
} else if(o instanceof Logger) { |
|
return (Logger) o; |
|
} else if (o instanceof ProvisionNode) { |
|
//System.out.println("("+name+") ht.get(this) returned ProvisionNode"); |
|
logger = factory.makeNewLoggerInstance(name); |
|
logger.setHierarchy(this); |
|
ht.put(key, logger); |
|
updateChildren((ProvisionNode) o, logger); |
|
updateParents(logger); |
|
return logger; |
|
} |
|
else { |
|
// It should be impossible to arrive here |
|
return null; // but let's keep the compiler happy. |
|
} |
|
} |
|
} |
|
|
|
/** |
|
Returns all the currently defined categories in this hierarchy as |
|
an {@link java.util.Enumeration Enumeration}. |
|
|
|
<p>The root logger is <em>not</em> included in the returned |
|
{@link Enumeration}. */ |
|
public |
|
Enumeration getCurrentLoggers() { |
|
// The accumlation in v is necessary because not all elements in |
|
// ht are Logger objects as there might be some ProvisionNodes |
|
// as well. |
|
Vector v = new Vector(ht.size()); |
|
|
|
Enumeration elems = ht.elements(); |
|
while(elems.hasMoreElements()) { |
|
Object o = elems.nextElement(); |
|
if(o instanceof Logger) { |
|
v.addElement(o); |
|
} |
|
} |
|
return v.elements(); |
|
} |
|
|
|
/** |
|
@deprecated Please use {@link #getCurrentLoggers} instead. |
|
*/ |
|
public |
|
Enumeration getCurrentCategories() { |
|
return getCurrentLoggers(); |
|
} |
|
|
|
|
|
/** |
|
Get the renderer map for this hierarchy. |
|
*/ |
|
public |
|
RendererMap getRendererMap() { |
|
return rendererMap; |
|
} |
|
|
|
|
|
/** |
|
Get the root of this hierarchy. |
|
|
|
@since 0.9.0 |
|
*/ |
|
public |
|
Logger getRootLogger() { |
|
return root; |
|
} |
|
|
|
/** |
|
This method will return <code>true</code> if this repository is |
|
disabled for <code>level</code> object passed as parameter and |
|
<code>false</code> otherwise. See also the {@link |
|
#setThreshold(Level) threshold} emthod. */ |
|
public |
|
boolean isDisabled(int level) { |
|
return thresholdInt > level; |
|
} |
|
|
|
/** |
|
@deprecated Deprecated with no replacement. |
|
*/ |
|
public |
|
void overrideAsNeeded(String override) { |
|
LogLog.warn("The Hiearchy.overrideAsNeeded method has been deprecated."); |
|
} |
|
|
|
/** |
|
Reset all values contained in this hierarchy instance to their |
|
default. This removes all appenders from all categories, sets |
|
the level of all non-root categories to <code>null</code>, |
|
sets their additivity flag to <code>true</code> and sets the level |
|
of the root logger to {@link Level#DEBUG DEBUG}. Moreover, |
|
message disabling is set its default "off" value. |
|
|
|
<p>Existing categories are not removed. They are just reset. |
|
|
|
<p>This method should be used sparingly and with care as it will |
|
block all logging until it is completed.</p> |
|
|
|
@since 0.8.5 */ |
|
public |
|
void resetConfiguration() { |
|
|
|
getRootLogger().setLevel((Level) Level.DEBUG); |
|
root.setResourceBundle(null); |
|
setThreshold(Level.ALL); |
|
|
|
// the synchronization is needed to prevent JDK 1.2.x hashtable |
|
// surprises |
|
synchronized(ht) { |
|
shutdown(); // nested locks are OK |
|
|
|
Enumeration cats = getCurrentLoggers(); |
|
while(cats.hasMoreElements()) { |
|
Logger c = (Logger) cats.nextElement(); |
|
c.setLevel(null); |
|
c.setAdditivity(true); |
|
c.setResourceBundle(null); |
|
} |
|
} |
|
rendererMap.clear(); |
|
throwableRenderer = null; |
|
} |
|
|
|
/** |
|
Does nothing. |
|
|
|
@deprecated Deprecated with no replacement. |
|
*/ |
|
public |
|
void setDisableOverride(String override) { |
|
LogLog.warn("The Hiearchy.setDisableOverride method has been deprecated."); |
|
} |
|
|
|
|
|
|
|
/** |
|
Used by subclasses to add a renderer to the hierarchy passed as parameter. |
|
*/ |
|
public |
|
void setRenderer(Class renderedClass, ObjectRenderer renderer) { |
|
rendererMap.put(renderedClass, renderer); |
|
} |
|
|
|
/** |
|
* {@inheritDoc} |
|
*/ |
|
public void setThrowableRenderer(final ThrowableRenderer renderer) { |
|
throwableRenderer = renderer; |
|
} |
|
|
|
/** |
|
* {@inheritDoc} |
|
*/ |
|
public ThrowableRenderer getThrowableRenderer() { |
|
return throwableRenderer; |
|
} |
|
|
|
|
|
/** |
|
Shutting down a hierarchy will <em>safely</em> close and remove |
|
all appenders in all categories including the root logger. |
|
|
|
<p>Some appenders such as {@link 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 |
|
void shutdown() { |
|
Logger root = getRootLogger(); |
|
|
|
// begin by closing nested appenders |
|
root.closeNestedAppenders(); |
|
|
|
synchronized(ht) { |
|
Enumeration cats = this.getCurrentLoggers(); |
|
while(cats.hasMoreElements()) { |
|
Logger c = (Logger) cats.nextElement(); |
|
c.closeNestedAppenders(); |
|
} |
|
|
|
// then, remove all appenders |
|
root.removeAllAppenders(); |
|
cats = this.getCurrentLoggers(); |
|
while(cats.hasMoreElements()) { |
|
Logger c = (Logger) cats.nextElement(); |
|
c.removeAllAppenders(); |
|
} |
|
} |
|
} |
|
|
|
|
|
/** |
|
This method loops through all the *potential* parents of |
|
'cat'. There 3 possible cases: |
|
|
|
1) No entry for the potential parent of 'cat' exists |
|
|
|
We create a ProvisionNode for this potential parent and insert |
|
'cat' in that provision node. |
|
|
|
2) There entry is of type Logger for the potential parent. |
|
|
|
The entry is 'cat's nearest existing parent. We update cat's |
|
parent field with this entry. We also break from the loop |
|
because updating our parent's parent is our parent's |
|
responsibility. |
|
|
|
3) There entry is of type ProvisionNode for this potential parent. |
|
|
|
We add 'cat' to the list of children for this potential parent. |
|
*/ |
|
final |
|
private |
|
void updateParents(Logger cat) { |
|
String name = cat.name; |
|
int length = name.length(); |
|
boolean parentFound = false; |
|
|
|
//System.out.println("UpdateParents called for " + name); |
|
|
|
// if name = "w.x.y.z", loop thourgh "w.x.y", "w.x" and "w", but not "w.x.y.z" |
|
for(int i = name.lastIndexOf('.', length-1); i >= 0; |
|
i = name.lastIndexOf('.', i-1)) { |
|
String substr = name.substring(0, i); |
|
|
|
//System.out.println("Updating parent : " + substr); |
|
CategoryKey key = new CategoryKey(substr); // simple constructor |
|
Object o = ht.get(key); |
|
// Create a provision node for a future parent. |
|
if(o == null) { |
|
//System.out.println("No parent "+substr+" found. Creating ProvisionNode."); |
|
ProvisionNode pn = new ProvisionNode(cat); |
|
ht.put(key, pn); |
|
} else if(o instanceof Category) { |
|
parentFound = true; |
|
cat.parent = (Category) o; |
|
//System.out.println("Linking " + cat.name + " -> " + ((Category) o).name); |
|
break; // no need to update the ancestors of the closest ancestor |
|
} else if(o instanceof ProvisionNode) { |
|
((ProvisionNode) o).addElement(cat); |
|
} else { |
|
Exception e = new IllegalStateException("unexpected object type " + |
|
o.getClass() + " in ht."); |
|
e.printStackTrace(); |
|
} |
|
} |
|
// If we could not find any existing parents, then link with root. |
|
if(!parentFound) |
|
cat.parent = root; |
|
} |
|
|
|
/** |
|
We update the links for all the children that placed themselves |
|
in the provision node 'pn'. The second argument 'cat' is a |
|
reference for the newly created Logger, parent of all the |
|
children in 'pn' |
|
|
|
We loop on all the children 'c' in 'pn': |
|
|
|
If the child 'c' has been already linked to a child of |
|
'cat' then there is no need to update 'c'. |
|
|
|
Otherwise, we set cat's parent field to c's parent and set |
|
c's parent field to cat. |
|
|
|
*/ |
|
final |
|
private |
|
void updateChildren(ProvisionNode pn, Logger logger) { |
|
//System.out.println("updateChildren called for " + logger.name); |
|
final int last = pn.size(); |
|
|
|
for(int i = 0; i < last; i++) { |
|
Logger l = (Logger) pn.elementAt(i); |
|
//System.out.println("Updating child " +p.name); |
|
|
|
// Unless this child already points to a correct (lower) parent, |
|
// make cat.parent point to l.parent and l.parent to cat. |
|
if(!l.parent.name.startsWith(logger.name)) { |
|
logger.parent = l.parent; |
|
l.parent = logger; |
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
|
|
|