View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  package org.apache.log4j;
    19
    20  import org.apache.log4j.helpers.LogLog;
    21  import org.apache.log4j.or.ObjectRenderer;
    22  import org.apache.log4j.or.RendererMap;
    23  import org.apache.log4j.plugins.Plugin;
    24  import org.apache.log4j.plugins.PluginRegistry;
    25  import org.apache.log4j.scheduler.Scheduler;
    26  import org.apache.log4j.spi.*;
    27  import org.apache.log4j.xml.DOMConfigurator;
    28  import org.apache.log4j.xml.UnrecognizedElementHandler;
    29  import org.w3c.dom.Element;
    30
    31  import java.util.*;
    32
    33
    34  /**
    35   * This class implements LoggerRepositoryEx by
    36   * wrapping an existing LoggerRepository implementation
    37   * and implementing the newly added capabilities.
    38   */
    39  public final class LoggerRepositoryExImpl
    40      implements LoggerRepositoryEx,
    41      RendererSupport,
    42      UnrecognizedElementHandler {
    43
    44      /**
    45       * Wrapped logger repository.
    46       */
    47      private final LoggerRepository repo;
    48
    49      /**
    50       * Logger factory.  Does not affect class of logger
    51       * created by underlying repository.
    52       */
    53      private LoggerFactory loggerFactory;
    54
    55      /**
    56       * Renderer support.
    57       */
    58      private final RendererSupport rendererSupport;
    59
    60      /**
    61       * List of repository event listeners.
    62       */
    63      private final ArrayList<LoggerRepositoryEventListener> repositoryEventListeners = new ArrayList<>();
    64      /**
    65       * Map of HierarchyEventListener keyed by LoggingEventListener.
    66       */
    67      private final Map<LoggerEventListener, HierarchyEventListenerProxy> loggerEventListeners = new HashMap<>();
    68      /**
    69       * Name of hierarchy.
    70       */
    71      private String name;
    72      /**
    73       * Plug in registry.
    74       */
    75      private PluginRegistry pluginRegistry;
    76      /**
    77       * Properties.
    78       */
    79      private final Map<String, String> properties = new Hashtable<>();
    80      /**
    81       * Scheduler.
    82       */
    83      private Scheduler scheduler;
    84
    85      /**
    86       * The repository can also be used as an object store
    87       * for various objects used by log4j components.
    88       */
    89      private Map<String, Object> objectMap = new HashMap<>();
    90
    91
    92      /**
    93       * Error list.
    94       */
    95      private List<ErrorItem> errorList = new Vector<>();
    96
    97      /**
    98       * True if hierarchy has not been modified.
    99       */
    100     private boolean pristine = true;
    101
    102     /**
    103      * Constructs a new logger hierarchy.
    104      *
    105      * @param repository Base implementation of repository.
    106      */
    107     public LoggerRepositoryExImpl(final LoggerRepository repository) {
    108         super();
    109         if (repository == null) {
    110             throw new NullPointerException("repository");
    111         }
    112         repo = repository;
    113         if (repository instanceof RendererSupport) {
    114             rendererSupport = (RendererSupport) repository;
    115         } else {
    116             rendererSupport = new RendererSupportImpl();
    117         }
    118     }
    119
    120
    121     /**
    122      * Add a {@link LoggerRepositoryEventListener} to the repository. The
    123      * listener will be called when repository events occur.
    124      *
    125      * @param listener listener
    126      */
    127     public void addLoggerRepositoryEventListener(
    128         final LoggerRepositoryEventListener listener) {
    129         synchronized (repositoryEventListeners) {
    130             if (repositoryEventListeners.contains(listener)) {
    131                 LogLog.warn(
    132                     "Ignoring attempt to add a previously "
    133                         + "registered LoggerRepositoryEventListener.");
    134             } else {
    135                 repositoryEventListeners.add(listener);
    136             }
    137         }
    138     }
    139
    140
    141     /**
    142      * Remove a {@link LoggerRepositoryEventListener} from the repository.
    143      *
    144      * @param listener listener
    145      */
    146     public void removeLoggerRepositoryEventListener(
    147         final LoggerRepositoryEventListener listener) {
    148         synchronized (repositoryEventListeners) {
    149             if (!repositoryEventListeners.contains(listener)) {
    150                 LogLog.warn(
    151                     "Ignoring attempt to remove a "
    152                         + "non-registered LoggerRepositoryEventListener.");
    153             } else {
    154                 repositoryEventListeners.remove(listener);
    155             }
    156         }
    157     }
    158
    159     /**
    160      * Add a {@link LoggerEventListener} to the repository. The  listener
    161      * will be called when repository events occur.
    162      *
    163      * @param listener listener
    164      */
    165     public void addLoggerEventListener(final LoggerEventListener listener) {
    166         synchronized (loggerEventListeners) {
    167             if (loggerEventListeners.get(listener) != null) {
    168                 LogLog.warn(
    169                     "Ignoring attempt to add a previously registerd LoggerEventListener.");
    170             } else {
    171                 HierarchyEventListenerProxy proxy =
    172                     new HierarchyEventListenerProxy(listener);
    173                 loggerEventListeners.put(listener, proxy);
    174                 repo.addHierarchyEventListener(proxy);
    175             }
    176         }
    177     }
    178
    179     /**
    180      * Add a {@link org.apache.log4j.spi.HierarchyEventListener}
    181      * event to the repository.
    182      *
    183      * @param listener listener
    184      * @deprecated Superceded by addLoggerEventListener
    185      */
    186     public void addHierarchyEventListener(final HierarchyEventListener listener) {
    187         repo.addHierarchyEventListener(listener);
    188     }
    189
    190
    191     /**
    192      * Remove a {@link LoggerEventListener} from the repository.
    193      *
    194      * @param listener listener to be removed
    195      */
    196     public void removeLoggerEventListener(final LoggerEventListener listener) {
    197         synchronized (loggerEventListeners) {
    198             HierarchyEventListenerProxy proxy =
    199                 loggerEventListeners.get(listener);
    200             if (proxy == null) {
    201                 LogLog.warn(
    202                     "Ignoring attempt to remove a non-registered LoggerEventListener.");
    203             } else {
    204                 loggerEventListeners.remove(listener);
    205                 proxy.disable();
    206             }
    207         }
    208     }
    209
    210     /**
    211      * Issue warning that there are no appenders in hierarchy.
    212      *
    213      * @param cat logger, not currently used.
    214      */
    215     public void emitNoAppenderWarning(final Category cat) {
    216         repo.emitNoAppenderWarning(cat);
    217     }
    218
    219     /**
    220      * Check if the named logger exists in the hierarchy. If so return
    221      * its reference, otherwise returns <code>null</code>.
    222      *
    223      * @param loggerName The name of the logger to search for.
    224      * @return true if logger exists.
    225      */
    226     public Logger exists(final String loggerName) {
    227         return repo.exists(loggerName);
    228     }
    229
    230     /**
    231      * Return the name of this hierarchy.
    232      *
    233      * @return name of hierarchy
    234      */
    235     public String getName() {
    236         return name;
    237     }
    238
    239     /**
    240      * Set the name of this repository.
    241      * <p>
    242      * Note that once named, a repository cannot be rerenamed.
    243      *
    244      * @param repoName name of hierarchy
    245      */
    246     public void setName(final String repoName) {
    247         if (name == null) {
    248             name = repoName;
    249         } else if (!name.equals(repoName)) {
    250             throw new IllegalStateException(
    251                 "Repository [" + name + "] cannot be renamed as [" + repoName + "].");
    252         }
    253     }
    254
    255     /**
    256      * {@inheritDoc}
    257      */
    258     public Map<String, String> getProperties() {
    259         return properties;
    260     }
    261
    262     /**
    263      * {@inheritDoc}
    264      */
    265     public String getProperty(final String key) {
    266         return properties.get(key);
    267     }
    268
    269     /**
    270      * Set a property by key and value. The property will be shared by all
    271      * events in this repository.
    272      *
    273      * @param key   property name
    274      * @param value property value
    275      */
    276     public void setProperty(final String key,
    277                             final String value) {
    278         properties.put(key, value);
    279     }
    280
    281     /**
    282      * The string form of {@link #setThreshold(Level)}.
    283      *
    284      * @param levelStr symbolic name for level
    285      */
    286     public void setThreshold(final String levelStr) {
    287         repo.setThreshold(levelStr);
    288     }
    289
    290     /**
    291      * Enable logging for logging requests with level <code>l</code> or
    292      * higher. By default all levels are enabled.
    293      *
    294      * @param l The minimum level for which logging requests are sent to
    295      *          their appenders.
    296      */
    297     public void setThreshold(final Level l) {
    298         repo.setThreshold(l);
    299     }
    300
    301     /**
    302      * {@inheritDoc}
    303      */
    304     public PluginRegistry getPluginRegistry() {
    305         if (pluginRegistry == null) {
    306             pluginRegistry = new PluginRegistry(this);
    307         }
    308         return pluginRegistry;
    309     }
    310
    311
    312     /**
    313      * Requests that a appender added event be sent to any registered
    314      * {@link LoggerEventListener}.
    315      *
    316      * @param logger   The logger to which the appender was added.
    317      * @param appender The appender added to the logger.
    318      */
    319     public void fireAddAppenderEvent(final Category logger,
    320                                      final Appender appender) {
    321         repo.fireAddAppenderEvent(logger, appender);
    322     }
    323
    324
    325     /**
    326      * Requests that a appender removed event be sent to any registered
    327      * {@link LoggerEventListener}.
    328      *
    329      * @param logger   The logger from which the appender was removed.
    330      * @param appender The appender removed from the logger.
    331      */
    332     public void fireRemoveAppenderEvent(final Category logger,
    333                                         final Appender appender) {
    334         if (repo instanceof Hierarchy) {
    335             ((Hierarchy) repo).fireRemoveAppenderEvent(logger, appender);
    336         }
    337     }
    338
    339
    340     /**
    341      * Requests that a level changed event be sent to any registered
    342      * {@link LoggerEventListener}.
    343      *
    344      * @param logger The logger which changed levels.
    345      */
    346     public void fireLevelChangedEvent(final Logger logger) {
    347     }
    348
    349     /**
    350      * Requests that a configuration changed event be sent to any registered
    351      * {@link LoggerRepositoryEventListener}.
    352      */
    353     public void fireConfigurationChangedEvent() {
    354     }
    355
    356
    357     /**
    358      * Returns the current threshold.
    359      *
    360      * @return current threshold level
    361      * @since 1.2
    362      */
    363     public Level getThreshold() {
    364         return repo.getThreshold();
    365     }
    366
    367
    368     /**
    369      * Return a new logger instance named as the first parameter using
    370      * the default factory.
    371      * <p>
    372      * <p>If a logger of that name already exists, then it will be
    373      * returned.  Otherwise, a new logger will be instantiated and
    374      * then linked with its existing ancestors as well as children.
    375      *
    376      * @param loggerName The name of the logger to retrieve.
    377      * @return logger
    378      */
    379     public Logger getLogger(final String loggerName) {
    380         return repo.getLogger(loggerName);
    381     }
    382
    383     /**
    384      * Return a new logger instance named as the first parameter using
    385      * <code>factory</code>.
    386      * <p>
    387      * <p>If a logger of that name already exists, then it will be
    388      * returned.  Otherwise, a new logger will be instantiated by the
    389      * <code>factory</code> parameter and linked with its existing
    390      * ancestors as well as children.
    391      *
    392      * @param loggerName The name of the logger to retrieve.
    393      * @param factory    The factory that will make the new logger instance.
    394      * @return logger
    395      */
    396     public Logger getLogger(final String loggerName,
    397                             final LoggerFactory factory) {
    398         return repo.getLogger(loggerName, factory);
    399     }
    400
    401     /**
    402      * Returns all the currently defined categories in this hierarchy as
    403      * an {@link java.util.Enumeration Enumeration}.
    404      * <p>
    405      * <p>The root logger is <em>not</em> included in the returned
    406      * {@link Enumeration}.
    407      *
    408      * @return enumerator of current loggers
    409      */
    410     public Enumeration getCurrentLoggers() {
    411         return repo.getCurrentLoggers();
    412     }
    413
    414     /**
    415      * Return the the list of previously encoutered {@link ErrorItem error items}.
    416      *
    417      * @return list of errors
    418      */
    419     public List<ErrorItem> getErrorList() {
    420         return errorList;
    421     }
    422
    423     /**
    424      * Add an error item to the list of previously encountered errors.
    425      *
    426      * @param errorItem error to add to list of errors.
    427      */
    428     public void addErrorItem(final ErrorItem errorItem) {
    429         getErrorList().add(errorItem);
    430     }
    431
    432     /**
    433      * Get enumerator over current loggers.
    434      *
    435      * @return enumerator over current loggers
    436      * @deprecated Please use {@link #getCurrentLoggers} instead.
    437      */
    438     public Enumeration getCurrentCategories() {
    439         return repo.getCurrentCategories();
    440     }
    441
    442     /**
    443      * Get the renderer map for this hierarchy.
    444      *
    445      * @return renderer map
    446      */
    447     public RendererMap getRendererMap() {
    448         return rendererSupport.getRendererMap();
    449     }
    450
    451     /**
    452      * Get the root of this hierarchy.
    453      *
    454      * @return root of hierarchy
    455      * @since 0.9.0
    456      */
    457     public Logger getRootLogger() {
    458         return repo.getRootLogger();
    459     }
    460
    461     /**
    462      * This method will return <code>true</code> if this repository is
    463      * disabled for <code>level</code> value passed as parameter and
    464      * <code>false</code> otherwise. See also the {@link
    465      * #setThreshold(Level) threshold} method.
    466      *
    467      * @param level numeric value for level.
    468      * @return true if disabled for specified level
    469      */
    470     public boolean isDisabled(final int level) {
    471         return repo.isDisabled(level);
    472     }
    473
    474     /**
    475      * Reset all values contained in this hierarchy instance to their
    476      * default.  This removes all appenders from all categories, sets
    477      * the level of all non-root categories to <code>null</code>,
    478      * sets their additivity flag to <code>true</code> and sets the level
    479      * of the root logger to DEBUG.  Moreover,
    480      * message disabling is set its default "off" value.
    481      * <p>
    482      * <p>Existing categories are not removed. They are just reset.
    483      * <p>
    484      * <p>This method should be used sparingly and with care as it will
    485      * block all logging until it is completed.</p>
    486      *
    487      * @since 0.8.5
    488      */
    489     public void resetConfiguration() {
    490         repo.resetConfiguration();
    491     }
    492
    493     /**
    494      * Used by subclasses to add a renderer to the hierarchy passed as parameter.
    495      *
    496      * @param renderedClass class
    497      * @param renderer      object used to render class.
    498      */
    499     public void setRenderer(final Class renderedClass,
    500                             final ObjectRenderer renderer) {
    501         rendererSupport.setRenderer(renderedClass, renderer);
    502     }
    503
    504     /**
    505      * {@inheritDoc}
    506      */
    507     public boolean isPristine() {
    508         return pristine;
    509     }
    510
    511     /**
    512      * {@inheritDoc}
    513      */
    514     public void setPristine(final boolean state) {
    515         pristine = state;
    516     }
    517
    518     /**
    519      * Shutting down a hierarchy will <em>safely</em> close and remove
    520      * all appenders in all categories including the root logger.
    521      * <p>
    522      * <p>Some appenders such as org.apache.log4j.net.SocketAppender
    523      * and AsyncAppender need to be closed before the
    524      * application exists. Otherwise, pending logging events might be
    525      * lost.
    526      * <p>
    527      * <p>The <code>shutdown</code> method is careful to close nested
    528      * appenders before closing regular appenders. This is allows
    529      * configurations where a regular appender is attached to a logger
    530      * and again to a nested appender.
    531      *
    532      * @since 1.0
    533      */
    534     public void shutdown() {
    535         repo.shutdown();
    536     }
    537
    538
    539     /**
    540      * Return this repository's own scheduler.
    541      * The scheduler is lazily instantiated.
    542      *
    543      * @return this repository's own scheduler.
    544      */
    545     public Scheduler getScheduler() {
    546         if (scheduler == null) {
    547             scheduler = new Scheduler();
    548             scheduler.setDaemon(true);
    549             scheduler.start();
    550         }
    551         return scheduler;
    552     }
    553
    554     /**
    555      * Puts object by key.
    556      *
    557      * @param key   key, may not be null.
    558      * @param value object to associate with key.
    559      */
    560     public void putObject(final String key,
    561                           final Object value) {
    562         objectMap.put(key, value);
    563     }
    564
    565     /**
    566      * Get object by key.
    567      *
    568      * @param key key, may not be null.
    569      * @return object associated with key or null.
    570      */
    571     public Object getObject(final String key) {
    572         return objectMap.get(key);
    573     }
    574
    575     /**
    576      * Set logger factory.
    577      *
    578      * @param factory logger factory.
    579      */
    580     public void setLoggerFactory(final LoggerFactory factory) {
    581         if (factory == null) {
    582             throw new NullPointerException();
    583         }
    584         this.loggerFactory = factory;
    585     }
    586
    587     /**
    588      * Get logger factory.
    589      *
    590      * @return logger factory.
    591      */
    592     public LoggerFactory getLoggerFactory() {
    593         return loggerFactory;
    594     }
    595
    596     /**
    597      * {@inheritDoc}
    598      */
    599     public boolean parseUnrecognizedElement(
    600         final Element element,
    601         final Properties props) throws Exception {
    602         if ("plugin".equals(element.getNodeName())) {
    603             Object instance =
    604                 DOMConfigurator.parseElement(element, props, Plugin.class);
    605             if (instance instanceof Plugin) {
    606                 Plugin plugin = (Plugin) instance;
    607                 String pluginName = DOMConfigurator.subst(element.getAttribute("name"), props);
    608                 if (pluginName.length() > 0) {
    609                     plugin.setName(pluginName);
    610                 }
    611                 getPluginRegistry().addPlugin(plugin);
    612                 plugin.setLoggerRepository(this);
    613
    614                 LogLog.debug("Pushing plugin on to the object stack.");
    615                 plugin.activateOptions();
    616                 return true;
    617             }
    618         }
    619         return false;
    620     }
    621
    622
    623     /**
    624      * Implementation of RendererSupportImpl if not
    625      * provided by LoggerRepository.
    626      */
    627     private static final class RendererSupportImpl implements RendererSupport {
    628         /**
    629          * Renderer map.
    630          */
    631         private final RendererMap renderers = new RendererMap();
    632
    633         /**
    634          * Create new instance.
    635          */
    636         public RendererSupportImpl() {
    637             super();
    638         }
    639
    640         /**
    641          * {@inheritDoc}
    642          */
    643         public RendererMap getRendererMap() {
    644             return renderers;
    645         }
    646
    647         /**
    648          * {@inheritDoc}
    649          */
    650         public void setRenderer(final Class renderedClass,
    651                                 final ObjectRenderer renderer) {
    652             renderers.put(renderedClass, renderer);
    653         }
    654     }
    655
    656     /**
    657      * Proxy that implements HierarchyEventListener
    658      * and delegates to LoggerEventListener.
    659      */
    660     private static final class HierarchyEventListenerProxy
    661         implements HierarchyEventListener {
    662         /**
    663          * Wrapper listener.
    664          */
    665         private LoggerEventListener listener;
    666
    667         /**
    668          * Creates new instance.
    669          *
    670          * @param l listener
    671          */
    672         public HierarchyEventListenerProxy(final LoggerEventListener l) {
    673             super();
    674             if (l == null) {
    675                 throw new NullPointerException("l");
    676             }
    677             listener = l;
    678         }
    679
    680         /**
    681          * {@inheritDoc}
    682          */
    683         public void addAppenderEvent(final Category cat,
    684                                      final Appender appender) {
    685             if (isEnabled() && cat instanceof Logger) {
    686                 listener.appenderAddedEvent((Logger) cat, appender);
    687             }
    688         }
    689
    690         /**
    691          * {@inheritDoc}
    692          */
    693         public void removeAppenderEvent(final Category cat,
    694                                         final Appender appender) {
    695             if (isEnabled() && cat instanceof Logger) {
    696                 listener.appenderRemovedEvent((Logger) cat, appender);
    697             }
    698         }
    699
    700         /**
    701          * Disable forwarding of notifications to
    702          * simulate removal of listener.
    703          */
    704         public synchronized void disable() {
    705             listener = null;
    706         }
    707
    708         /**
    709          * Gets whether proxy is enabled.
    710          *
    711          * @return true if proxy is enabled.
    712          */
    713         private synchronized boolean isEnabled() {
    714             return listener != null;
    715         }
    716     }
    717
    718 }