org.apache.log4j.Hierarchy Maven / Gradle / Ivy
Show all versions of reload4j Show documentation
/*
* 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
// Mario Schomburg - IBM Global Services/Germany
// Anders Kristensen
// Igor Poteryaev
package org.apache.log4j;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.log4j.spi.LoggerFactory;
import org.apache.log4j.spi.HierarchyEventListener;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.RendererSupport;
import org.apache.log4j.or.RendererMap;
import org.apache.log4j.or.ObjectRenderer;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.ThrowableRendererSupport;
import org.apache.log4j.spi.ThrowableRenderer;
/**
* This class is specialized in retrieving loggers by name and also maintaining
* the logger hierarchy.
*
*
* The casual user does not have to deal with this class directly.
*
*
* 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.
*
*
* 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.
*
*
* You should really 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 null
.
*
* @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 l
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 enable
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.
*
*
* 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
* factory
.
*
*
* If a logger of that name already exists, then it will be returned. Otherwise,
* a new logger will be instantiated by the factory
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}.
*
*
* The root logger is not 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 true
if this repository is disabled for
* level
object passed as parameter and false
* 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 null
, sets their additivity flag to
* true
and sets the level of the root logger to {@link Level#DEBUG
* DEBUG}. Moreover, message disabling is set its default "off" value.
*
*
* Existing categories are not removed. They are just reset.
*
*
* This method should be used sparingly and with care as it will block all
* logging until it is completed.
*
*
* @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 safely close and remove all appenders
* in all categories including the root logger.
*
*
* Some appenders such as {@link org.apache.log4j.net.SocketAppender} and
* {@link AsyncAppender} need to be closed before the application exists.
* Otherwise, pending logging events might be lost.
*
*
* The shutdown
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;
}
}
}
}