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