You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2022/01/05 23:38:42 UTC

[logging-log4j2] 01/02: Log4j 1.2 bridge adds org.apache.log4j.Hierarchy.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 2e222378f407443613705764fb44a64c3b986eda
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Jan 5 18:36:35 2022 -0500

    Log4j 1.2 bridge adds org.apache.log4j.Hierarchy.
---
 .../main/java/org/apache/log4j/CategoryKey.java    |  50 ++
 .../org/apache/log4j/DefaultCategoryFactory.java   |  31 ++
 .../src/main/java/org/apache/log4j/Hierarchy.java  | 532 +++++++++++++++++++++
 .../main/java/org/apache/log4j/ProvisionNode.java  |  29 ++
 .../java/org/apache/log4j/spi/LoggerFactory.java   |   1 -
 5 files changed, 642 insertions(+), 1 deletion(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/CategoryKey.java b/log4j-1.2-api/src/main/java/org/apache/log4j/CategoryKey.java
new file mode 100644
index 0000000..048c6b1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/CategoryKey.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package org.apache.log4j;
+
+/**
+ * CategoryKey is a wrapper for String that apparently accelerated hash table lookup in early JVM's.
+ */
+class CategoryKey {
+
+    String name;
+    int hashCache;
+
+    CategoryKey(final String name) {
+        this.name = name;
+        this.hashCache = name.hashCode();
+    }
+
+    @Override
+    final public int hashCode() {
+        return hashCache;
+    }
+
+    @Override
+    final public boolean equals(final Object rArg) {
+        if (this == rArg) {
+            return true;
+        }
+
+        if (rArg != null && CategoryKey.class == rArg.getClass()) {
+            return name.equals(((CategoryKey) rArg).name);
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultCategoryFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultCategoryFactory.java
new file mode 100644
index 0000000..cb443ea
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultCategoryFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package org.apache.log4j;
+
+import org.apache.log4j.spi.LoggerFactory;
+
+class DefaultCategoryFactory implements LoggerFactory {
+
+    DefaultCategoryFactory() {
+    }
+
+    @Override
+    public Logger makeNewLoggerInstance(final String name) {
+        return new Logger(name);
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java
new file mode 100644
index 0000000..b497706
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java
@@ -0,0 +1,532 @@
+/*
+ * 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.
+
+package org.apache.log4j;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.log4j.helpers.LogLog;
+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;
+
+/**
+ * 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>
+ * <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>
+ * <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.
+ * </p>
+ */
+public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport {
+
+    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.
+     *
+     * <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();
+    }
+
+    @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 <code>null</code>.
+     *
+     * @param name The name of the logger to search for.
+     *
+     */
+    @Override
+    public Logger exists(final String name) {
+        final Object o = ht.get(new CategoryKey(name));
+        if (o instanceof Logger) {
+            return (Logger) o;
+        } else {
+            return null;
+        }
+    }
+
+    @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);
+            }
+        }
+    }
+
+    /**
+     * @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}.
+     *
+     * <p>
+     * The root logger is <em>not</em> 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();
+    }
+
+    /**
+     * Gets 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.
+     *
+     */
+    @Override
+    public Logger getLogger(final String name) {
+        return getLogger(name, defaultFactory);
+    }
+
+    /**
+     * Gets an integer representation of the this repository's threshold.
+     *
+     * @since 1.2
+     */
+    // public
+    // int getThresholdInt() {
+    // return thresholdInt;
+    // }
+
+    /**
+     * Gets 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.
+     *
+     */
+    @Override
+    public Logger getLogger(final String name, final LoggerFactory factory) {
+        // System.out.println("getInstance("+name+") called.");
+        final 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) {
+            final 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.
+            }
+        }
+    }
+
+    /**
+     * 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 root;
+    }
+
+    /**
+     * Gets a {@link Level} representation of the <code>enable</code> state.
+     *
+     * @since 1.2
+     */
+    @Override
+    public Level getThreshold() {
+        return threshold;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ThrowableRenderer getThrowableRenderer() {
+        return throwableRenderer;
+    }
+
+    /**
+     * 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.
+     */
+    @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 <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
+     */
+    @Override
+    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
+
+            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 <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.
+     */
+    @Override
+    public void setThreshold(final Level l) {
+        if (l != null) {
+            thresholdInt = l.level;
+            threshold = l;
+        }
+    }
+
+    /**
+     * The string form of {@link #setThreshold(Level)}.
+     */
+    @Override
+    public void setThreshold(final String levelStr) {
+        final Level l = (Level) Level.toLevel(levelStr, null);
+        if (l != null) {
+            setThreshold(l);
+        } else {
+            LogLog.warn("Could not convert [" + levelStr + "] to Level.");
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setThrowableRenderer(final ThrowableRenderer renderer) {
+        throwableRenderer = renderer;
+    }
+
+    /**
+     * 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 org.apache.log4j.net.SocketAppender} and {@link AsyncAppender} need to be closed before
+     * the application exists. Otherwise, pending logging events might be lost.
+     * </p>
+     * <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.
+     * </p>
+     *
+     * @since 1.0
+     */
+    @Override
+    public void shutdown() {
+        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();
+            }
+        }
+    }
+
+    /**
+     * 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;
+        }
+    }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/ProvisionNode.java b/log4j-1.2-api/src/main/java/org/apache/log4j/ProvisionNode.java
new file mode 100644
index 0000000..31cfb82
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/ProvisionNode.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package org.apache.log4j;
+
+import java.util.Vector;
+
+class ProvisionNode extends Vector {
+    private static final long serialVersionUID = -4479121426311014469L;
+
+    ProvisionNode(final Logger logger) {
+        super();
+        this.addElement(logger);
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java
index e2f3708..69c30db 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java
@@ -19,7 +19,6 @@ package org.apache.log4j.spi;
 import org.apache.log4j.Logger;
 
 /**
- *
  * Implement this interface to create new instances of Logger or a sub-class of Logger.
  *
  * <p>