All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.log4j.Hierarchy Maven / Gradle / Ivy

There is a newer version: 1.0.0-beta2
Show newest version
/*
 * 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 initialization neither
// WARNING directly nor indirectly.

package org.apache.log4j;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.legacy.core.ContextUtil;
import org.apache.log4j.or.ObjectRenderer;
import org.apache.log4j.or.RendererMap;
import org.apache.log4j.spi.HierarchyEventListener;
import org.apache.log4j.spi.LoggerFactory;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.RendererSupport;
import org.apache.log4j.spi.ThrowableRenderer;
import org.apache.log4j.spi.ThrowableRendererSupport;
import org.apache.logging.log4j.core.appender.AsyncAppender;
import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
import org.apache.logging.log4j.spi.LoggerContext;
import org.apache.logging.log4j.util.StackLocatorUtil;

/**
 * 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. *

*/ public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport { private static class PrivateLoggerAdapter extends AbstractLoggerAdapter { @Override protected org.apache.logging.log4j.spi.LoggerContext getContext() { return PrivateLogManager.getContext(); } @Override protected Logger newLogger(final String name, final org.apache.logging.log4j.spi.LoggerContext context) { return new Logger(context, name); } } /** * Private LogManager. */ private static class PrivateLogManager extends org.apache.logging.log4j.LogManager { private static final String FQCN = Hierarchy.class.getName(); public static LoggerContext getContext() { return getContext(FQCN, false); } public static org.apache.logging.log4j.Logger getLogger(final String name) { return getLogger(FQCN, name); } } private static final PrivateLoggerAdapter LOGGER_ADAPTER = new PrivateLoggerAdapter(); private static final WeakHashMap> CONTEXT_MAP = new WeakHashMap<>(); static LoggerContext getContext() { return PrivateLogManager.getContext(); } static Logger getInstance(final LoggerContext context, final String name) { return getInstance(context, name, LOGGER_ADAPTER); } static Logger getInstance(final LoggerContext context, final String name, final LoggerFactory factory) { return getLoggersMap(context).computeIfAbsent(name, k -> factory.makeNewLoggerInstance(name)); } static Logger getInstance(final LoggerContext context, final String name, final PrivateLoggerAdapter factory) { return getLoggersMap(context).computeIfAbsent(name, k -> factory.newLogger(name, context)); } static ConcurrentMap getLoggersMap(final LoggerContext context) { synchronized (CONTEXT_MAP) { return CONTEXT_MAP.computeIfAbsent(context, k -> new ConcurrentHashMap<>()); } } static Logger getRootLogger(final LoggerContext context) { return getInstance(context, org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME); } private final LoggerFactory defaultFactory; private final Vector listeners; Hashtable ht; Logger root; RendererMap rendererMap; int thresholdInt; Level threshold; boolean emittedNoAppenderWarning; boolean emittedNoResourceBundleWarning; private ThrowableRenderer throwableRenderer; /** * Creates a new logger hierarchy. * * @param root The root of the new hierarchy. * */ public Hierarchy(final 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(); } @Override public void addHierarchyEventListener(final HierarchyEventListener listener) { if (listeners.contains(listener)) { LogLog.warn("Ignoring attempt to add an existent listener."); } else { listeners.addElement(listener); } } /** * Adds an object renderer for a specific class. */ public void addRenderer(final Class classToRender, final ObjectRenderer or) { rendererMap.put(classToRender, or); } /** * 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(); getLoggersMap(getContext()).clear(); } @Override public void emitNoAppenderWarning(final 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; } } /** * Tests 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. * */ @Override public Logger exists(final String name) { return exists(name, getContext()); } Logger exists(final String name, final ClassLoader classLoader) { return exists(name, getContext(classLoader)); } Logger exists(final String name, final LoggerContext loggerContext) { if (!loggerContext.hasLogger(name)) { return null; } return Logger.getLogger(name); } @Override public void fireAddAppenderEvent(final Category logger, final Appender appender) { if (listeners != null) { final int size = listeners.size(); HierarchyEventListener listener; for (int i = 0; i < size; i++) { listener = (HierarchyEventListener) listeners.elementAt(i); listener.addAppenderEvent(logger, appender); } } } void fireRemoveAppenderEvent(final Category logger, final Appender appender) { if (listeners != null) { final int size = listeners.size(); HierarchyEventListener listener; for (int i = 0; i < size; i++) { listener = (HierarchyEventListener) listeners.elementAt(i); listener.removeAppenderEvent(logger, appender); } } } LoggerContext getContext(final ClassLoader classLoader) { return LogManager.getContext(classLoader); } /** * @deprecated Please use {@link #getCurrentLoggers} instead. */ @Deprecated @Override public Enumeration getCurrentCategories() { return getCurrentLoggers(); } /** * Gets 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}. *

*/ @Override 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. // final Vector v = new Vector(ht.size()); // // final Enumeration elems = ht.elements(); // while (elems.hasMoreElements()) { // final Object o = elems.nextElement(); // if (o instanceof Logger) { // v.addElement(o); // } // } // return v.elements(); return LogManager.getCurrentLoggers(StackLocatorUtil.getCallerClassLoader(2)); } /** * Gets 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. * */ @Override public Logger getLogger(final String name) { return getInstance(getContext(), name); } Logger getLogger(final String name, final ClassLoader classLoader) { return getInstance(getContext(classLoader), name); } /** * Gets 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. * */ @Override public Logger getLogger(final String name, final LoggerFactory factory) { return getInstance(getContext(), name, factory); } Logger getLogger(final String name, final LoggerFactory factory, final ClassLoader classLoader) { return getInstance(getContext(classLoader), name, factory); } /** * Gets the renderer map for this hierarchy. */ @Override public RendererMap getRendererMap() { return rendererMap; } /** * Gets the root of this hierarchy. * * @since 0.9.0 */ @Override public Logger getRootLogger() { return getInstance(getContext(), org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME); } Logger getRootLogger(final ClassLoader classLoader) { return getInstance(getContext(classLoader), org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME); } /** * Gets a {@link Level} representation of the enable state. * * @since 1.2 */ @Override public Level getThreshold() { return threshold; } /** * {@inheritDoc} */ @Override public ThrowableRenderer getThrowableRenderer() { return throwableRenderer; } /** * 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. */ @Override public boolean isDisabled(final int level) { return thresholdInt > level; } /** * @deprecated Deprecated with no replacement. */ @Deprecated public void overrideAsNeeded(final String override) { LogLog.warn("The Hiearchy.overrideAsNeeded method has been deprecated."); } /** * Resets 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 */ @Override public void resetConfiguration() { resetConfiguration(getContext()); } void resetConfiguration(final ClassLoader classLoader) { resetConfiguration(getContext(classLoader)); } void resetConfiguration(final LoggerContext loggerContext) { getLoggersMap(loggerContext).clear(); getRootLogger().setLevel(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 final Enumeration cats = getCurrentLoggers(); while (cats.hasMoreElements()) { final 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. */ @Deprecated public void setDisableOverride(final String override) { LogLog.warn("The Hiearchy.setDisableOverride method has been deprecated."); } /** * Used by subclasses to add a renderer to the hierarchy passed as parameter. */ @Override public void setRenderer(final Class renderedClass, final ObjectRenderer renderer) { rendererMap.put(renderedClass, renderer); } /** * Enable logging for logging requests with level l or higher. By default all levels are enabled. * * @param level The minimum level for which logging requests are sent to their appenders. */ @Override public void setThreshold(final Level level) { if (level != null) { thresholdInt = level.level; threshold = level; } } /** * The string form of {@link #setThreshold(Level)}. */ @Override public void setThreshold(final String levelStr) { final Level level = Level.toLevel(levelStr, null); if (level != null) { setThreshold(level); } else { LogLog.warn("Could not convert [" + levelStr + "] to Level."); } } /** * {@inheritDoc} */ @Override public void setThrowableRenderer(final ThrowableRenderer throwableRenderer) { this.throwableRenderer = 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 */ @Override public void shutdown() { shutdown(getContext()); } public void shutdown(final ClassLoader classLoader) { shutdown(org.apache.logging.log4j.LogManager.getContext(classLoader, false)); } void shutdown(final LoggerContext context) { // final Logger root = getRootLogger(); // // begin by closing nested appenders // root.closeNestedAppenders(); // // synchronized (ht) { // Enumeration cats = this.getCurrentLoggers(); // while (cats.hasMoreElements()) { // final Logger c = (Logger) cats.nextElement(); // c.closeNestedAppenders(); // } // // // then, remove all appenders // root.removeAllAppenders(); // cats = this.getCurrentLoggers(); // while (cats.hasMoreElements()) { // final Logger c = (Logger) cats.nextElement(); // c.removeAllAppenders(); // } // } getLoggersMap(context).clear(); if (LogManager.isLog4jCorePresent()) { ContextUtil.shutdown(context); } } /** * 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(final ProvisionNode pn, final Logger logger) { // System.out.println("updateChildren called for " + logger.name); final int last = pn.size(); for (int i = 0; i < last; i++) { final 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; } } } /** * 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(final Logger cat) { final String name = cat.name; final 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)) { final String substr = name.substring(0, i); // System.out.println("Updating parent : " + substr); final CategoryKey key = new CategoryKey(substr); // simple constructor final 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."); final 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 { final 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; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy