| /* |
| * 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.helpers.LogLog; |
| import org.apache.log4j.or.ObjectRenderer; |
| import org.apache.log4j.or.RendererMap; |
| import org.apache.log4j.plugins.Plugin; |
| import org.apache.log4j.plugins.PluginRegistry; |
| import org.apache.log4j.scheduler.Scheduler; |
| import org.apache.log4j.spi.ErrorItem; |
| import org.apache.log4j.spi.HierarchyEventListener; |
| import org.apache.log4j.spi.LoggerEventListener; |
| import org.apache.log4j.spi.LoggerFactory; |
| import org.apache.log4j.spi.LoggerRepository; |
| import org.apache.log4j.spi.LoggerRepositoryEventListener; |
| import org.apache.log4j.spi.LoggerRepositoryEx; |
| import org.apache.log4j.spi.RendererSupport; |
| import org.apache.log4j.xml.UnrecognizedElementHandler; |
| import org.apache.log4j.xml.DOMConfigurator; |
| import org.w3c.dom.Element; |
| |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Vector; |
| |
| |
| /** |
| * This class implements LoggerRepositoryEx by |
| * wrapping an existing LoggerRepository implementation |
| * and implementing the newly added capabilities. |
| */ |
| public final class LoggerRepositoryExImpl |
| implements LoggerRepositoryEx, |
| RendererSupport, |
| UnrecognizedElementHandler { |
| |
| /** |
| * Wrapped logger repository. |
| */ |
| private final LoggerRepository repo; |
| |
| /** |
| * Logger factory. Does not affect class of logger |
| * created by underlying repository. |
| */ |
| private LoggerFactory loggerFactory; |
| |
| /** |
| * Renderer support. |
| */ |
| private final RendererSupport rendererSupport; |
| |
| /** |
| * List of repository event listeners. |
| */ |
| private final ArrayList repositoryEventListeners = new ArrayList(); |
| /** |
| * Map of HierarchyEventListener keyed by LoggingEventListener. |
| */ |
| private final Map loggerEventListeners = new HashMap(); |
| /** |
| * Name of hierarchy. |
| */ |
| private String name; |
| /** |
| * Plug in registry. |
| */ |
| private PluginRegistry pluginRegistry; |
| /** |
| * Properties. |
| */ |
| private final Map properties = new Hashtable(); |
| /** |
| * Scheduler. |
| */ |
| private Scheduler scheduler; |
| |
| /** The repository can also be used as an object store |
| * for various objects used by log4j components. |
| */ |
| private Map objectMap = new HashMap(); |
| |
| |
| /** |
| * Error list. |
| */ |
| private List errorList = new Vector(); |
| |
| /** |
| * True if hierarchy has not been modified. |
| */ |
| private boolean pristine = true; |
| |
| /** |
| Constructs a new logger hierarchy. |
| |
| @param repository Base implementation of repository. |
| |
| */ |
| public LoggerRepositoryExImpl(final LoggerRepository repository) { |
| super(); |
| if (repository == null) { |
| throw new NullPointerException("repository"); |
| } |
| repo = repository; |
| if (repository instanceof RendererSupport) { |
| rendererSupport = (RendererSupport) repository; |
| } else { |
| rendererSupport = new RendererSupportImpl(); |
| } |
| } |
| |
| |
| /** |
| Add a {@link LoggerRepositoryEventListener} to the repository. The |
| listener will be called when repository events occur. |
| @param listener listener |
| */ |
| public void addLoggerRepositoryEventListener( |
| final LoggerRepositoryEventListener listener) { |
| synchronized (repositoryEventListeners) { |
| if (repositoryEventListeners.contains(listener)) { |
| LogLog.warn( |
| "Ignoring attempt to add a previously " |
| + "registered LoggerRepositoryEventListener."); |
| } else { |
| repositoryEventListeners.add(listener); |
| } |
| } |
| } |
| |
| |
| /** |
| Remove a {@link LoggerRepositoryEventListener} from the repository. |
| @param listener listener |
| */ |
| public void removeLoggerRepositoryEventListener( |
| final LoggerRepositoryEventListener listener) { |
| synchronized (repositoryEventListeners) { |
| if (!repositoryEventListeners.contains(listener)) { |
| LogLog.warn( |
| "Ignoring attempt to remove a " |
| + "non-registered LoggerRepositoryEventListener."); |
| } else { |
| repositoryEventListeners.remove(listener); |
| } |
| } |
| } |
| |
| /** |
| Add a {@link LoggerEventListener} to the repository. The listener |
| will be called when repository events occur. |
| @param listener listener |
| */ |
| public void addLoggerEventListener(final LoggerEventListener listener) { |
| synchronized (loggerEventListeners) { |
| if (loggerEventListeners.get(listener) != null) { |
| LogLog.warn( |
| "Ignoring attempt to add a previously registerd LoggerEventListener."); |
| } else { |
| HierarchyEventListenerProxy proxy = |
| new HierarchyEventListenerProxy(listener); |
| loggerEventListeners.put(listener, proxy); |
| repo.addHierarchyEventListener(proxy); |
| } |
| } |
| } |
| |
| /** |
| Add a {@link org.apache.log4j.spi.HierarchyEventListener} |
| event to the repository. |
| @param listener listener |
| @deprecated Superceded by addLoggerEventListener |
| */ |
| public |
| void addHierarchyEventListener(final HierarchyEventListener listener) { |
| repo.addHierarchyEventListener(listener); |
| } |
| |
| |
| /** |
| Remove a {@link LoggerEventListener} from the repository. |
| @param listener listener to be removed |
| */ |
| public void removeLoggerEventListener(final LoggerEventListener listener) { |
| synchronized (loggerEventListeners) { |
| HierarchyEventListenerProxy proxy = |
| (HierarchyEventListenerProxy) loggerEventListeners.get(listener); |
| if (proxy == null) { |
| LogLog.warn( |
| "Ignoring attempt to remove a non-registered LoggerEventListener."); |
| } else { |
| loggerEventListeners.remove(listener); |
| proxy.disable(); |
| } |
| } |
| } |
| |
| /** |
| * Issue warning that there are no appenders in hierarchy. |
| * @param cat logger, not currently used. |
| */ |
| public void emitNoAppenderWarning(final Category cat) { |
| repo.emitNoAppenderWarning(cat); |
| } |
| |
| /** |
| Check if the named logger exists in the hierarchy. If so return |
| its reference, otherwise returns <code>null</code>. |
| |
| @param loggerName The name of the logger to search for. |
| @return true if logger exists. |
| */ |
| public Logger exists(final String loggerName) { |
| return repo.exists(loggerName); |
| } |
| |
| /** |
| * Return the name of this hierarchy. |
| * @return name of hierarchy |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * Set the name of this repository. |
| * |
| * Note that once named, a repository cannot be rerenamed. |
| * @param repoName name of hierarchy |
| */ |
| public void setName(final String repoName) { |
| if (name == null) { |
| name = repoName; |
| } else if (!name.equals(repoName)) { |
| throw new IllegalStateException( |
| "Repository [" + name + "] cannot be renamed as [" + repoName + "]."); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Map getProperties() { |
| return properties; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getProperty(final String key) { |
| return (String) properties.get(key); |
| } |
| |
| /** |
| * Set a property by key and value. The property will be shared by all |
| * events in this repository. |
| * @param key property name |
| * @param value property value |
| */ |
| public void setProperty(final String key, |
| final String value) { |
| properties.put(key, value); |
| } |
| |
| /** |
| The string form of {@link #setThreshold(Level)}. |
| @param levelStr symbolic name for level |
| */ |
| public void setThreshold(final String levelStr) { |
| repo.setThreshold(levelStr); |
| } |
| |
| /** |
| 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. */ |
| public void setThreshold(final Level l) { |
| repo.setThreshold(l); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public PluginRegistry getPluginRegistry() { |
| if (pluginRegistry == null) { |
| pluginRegistry = new PluginRegistry(this); |
| } |
| return pluginRegistry; |
| } |
| |
| |
| /** |
| Requests that a appender added event be sent to any registered |
| {@link LoggerEventListener}. |
| @param logger The logger to which the appender was added. |
| @param appender The appender added to the logger. |
| */ |
| public void fireAddAppenderEvent(final Category logger, |
| final Appender appender) { |
| repo.fireAddAppenderEvent(logger, appender); |
| } |
| |
| |
| /** |
| Requests that a appender removed event be sent to any registered |
| {@link LoggerEventListener}. |
| @param logger The logger from which the appender was removed. |
| @param appender The appender removed from the logger. |
| */ |
| public void fireRemoveAppenderEvent(final Category logger, |
| final Appender appender) { |
| if (repo instanceof Hierarchy) { |
| ((Hierarchy) repo).fireRemoveAppenderEvent(logger, appender); |
| } |
| } |
| |
| |
| /** |
| Requests that a level changed event be sent to any registered |
| {@link LoggerEventListener}. |
| @param logger The logger which changed levels. |
| */ |
| public void fireLevelChangedEvent(final Logger logger) { |
| } |
| |
| /** |
| * |
| * Requests that a configuration changed event be sent to any registered |
| * {@link LoggerRepositoryEventListener}. |
| * |
| */ |
| public void fireConfigurationChangedEvent() { |
| } |
| |
| |
| /** |
| Returns the current threshold. |
| @return current threshold level |
| |
| @since 1.2 */ |
| public Level getThreshold() { |
| return repo.getThreshold(); |
| } |
| |
| |
| /** |
| Return 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 loggerName The name of the logger to retrieve. |
| @return logger |
| |
| */ |
| public Logger getLogger(final String loggerName) { |
| return repo.getLogger(loggerName); |
| } |
| |
| /** |
| Return 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 loggerName The name of the logger to retrieve. |
| @param factory The factory that will make the new logger instance. |
| @return logger |
| |
| */ |
| public Logger getLogger(final String loggerName, |
| final LoggerFactory factory) { |
| return repo.getLogger(loggerName, factory); |
| } |
| |
| /** |
| Returns 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}. |
| @return enumerator of current loggers |
| */ |
| public Enumeration getCurrentLoggers() { |
| return repo.getCurrentLoggers(); |
| } |
| |
| /** |
| * Return the the list of previously encoutered {@link ErrorItem error items}. |
| * @return list of errors |
| */ |
| public List getErrorList() { |
| return errorList; |
| } |
| |
| /** |
| * Add an error item to the list of previously encountered errors. |
| * @param errorItem error to add to list of errors. |
| */ |
| public void addErrorItem(final ErrorItem errorItem) { |
| getErrorList().add(errorItem); |
| } |
| |
| /** |
| * Get enumerator over current loggers. |
| * @return enumerator over current loggers |
| @deprecated Please use {@link #getCurrentLoggers} instead. |
| */ |
| public Enumeration getCurrentCategories() { |
| return repo.getCurrentCategories(); |
| } |
| |
| /** |
| Get the renderer map for this hierarchy. |
| @return renderer map |
| */ |
| public RendererMap getRendererMap() { |
| return rendererSupport.getRendererMap(); |
| } |
| |
| /** |
| Get the root of this hierarchy. |
| |
| @since 0.9.0 |
| @return root of hierarchy |
| */ |
| public Logger getRootLogger() { |
| return repo.getRootLogger(); |
| } |
| |
| /** |
| This method will return <code>true</code> if this repository is |
| disabled for <code>level</code> value passed as parameter and |
| <code>false</code> otherwise. See also the {@link |
| #setThreshold(Level) threshold} method. |
| @param level numeric value for level. |
| @return true if disabled for specified level |
| */ |
| public boolean isDisabled(final int level) { |
| return repo.isDisabled(level); |
| } |
| |
| /** |
| Reset all values contained in this hierarchy instance to their |
| default. This removes all appenders from all categories, sets |
| the level of all non-root categories to <code>null</code>, |
| sets their additivity flag to <code>true</code> and sets the level |
| of the root logger to 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 */ |
| public void resetConfiguration() { |
| repo.resetConfiguration(); |
| } |
| |
| /** |
| Used by subclasses to add a renderer to the hierarchy passed as parameter. |
| @param renderedClass class |
| @param renderer object used to render class. |
| */ |
| public void setRenderer(final Class renderedClass, |
| final ObjectRenderer renderer) { |
| rendererSupport.setRenderer(renderedClass, renderer); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean isPristine() { |
| return pristine; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setPristine(final boolean state) { |
| pristine = state; |
| } |
| |
| /** |
| 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 org.apache.log4j.net.SocketAppender |
| and AsyncAppender need to be closed before the |
| application exists. Otherwise, pending logging events might be |
| lost. |
| |
| <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. |
| |
| @since 1.0 */ |
| public void shutdown() { |
| repo.shutdown(); |
| } |
| |
| |
| /** |
| * Return this repository's own scheduler. |
| * The scheduler is lazily instantiated. |
| * @return this repository's own scheduler. |
| */ |
| public Scheduler getScheduler() { |
| if (scheduler == null) { |
| scheduler = new Scheduler(); |
| scheduler.setDaemon(true); |
| scheduler.start(); |
| } |
| return scheduler; |
| } |
| |
| /** |
| * Puts object by key. |
| * @param key key, may not be null. |
| * @param value object to associate with key. |
| */ |
| public void putObject(final String key, |
| final Object value) { |
| objectMap.put(key, value); |
| } |
| |
| /** |
| * Get object by key. |
| * @param key key, may not be null. |
| * @return object associated with key or null. |
| */ |
| public Object getObject(final String key) { |
| return objectMap.get(key); |
| } |
| |
| /** |
| * Set logger factory. |
| * @param factory logger factory. |
| */ |
| public void setLoggerFactory(final LoggerFactory factory) { |
| if (factory == null) { |
| throw new NullPointerException(); |
| } |
| this.loggerFactory = factory; |
| } |
| |
| /** |
| * Get logger factory. |
| * @return logger factory. |
| */ |
| public LoggerFactory getLoggerFactory() { |
| return loggerFactory; |
| } |
| |
| /** {@inheritDoc} */ |
| public boolean parseUnrecognizedElement( |
| final Element element, |
| final Properties props) throws Exception { |
| if ("plugin".equals(element.getNodeName())) { |
| Object instance = |
| DOMConfigurator.parseElement(element, props, Plugin.class); |
| if (instance instanceof Plugin) { |
| Plugin plugin = (Plugin) instance; |
| String pluginName = DOMConfigurator.subst(element.getAttribute("name"), props); |
| if (pluginName.length() > 0) { |
| plugin.setName(pluginName); |
| } |
| getPluginRegistry().addPlugin(plugin); |
| plugin.setLoggerRepository(this); |
| |
| LogLog.debug("Pushing plugin on to the object stack."); |
| plugin.activateOptions(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| |
| /** |
| * Implementation of RendererSupportImpl if not |
| * provided by LoggerRepository. |
| */ |
| private static final class RendererSupportImpl implements RendererSupport { |
| /** |
| * Renderer map. |
| */ |
| private final RendererMap renderers = new RendererMap(); |
| |
| /** |
| * Create new instance. |
| */ |
| public RendererSupportImpl() { |
| super(); |
| } |
| |
| /** {@inheritDoc} */ |
| public RendererMap getRendererMap() { |
| return renderers; |
| } |
| |
| /** {@inheritDoc} */ |
| public void setRenderer(final Class renderedClass, |
| final ObjectRenderer renderer) { |
| renderers.put(renderedClass, renderer); |
| } |
| } |
| |
| /** |
| * Proxy that implements HierarchyEventListener |
| * and delegates to LoggerEventListener. |
| */ |
| private static final class HierarchyEventListenerProxy |
| implements HierarchyEventListener { |
| /** |
| * Wrapper listener. |
| */ |
| private LoggerEventListener listener; |
| |
| /** |
| * Creates new instance. |
| * @param l listener |
| */ |
| public HierarchyEventListenerProxy(final LoggerEventListener l) { |
| super(); |
| if (l == null) { |
| throw new NullPointerException("l"); |
| } |
| listener = l; |
| } |
| |
| /** {@inheritDoc} */ |
| public void addAppenderEvent(final Category cat, |
| final Appender appender) { |
| if (isEnabled() && cat instanceof Logger) { |
| listener.appenderAddedEvent((Logger) cat, appender); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public void removeAppenderEvent(final Category cat, |
| final Appender appender) { |
| if (isEnabled() && cat instanceof Logger) { |
| listener.appenderRemovedEvent((Logger) cat, appender); |
| } |
| } |
| |
| /** |
| * Disable forwarding of notifications to |
| * simulate removal of listener. |
| */ |
| public synchronized void disable() { |
| listener = null; |
| } |
| |
| /** |
| * Gets whether proxy is enabled. |
| * @return true if proxy is enabled. |
| */ |
| private synchronized boolean isEnabled() { |
| return listener != null; |
| } |
| } |
| |
| } |