http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java b/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java new file mode 100644 index 0000000..cd37dc4 --- /dev/null +++ b/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java @@ -0,0 +1,316 @@ +/* + * 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.net; + +import java.net.ServerSocket; +import java.net.Socket; +import java.util.List; +import java.util.Vector; + +import org.apache.log4j.plugins.Pauseable; +import org.apache.log4j.plugins.Plugin; +import org.apache.log4j.plugins.Receiver; +import org.apache.log4j.spi.LoggerRepository; +import org.apache.log4j.spi.LoggingEvent; + + +/** + XMLSocketReceiver receives a remote logging event via XML on a configured + socket and "posts" it to a LoggerRepository as if the event were + generated locally. This class is designed to receive events from + the XMLSocketAppender class (or classes that send compatible events). + <p> + This receiver supports log files created using log4j's XMLLayout, as well as java.util.logging + XMLFormatter (via the org.apache.log4j.spi.Decoder interface). + <p> + By default, log4j's XMLLayout is supported (no need to specify a decoder in that case). + <p> + To configure this receiver to support java.util.logging's XMLFormatter, specify a 'decoder' param + of org.apache.log4j.xml.UtilLoggingXMLDecoder. + <p> + Once the event has been "posted", it will be handled by the + appenders currently configured in the LoggerRespository. + + @author Mark Womack + @author Scott Deboy <[email protected]> + +*/ +public class XMLSocketReceiver extends Receiver implements Runnable, PortBased, Pauseable { + private boolean paused; + //default to log4j xml decoder + protected String decoder = "org.apache.log4j.xml.XMLDecoder"; + private ServerSocket serverSocket; + private List socketList = new Vector(); + private Thread rThread; + public static final int DEFAULT_PORT = 4448; + protected int port = DEFAULT_PORT; + private boolean advertiseViaMulticastDNS; + private ZeroConfSupport zeroConf; + + /** + * The MulticastDNS zone advertised by an XMLSocketReceiver + */ + public static final String ZONE = "_log4j_xml_tcpaccept_receiver.local."; + + /* + * Log4j doesn't provide an XMLSocketAppender, but the MulticastDNS zone that should be advertised by one is: + * _log4j_xml_tcpconnect_appender.local. + */ + + public XMLSocketReceiver() { + } + + public XMLSocketReceiver(int _port) { + port = _port; + } + + public XMLSocketReceiver(int _port, LoggerRepository _repository) { + port = _port; + repository = _repository; + } + + /** + Get the port to receive logging events on. */ + public int getPort() { + return port; + } + + /** + Set the port to receive logging events on. */ + public void setPort(int _port) { + port = _port; + } + + public String getDecoder() { + return decoder; + } + + /** + *Specify the class name implementing org.apache.log4j.spi.Decoder that can process the file. + */ + public void setDecoder(String _decoder) { + decoder = _decoder; + } + + public boolean isPaused() { + return paused; + } + + public void setPaused(boolean b) { + paused = b; + } + + /** + * Returns true if the receiver is the same class and they are + * configured for the same properties, and super class also considers + * them to be equivalent. This is used by PluginRegistry when determining + * if the a similarly configured receiver is being started. + * + * @param testPlugin The plugin to test equivalency against. + * @return boolean True if the testPlugin is equivalent to this plugin. + */ + public boolean isEquivalent(Plugin testPlugin) { + if ((testPlugin != null) && testPlugin instanceof XMLSocketReceiver) { + XMLSocketReceiver sReceiver = (XMLSocketReceiver) testPlugin; + + return (port == sReceiver.getPort() && super.isEquivalent(testPlugin)); + } + + return false; + } + + public int hashCode() { + + int result = 37 * (repository != null? repository.hashCode():0); + result = result * 37 + port; + return (result * 37 + (getName() != null? getName().hashCode():0)); + } + + /** + Sets the flag to indicate if receiver is active or not. + @param b new value + */ + protected synchronized void setActive(final boolean b) { + active = b; + } + + /** + Starts the SocketReceiver with the current options. */ + public void activateOptions() { + if (!isActive()) { + rThread = new Thread(this); + rThread.setDaemon(true); + rThread.start(); + + if (advertiseViaMulticastDNS) { + zeroConf = new ZeroConfSupport(ZONE, port, getName()); + zeroConf.advertise(); + } + + active = true; + } + } + + public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) { + this.advertiseViaMulticastDNS = advertiseViaMulticastDNS; + } + + public boolean isAdvertiseViaMulticastDNS() { + return advertiseViaMulticastDNS; + } + + /** + Called when the receiver should be stopped. Closes the + server socket and all of the open sockets. */ + public synchronized void shutdown() { + // mark this as no longer running + active = false; + + if (rThread != null) { + rThread.interrupt(); + rThread = null; + } + doShutdown(); + } + + /** + * Does the actual shutting down by closing the server socket + * and any connected sockets that have been created. + */ + private synchronized void doShutdown() { + active = false; + + getLogger().debug("{} doShutdown called", getName()); + + // close the server socket + closeServerSocket(); + + // close all of the accepted sockets + closeAllAcceptedSockets(); + + if (advertiseViaMulticastDNS) { + zeroConf.unadvertise(); + } + } + + /** + * Closes the server socket, if created. + */ + private void closeServerSocket() { + getLogger().debug("{} closing server socket", getName()); + + try { + if (serverSocket != null) { + serverSocket.close(); + } + } catch (Exception e) { + // ignore for now + } + + serverSocket = null; + } + + /** + * Closes all the connected sockets in the List. + */ + private synchronized void closeAllAcceptedSockets() { + for (int x = 0; x < socketList.size(); x++) { + try { + ((Socket) socketList.get(x)).close(); + } catch (Exception e) { + // ignore for now + } + } + + // clear member variables + socketList.clear(); + } + + /** + Loop, accepting new socket connections. */ + public void run() { + /** + * Ensure we start fresh. + */ + getLogger().debug("performing socket cleanup prior to entering loop for {}", name); + closeServerSocket(); + closeAllAcceptedSockets(); + getLogger().debug("socket cleanup complete for {}", name); + active = true; + + // start the server socket + try { + serverSocket = new ServerSocket(port); + } catch (Exception e) { + getLogger().error( + "error starting SocketReceiver (" + this.getName() + + "), receiver did not start", e); + active = false; + doShutdown(); + + return; + } + + Socket socket = null; + + try { + getLogger().debug("in run-about to enter while isactiveloop"); + + active = true; + + while (!rThread.isInterrupted()) { + // if we have a socket, start watching it + if (socket != null) { + getLogger().debug("socket not null - creating and starting socketnode"); + socketList.add(socket); + + XMLSocketNode node = new XMLSocketNode(decoder, socket, this); + node.setLoggerRepository(this.repository); + new Thread(node).start(); + socket = null; + } + + getLogger().debug("waiting to accept socket"); + + // wait for a socket to open, then loop to start it + socket = serverSocket.accept(); + getLogger().debug("accepted socket"); + } + + // socket not watched because we a no longer running + // so close it now. + if (socket != null) { + socket.close(); + } + } catch (Exception e) { + getLogger().warn( + "socket server disconnected, stopping"); + } + } + + /* (non-Javadoc) + * @see org.apache.log4j.plugins.Receiver#doPost(org.apache.log4j.spi.LoggingEvent) + */ + public void doPost(LoggingEvent event) { + if(!isPaused()){ + super.doPost(event); + } + } + + +}
http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/Pauseable.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/plugins/Pauseable.java b/src/main/java/org/apache/log4j/plugins/Pauseable.java new file mode 100644 index 0000000..6268ba3 --- /dev/null +++ b/src/main/java/org/apache/log4j/plugins/Pauseable.java @@ -0,0 +1,39 @@ +/* + * 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.plugins; + + +/** + * Instances of this interface can be paused, and resumed. + * + * @author Paul Smith ([email protected]) + * + */ +public interface Pauseable { + /** + * Set paused state. + * @param paused new value + */ + void setPaused(boolean paused); + + /** + * Get paused state. + * @return paused state. + */ + boolean isPaused(); +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/Plugin.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/plugins/Plugin.java b/src/main/java/org/apache/log4j/plugins/Plugin.java new file mode 100644 index 0000000..e1b6a6f --- /dev/null +++ b/src/main/java/org/apache/log4j/plugins/Plugin.java @@ -0,0 +1,144 @@ +/* + * 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.plugins; + +import org.apache.log4j.spi.LoggerRepository; +import org.apache.log4j.spi.OptionHandler; + +import java.beans.PropertyChangeListener; + + +/** + * Defines the required interface for all Plugin objects. + * <p/> + * <p>A plugin implements some specific functionality to extend + * the log4j framework. Each plugin is associated with a specific + * LoggerRepository, which it then uses/acts upon. The functionality + * of the plugin is up to the developer. + * <p/> + * <p>Examples of plugins are Receiver and Watchdog. Receiver plugins + * allow for remote logging events to be received and processed by + * a repository as if the event was sent locally. Watchdog plugins + * allow for a repository to be reconfigured when some "watched" + * configuration data changes. + * + * @author Mark Womack ([email protected]) + * @author Nicko Cadell + * @author Paul Smith ([email protected]) + */ +public interface Plugin extends OptionHandler { + /** + * Gets the name of the plugin. + * + * @return String the name of the plugin. + */ + String getName(); + + /** + * Sets the name of the plugin. + * + * @param name the name of the plugin. + */ + void setName(String name); + + /** + * Gets the logger repository for this plugin. + * + * @return the logger repository to which this plugin is attached. + */ + LoggerRepository getLoggerRepository(); + + /** + * Sets the logger repository used by this plugin. This + * repository will be used by the plugin functionality. + * + * @param repository the logger repository to attach this plugin to. + */ + void setLoggerRepository(LoggerRepository repository); + + /** + * Adds a PropertyChangeListener to this instance which is + * notified only by changes of the property with name propertyName. + * + * @param propertyName + * the name of the property in standard JavaBean syntax + * (e.g. for setName(), property="name") + * @param l listener + */ + void addPropertyChangeListener( + String propertyName, PropertyChangeListener l); + + /** + * Adds a PropertyChangeListener that will be notified of all property + * changes. + * + * @param l The listener to add. + */ + void addPropertyChangeListener(PropertyChangeListener l); + + /** + * Removes a specific PropertyChangeListener from this instances + * registry that has been mapped to be notified of all property + * changes. + * + * @param l The listener to remove. + */ + void removePropertyChangeListener(PropertyChangeListener l); + + /** + * Removes a specific PropertyChangeListener from this instance's + * registry which has been previously registered to be notified + * of only a specific property change. + * + * @param propertyName property name, may not be null. + * @param l listener to be removed. + */ + void removePropertyChangeListener( + String propertyName, PropertyChangeListener l); + + /** + * True if the plugin is active and running. + * + * @return boolean true if the plugin is currently active. + */ + boolean isActive(); + + /** + * Returns true if the testPlugin is considered to be "equivalent" to the + * this plugin. The equivalency test is at the discretion of the plugin + * implementation. The PluginRegistry will use this method when starting + * new plugins to see if a given plugin is considered equivalent to an + * already running plugin with the same name. If they are considered to + * be equivalent, the currently running plugin will be left in place, and + * the new plugin will not be started. + * <p/> + * It is possible to override the equals() method, however this has + * more meaning than is required for this simple test and would also + * require the overriding of the hashCode() method as well. All of this + * is more work than is needed, so this simple method is used instead. + * + * @param testPlugin The plugin to test equivalency against. + * @return Returns true if testPlugin is considered to be equivelent. + */ + boolean isEquivalent(Plugin testPlugin); + + /** + * Call when the plugin should be stopped. + */ + void shutdown(); +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/PluginEvent.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/plugins/PluginEvent.java b/src/main/java/org/apache/log4j/plugins/PluginEvent.java new file mode 100644 index 0000000..1843034 --- /dev/null +++ b/src/main/java/org/apache/log4j/plugins/PluginEvent.java @@ -0,0 +1,47 @@ +/* + * 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.plugins; + +import java.util.EventObject; + + +/** + * All Plugin events are encapsulated in this class, which + * simply contains the source Plugin, but may in future include more + * information. + * + * @author Paul Smith + */ +public class PluginEvent extends EventObject { + /** + * @param source The source plugin of the event + */ + PluginEvent(final Plugin source) { + super(source); + } + + /** + * Returns the source Plugin of this event, which is simple + * the getSource() method casted to Plugin for convenience. + * + * @return Plugin source of this event + */ + public Plugin getPlugin() { + return (Plugin) getSource(); + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/PluginListener.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/plugins/PluginListener.java b/src/main/java/org/apache/log4j/plugins/PluginListener.java new file mode 100644 index 0000000..11f628e --- /dev/null +++ b/src/main/java/org/apache/log4j/plugins/PluginListener.java @@ -0,0 +1,43 @@ +/* + * 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.plugins; + +import java.util.EventListener; + + +/** + * PluginListeners are notified when plugins are started or stopped + * by the PluginRegistry. + * + * @author Paul Smith ([email protected]) + */ +public interface PluginListener extends EventListener { + /** + * Notification that plugin has started. + * @param e event + */ + void pluginStarted(PluginEvent e); + + /** + * Notification that plugin has stopped. + * @param e event + */ + void pluginStopped(PluginEvent e); +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/PluginRegistry.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/plugins/PluginRegistry.java b/src/main/java/org/apache/log4j/plugins/PluginRegistry.java new file mode 100644 index 0000000..d34f885 --- /dev/null +++ b/src/main/java/org/apache/log4j/plugins/PluginRegistry.java @@ -0,0 +1,303 @@ +/* + * 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.plugins; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.spi.LoggerRepository; +import org.apache.log4j.spi.LoggerRepositoryEx; +import org.apache.log4j.spi.LoggerRepositoryEventListener; + + +/** + * This is a registry for Plugin instances. It provides methods to + * start and stop plugin objects individually and to stop all + * plugins for a repository. + * + * @author Mark Womack + * @author Paul Smith + */ +public final class PluginRegistry { + /** + * The pluginMap is keyed by plugin name and contains plugins as values. + * key=plugin.getName, value=plugin + */ + private final Map pluginMap; + /** + * Logger repository. + */ + private final LoggerRepositoryEx loggerRepository; + + /** + * the listener used to listen for repository events. + */ + private final RepositoryListener listener = new RepositoryListener(); + /** + * List of listeners. + */ + private final List listenerList = + Collections.synchronizedList(new ArrayList()); + + /** + * Creates a new instance. + * @param repository logger repository. + */ + public PluginRegistry(final LoggerRepositoryEx repository) { + super(); + pluginMap = new HashMap(); + this.loggerRepository = repository; + this.loggerRepository.addLoggerRepositoryEventListener(listener); + } + + /** + * Get logger repository. + * @return logger repository. + */ + public LoggerRepositoryEx getLoggerRepository() { + return loggerRepository; + } + + + /** + * Returns true if the specified name is already taken by + * an existing Plugin registered within the scope of the specified + * LoggerRepository. + * + * @param name The name to check the repository for + * @return true if the name is already in use, otherwise false + */ + public boolean pluginNameExists(final String name) { + synchronized (pluginMap) { + return pluginMap.containsKey(name); + } + } + + + /** + * Adds a plugin to the plugin registry. + * If a plugin with the same name exists + * already, it is shutdown and removed. + * + * @param plugin the plugin to add. + */ + public void addPlugin(final Plugin plugin) { + // put plugin into the repository's reciever map + synchronized (pluginMap) { + String name = plugin.getName(); + + // make sure the plugin has reference to repository + plugin.setLoggerRepository(getLoggerRepository()); + + Plugin existingPlugin = (Plugin) pluginMap.get(name); + if (existingPlugin != null) { + existingPlugin.shutdown(); + } + + // put the new plugin into the map + pluginMap.put(name, plugin); + firePluginStarted(plugin); + } + } + + + /** + * Calls the pluginStarted method on every registered PluginListener. + * + * @param plugin The plugin that has been started. + */ + private void firePluginStarted(final Plugin plugin) { + PluginEvent e = null; + synchronized (listenerList) { + for (Iterator iter = listenerList.iterator(); iter.hasNext();) { + PluginListener l = (PluginListener) iter.next(); + if (e == null) { + e = new PluginEvent(plugin); + } + l.pluginStarted(e); + } + } + } + + + /** + * Calls the pluginStopped method for every registered PluginListner. + * + * @param plugin The plugin that has been stopped. + */ + private void firePluginStopped(final Plugin plugin) { + PluginEvent e = null; + synchronized (listenerList) { + for (Iterator iter = listenerList.iterator(); iter.hasNext();) { + PluginListener l = (PluginListener) iter.next(); + if (e == null) { + e = new PluginEvent(plugin); + } + l.pluginStopped(e); + } + } + } + + + /** + * Returns all the plugins for a given repository. + * + * @return List list of plugins from the repository. + */ + public List getPlugins() { + synchronized (pluginMap) { + List pluginList = new ArrayList(pluginMap.size()); + Iterator iter = pluginMap.values().iterator(); + + while (iter.hasNext()) { + pluginList.add(iter.next()); + } + return pluginList; + } + } + + + /** + * Returns all the plugins for a given repository that are instances + * of a certain class. + * + * @param pluginClass the class the plugin must implement to be selected. + * @return List list of plugins from the repository. + */ + public List getPlugins(final Class pluginClass) { + synchronized (pluginMap) { + List pluginList = new ArrayList(pluginMap.size()); + Iterator iter = pluginMap.values().iterator(); + + while (iter.hasNext()) { + Object plugin = iter.next(); + + if (pluginClass.isInstance(plugin)) { + pluginList.add(plugin); + } + } + return pluginList; + } + } + + + /** + * Stops a plugin by plugin name and repository. + * + * @param pluginName the name of the plugin to stop. + * @return Plugin the plugin, if stopped, or null if the + * the plugin was not found in the registry. + */ + public Plugin stopPlugin(final String pluginName) { + synchronized (pluginMap) { + Plugin plugin = (Plugin) pluginMap.get(pluginName); + + if (plugin == null) { + return null; + } + + // shutdown the plugin + plugin.shutdown(); + + // remove it from the plugin map + pluginMap.remove(pluginName); + firePluginStopped(plugin); + + // return it for future use + return plugin; + } + } + + /** + * Stops all plugins in the given logger repository. + */ + public void stopAllPlugins() { + synchronized (pluginMap) { + // remove the listener for this repository + loggerRepository.removeLoggerRepositoryEventListener(listener); + + Iterator iter = pluginMap.values().iterator(); + + while (iter.hasNext()) { + Plugin plugin = (Plugin) iter.next(); + plugin.shutdown(); + firePluginStopped(plugin); + } + } + } + + + /** + * Adds a PluginListener to this registry to be notified + * of PluginEvents. + * + * @param l PluginListener to add to this registry + */ + public void addPluginListener(final PluginListener l) { + listenerList.add(l); + } + + + /** + * Removes a particular PluginListener from this registry + * such that it will no longer be notified of PluginEvents. + * + * @param l PluginListener to remove + */ + public void removePluginListener(final PluginListener l) { + listenerList.remove(l); + } + + /** + * Internal class used to handle listener events from repositories. + */ + private class RepositoryListener implements LoggerRepositoryEventListener { + /** + * Stops all plugins associated with the repository being reset. + * + * @param repository the repository that was reset. + */ + public void configurationResetEvent(final LoggerRepository repository) { + PluginRegistry.this.stopAllPlugins(); + } + + + /** + * Called when the repository configuration is changed. + * + * @param repository the repository that was changed. + */ + public void configurationChangedEvent( + final LoggerRepository repository) { + // do nothing with this event + } + + + /** + * Stops all plugins associated with the repository being shutdown. + * + * @param repository the repository being shutdown. + */ + public void shutdownEvent(final LoggerRepository repository) { + PluginRegistry.this.stopAllPlugins(); + } + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/PluginSkeleton.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/plugins/PluginSkeleton.java b/src/main/java/org/apache/log4j/plugins/PluginSkeleton.java new file mode 100644 index 0000000..2660473 --- /dev/null +++ b/src/main/java/org/apache/log4j/plugins/PluginSkeleton.java @@ -0,0 +1,222 @@ +/* + * 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.plugins; + +import org.apache.log4j.spi.ComponentBase; +import org.apache.log4j.spi.LoggerRepository; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + + +/** + * A convienent abstract class for plugin subclasses that implements + * the basic methods of the Plugin interface. Subclasses are required + * to implement the isActive(), activateOptions(), and shutdown() + * methods. + * <p/> + * <p>Developers are not required to subclass PluginSkeleton to + * develop their own plugins (they are only required to implement the + * Plugin interface), but it provides a convenient base class to start + * from. + * <p/> + * Contributors: Nicko Cadell + * + * @author Mark Womack ([email protected]) + * @author Paul Smith ([email protected]) + */ +public abstract class PluginSkeleton extends ComponentBase implements Plugin { + /** + * Name of this plugin. + */ + protected String name = "plugin"; + + /** + * Active state of plugin. + */ + protected boolean active; + + /** + * This is a delegate that does all the PropertyChangeListener + * support. + */ + private PropertyChangeSupport propertySupport = + new PropertyChangeSupport(this); + + /** + * Construct new instance. + */ + protected PluginSkeleton() { + super(); + } + + /** + * Gets the name of the plugin. + * + * @return String the name of the plugin. + */ + public String getName() { + return name; + } + + /** + * Sets the name of the plugin and notifies + * PropertyChangeListeners of the change. + * + * @param newName the name of the plugin to set. + */ + public void setName(final String newName) { + String oldName = this.name; + this.name = newName; + propertySupport.firePropertyChange("name", oldName, this.name); + } + + /** + * Gets the logger repository for this plugin. + * + * @return LoggerRepository the logger repository this plugin will affect. + */ + public LoggerRepository getLoggerRepository() { + return repository; + } + + /** + * Sets the logger repository used by this plugin and notifies a + * relevant PropertyChangeListeners registered. This + * repository will be used by the plugin functionality. + * + * @param repository the logger repository that this plugin should affect. + */ + public void setLoggerRepository(final LoggerRepository repository) { + Object oldValue = this.repository; + this.repository = repository; + firePropertyChange("loggerRepository", oldValue, this.repository); + } + + /** + * Returns whether this plugin is Active or not. + * + * @return true/false + */ + public synchronized boolean isActive() { + return active; + } + + /** + * Returns true if the plugin has the same name and logger repository as the + * testPlugin passed in. + * + * @param testPlugin The plugin to test equivalency against. + * @return Returns true if testPlugin is considered to be equivalent. + */ + public boolean isEquivalent(final Plugin testPlugin) { + return (repository == testPlugin.getLoggerRepository()) + && ((this.name == null && testPlugin.getName() == null) + || (this.name != null + && name.equals(testPlugin.getName()))) + && this.getClass().equals(testPlugin.getClass()); + } + + /** + * Add property change listener. + * @param listener listener. + */ + public final void addPropertyChangeListener( + final PropertyChangeListener listener) { + propertySupport.addPropertyChangeListener(listener); + } + + /** + * Add property change listener for one property only. + * @param propertyName property name. + * @param listener listener. + */ + public final void addPropertyChangeListener( + final String propertyName, + final PropertyChangeListener listener) { + propertySupport.addPropertyChangeListener(propertyName, listener); + } + + /** + * Remove property change listener. + * @param listener listener. + */ + public final void removePropertyChangeListener( + final PropertyChangeListener listener) { + propertySupport.removePropertyChangeListener(listener); + } + + /** + * Remove property change listener on a specific property. + * @param propertyName property name. + * @param listener listener. + */ + public final void removePropertyChangeListener( + final String propertyName, + final PropertyChangeListener listener) { + propertySupport.removePropertyChangeListener(propertyName, listener); + } + + /** + * Fire a property change event to appropriate listeners. + * @param evt change event. + */ + protected final void firePropertyChange( + final PropertyChangeEvent evt) { + propertySupport.firePropertyChange(evt); + } + + /** + * Fire property change event to appropriate listeners. + * @param propertyName property name. + * @param oldValue old value. + * @param newValue new value. + */ + protected final void firePropertyChange( + final String propertyName, + final boolean oldValue, + final boolean newValue) { + propertySupport.firePropertyChange(propertyName, oldValue, newValue); + } + + /** + * Fire property change event to appropriate listeners. + * @param propertyName property name. + * @param oldValue old value. + * @param newValue new value. + */ + protected final void firePropertyChange( + final String propertyName, + final int oldValue, final int newValue) { + propertySupport.firePropertyChange(propertyName, oldValue, newValue); + } + + /** + * Fire property change event to appropriate listeners. + * @param propertyName property name. + * @param oldValue old value. + * @param newValue new value. + */ + protected final void firePropertyChange( + final String propertyName, + final Object oldValue, + final Object newValue) { + propertySupport.firePropertyChange(propertyName, oldValue, newValue); + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/Receiver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/plugins/Receiver.java b/src/main/java/org/apache/log4j/plugins/Receiver.java new file mode 100644 index 0000000..d78ae59 --- /dev/null +++ b/src/main/java/org/apache/log4j/plugins/Receiver.java @@ -0,0 +1,131 @@ +/* + * 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.plugins; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.log4j.spi.LoggingEvent; +import org.apache.log4j.spi.Thresholdable; + + +/** + * Defines the base class for Receiver plugins. + * <p/> + * <p>Just as Appenders send logging events outside of the log4j + * environment (to files, to smtp, to sockets, etc), Receivers bring + * logging events inside the log4j environment. + * <p/> + * <p>Receivers are meant to support the receiving of + * remote logging events from another process. For example, + * SocketAppender "appends" a logging event to a socket, configured + * for a specific host and port number. On the receiving side of + * the socket can be a SocketReceiver object. The SocketReceiver + * object receives the logging event, and then "posts" it to the + * log4j environment (LoggerRepository) on the receiving machine, to + * be handled by the configured appenders, etc. The various + * settings in this environment (Logger levels, Appender filters & + * thresholds) are applied to the received logging event. + * <p/> + * <p>Receivers can also be used to "import" log messages from other + * logging packages into the log4j environment. + * <p/> + * <p>Receivers can be configured to post events to a given + * LoggerRepository. + * <p/> + * <p>Subclasses of Receiver must implement the isActive(), + * activateOptions(), and shutdown() methods. The doPost() method + * is provided to standardize the "import" of remote events into + * the repository. + * + * @author Mark Womack + * @author Ceki Gülcü + * @author Paul Smith ([email protected]) + */ +public abstract class Receiver extends PluginSkeleton implements Thresholdable { + /** + * Threshold level. + */ + protected Level thresholdLevel; + + /** + * Create new instance. + */ + protected Receiver() { + super(); + } + + /** + * Sets the receiver theshold to the given level. + * + * @param level The threshold level events must equal or be greater + * than before further processing can be done. + */ + public void setThreshold(final Level level) { + Level oldValue = this.thresholdLevel; + thresholdLevel = level; + firePropertyChange("threshold", oldValue, this.thresholdLevel); + } + + /** + * Gets the current threshold setting of the receiver. + * + * @return Level The current threshold level of the receiver. + */ + public Level getThreshold() { + return thresholdLevel; + } + + /** + * Returns true if the given level is equals or greater than the current + * threshold value of the receiver. + * + * @param level The level to test against the receiver threshold. + * @return boolean True if level is equal or greater than the + * receiver threshold. + */ + public boolean isAsSevereAsThreshold(final Level level) { + return ((thresholdLevel == null) + || level.isGreaterOrEqual(thresholdLevel)); + } + + /** + * Posts the logging event to a logger in the configured logger + * repository. + * + * @param event the log event to post to the local log4j environment. + */ + public void doPost(final LoggingEvent event) { + // if event does not meet threshold, exit now + if (!isAsSevereAsThreshold(event.getLevel())) { + return; + } + + // get the "local" logger for this event from the + // configured repository. + Logger localLogger = + getLoggerRepository().getLogger(event.getLoggerName()); + + // if the logger level is greater or equal to the level + // of the event, use the logger to append the event. + if (event.getLevel() + .isGreaterOrEqual(localLogger.getEffectiveLevel())) { + // call the loggers appenders to process the event + localLogger.callAppenders(event); + } + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java b/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java new file mode 100644 index 0000000..4fca465 --- /dev/null +++ b/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java @@ -0,0 +1,85 @@ +/* + * 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.rewrite; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.apache.log4j.spi.LoggingEvent; + +/** + * This policy rewrites events where the message of the + * original event implementes java.util.Map. + * All other events are passed through unmodified. + * If the map contains a "message" entry, the value will be + * used as the message for the rewritten event. The rewritten + * event will have a property set that is the combination of the + * original property set and the other members of the message map. + * If both the original property set and the message map + * contain the same entry, the value from the message map + * will overwrite the original property set. + * + * The combination of the RewriteAppender and this policy + * performs the same actions as the MapFilter from log4j 1.3. + */ +public class MapRewritePolicy implements RewritePolicy { + /** + * {@inheritDoc} + */ + public LoggingEvent rewrite(final LoggingEvent source) { + Object msg = source.getMessage(); + if (msg instanceof Map) { + Map props = new HashMap(source.getProperties()); + Map eventProps = (Map) msg; + // + // if the map sent in the logging request + // has "message" entry, use that as the message body + // otherwise, use the entire map. + // + Object newMsg = eventProps.get("message"); + if (newMsg == null) { + newMsg = msg; + } + + for(Iterator iter = eventProps.entrySet().iterator(); + iter.hasNext(); + ) { + Map.Entry entry = (Map.Entry) iter.next(); + if (!("message".equals(entry.getKey()))) { + props.put(entry.getKey(), entry.getValue()); + } + } + + return new LoggingEvent( + source.getFQNOfLoggerClass(), + source.getLogger() != null ? source.getLogger(): Logger.getLogger(source.getLoggerName()), + source.getTimeStamp(), + source.getLevel(), + newMsg, + source.getThreadName(), + source.getThrowableInformation(), + source.getNDC(), + source.getLocationInformation(), + props); + } else { + return source; + } + + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java b/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java new file mode 100644 index 0000000..535736c --- /dev/null +++ b/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java @@ -0,0 +1,93 @@ +/* + * 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.rewrite; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.StringTokenizer; + +import org.apache.log4j.Logger; +import org.apache.log4j.spi.LoggingEvent; + +/** + * This policy rewrites events by adding + * a user-specified list of properties to the event. + * Existing properties are not modified. + * + * The combination of the RewriteAppender and this policy + * performs the same actions as the PropertyFilter from log4j 1.3. + */ + +public class PropertyRewritePolicy implements RewritePolicy { + private Map properties = Collections.EMPTY_MAP; + public PropertyRewritePolicy() { + } + + /** + * Set a string representing the property name/value pairs. + * + * Form: propname1=propvalue1,propname2=propvalue2 + * + * @param props + */ + public void setProperties(String props) { + Map hashTable = new HashMap(); + StringTokenizer pairs = new StringTokenizer(props, ","); + while (pairs.hasMoreTokens()) { + StringTokenizer entry = new StringTokenizer(pairs.nextToken(), "="); + hashTable.put(entry.nextElement().toString().trim(), entry.nextElement().toString().trim()); + } + synchronized(this) { + properties = hashTable; + } + } + + /** + * {@inheritDoc} + */ + public LoggingEvent rewrite(final LoggingEvent source) { + if (!properties.isEmpty()) { + Map rewriteProps = new HashMap(source.getProperties()); + for(Iterator iter = properties.entrySet().iterator(); + iter.hasNext(); + ) { + Map.Entry entry = (Map.Entry) iter.next(); + if (!rewriteProps.containsKey(entry.getKey())) { + rewriteProps.put(entry.getKey(), entry.getValue()); + } + } + + return new LoggingEvent( + source.getFQNOfLoggerClass(), + source.getLogger() != null ? source.getLogger(): Logger.getLogger(source.getLoggerName()), + source.getTimeStamp(), + source.getLevel(), + source.getMessage(), + source.getThreadName(), + source.getThrowableInformation(), + source.getNDC(), + source.getLocationInformation(), + rewriteProps); + } + return source; + } + + + +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/rewrite/ReflectionRewritePolicy.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/rewrite/ReflectionRewritePolicy.java b/src/main/java/org/apache/log4j/rewrite/ReflectionRewritePolicy.java new file mode 100644 index 0000000..f1a4cc5 --- /dev/null +++ b/src/main/java/org/apache/log4j/rewrite/ReflectionRewritePolicy.java @@ -0,0 +1,89 @@ +/* + * 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.rewrite; + +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.apache.log4j.helpers.LogLog; +import org.apache.log4j.spi.LoggingEvent; + +/** + * This policy rewrites events by evaluating any + * JavaBean properties on the message object and adding them + * to the event properties. If the message object has a + * message property, the value of that property will be + * used as the message for the rewritten event and will + * not be added to the event properties. Values from the + * JavaBean properties will replace any existing property + * with the same name. + * + * The combination of the RewriteAppender and this policy + * performs the same actions as the ReflectionFilter from log4j 1.3. + */ +public class ReflectionRewritePolicy implements RewritePolicy { + /** + * {@inheritDoc} + */ + public LoggingEvent rewrite(final LoggingEvent source) { + Object msg = source.getMessage(); + if (!(msg instanceof String)) { + Object newMsg = msg; + Map rewriteProps = new HashMap(source.getProperties()); + + try { + PropertyDescriptor[] props = Introspector.getBeanInfo( + msg.getClass(), Object.class).getPropertyDescriptors(); + if (props.length > 0) { + for (int i=0;i<props.length;i++) { + try { + Object propertyValue = + props[i].getReadMethod().invoke(msg, + (Object[]) null); + if ("message".equalsIgnoreCase(props[i].getName())) { + newMsg = propertyValue; + } else { + rewriteProps.put(props[i].getName(), propertyValue); + } + } catch (Exception e) { + LogLog.warn("Unable to evaluate property " + + props[i].getName(), e); + } + } + return new LoggingEvent( + source.getFQNOfLoggerClass(), + source.getLogger() != null ? source.getLogger(): Logger.getLogger(source.getLoggerName()), + source.getTimeStamp(), + source.getLevel(), + newMsg, + source.getThreadName(), + source.getThrowableInformation(), + source.getNDC(), + source.getLocationInformation(), + rewriteProps); + } + } catch (Exception e) { + LogLog.warn("Unable to get property descriptors", e); + } + + } + return source; + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/rewrite/RewriteAppender.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/rewrite/RewriteAppender.java b/src/main/java/org/apache/log4j/rewrite/RewriteAppender.java new file mode 100644 index 0000000..368ecf9 --- /dev/null +++ b/src/main/java/org/apache/log4j/rewrite/RewriteAppender.java @@ -0,0 +1,199 @@ +/* + * 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.rewrite; + +import org.apache.log4j.Appender; +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.helpers.AppenderAttachableImpl; +import org.apache.log4j.spi.AppenderAttachable; +import org.apache.log4j.spi.LoggingEvent; +import org.apache.log4j.spi.OptionHandler; +import org.apache.log4j.xml.UnrecognizedElementHandler; +import org.w3c.dom.Element; + +import java.util.Enumeration; +import java.util.Properties; + +/** + * This appender forwards a logging request to another + * appender after possibly rewriting the logging event. + * + * This appender (with the appropriate policy) + * replaces the MapFilter, PropertyFilter and ReflectionFilter + * from log4j 1.3. + */ +public class RewriteAppender extends AppenderSkeleton + implements AppenderAttachable, UnrecognizedElementHandler { + /** + * Rewrite policy. + */ + private RewritePolicy policy; + /** + * Nested appenders. + */ + private final AppenderAttachableImpl appenders; + + public RewriteAppender() { + appenders = new AppenderAttachableImpl(); + } + + /** + * {@inheritDoc} + */ + protected void append(final LoggingEvent event) { + LoggingEvent rewritten = event; + if (policy != null) { + rewritten = policy.rewrite(event); + } + if (rewritten != null) { + synchronized (appenders) { + appenders.appendLoopOnAppenders(rewritten); + } + } + } + + /** + * Add appender. + * + * @param newAppender appender to add, may not be null. + */ + public void addAppender(final Appender newAppender) { + synchronized (appenders) { + appenders.addAppender(newAppender); + } + } + + /** + * Get iterator over attached appenders. + * @return iterator or null if no attached appenders. + */ + public Enumeration getAllAppenders() { + synchronized (appenders) { + return appenders.getAllAppenders(); + } + } + + /** + * Get appender by name. + * + * @param name name, may not be null. + * @return matching appender or null. + */ + public Appender getAppender(final String name) { + synchronized (appenders) { + return appenders.getAppender(name); + } + } + + + /** + * Close this <code>AsyncAppender</code> by interrupting the dispatcher + * thread which will process all pending events before exiting. + */ + public void close() { + closed = true; + // + // close all attached appenders. + // + synchronized (appenders) { + Enumeration iter = appenders.getAllAppenders(); + + if (iter != null) { + while (iter.hasMoreElements()) { + Object next = iter.nextElement(); + + if (next instanceof Appender) { + ((Appender) next).close(); + } + } + } + } + } + + /** + * Determines if specified appender is attached. + * @param appender appender. + * @return true if attached. + */ + public boolean isAttached(final Appender appender) { + synchronized (appenders) { + return appenders.isAttached(appender); + } + } + + /** + * {@inheritDoc} + */ + public boolean requiresLayout() { + return false; + } + + /** + * Removes and closes all attached appenders. + */ + public void removeAllAppenders() { + synchronized (appenders) { + appenders.removeAllAppenders(); + } + } + + /** + * Removes an appender. + * @param appender appender to remove. + */ + public void removeAppender(final Appender appender) { + synchronized (appenders) { + appenders.removeAppender(appender); + } + } + + /** + * Remove appender by name. + * @param name name. + */ + public void removeAppender(final String name) { + synchronized (appenders) { + appenders.removeAppender(name); + } + } + + + public void setRewritePolicy(final RewritePolicy rewritePolicy) { + policy = rewritePolicy; + } + /** + * {@inheritDoc} + */ + public boolean parseUnrecognizedElement(final Element element, + final Properties props) throws Exception { + final String nodeName = element.getNodeName(); + if ("rewritePolicy".equals(nodeName)) { + Object rewritePolicy = + org.apache.log4j.xml.DOMConfigurator.parseElement( + element, props, RewritePolicy.class); + if (rewritePolicy != null) { + if (rewritePolicy instanceof OptionHandler) { + ((OptionHandler) rewritePolicy).activateOptions(); + } + this.setRewritePolicy((RewritePolicy) rewritePolicy); + } + return true; + } + return false; + } + +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java b/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java new file mode 100644 index 0000000..bb40507 --- /dev/null +++ b/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java @@ -0,0 +1,37 @@ +package org.apache.log4j.rewrite; + +import org.apache.log4j.spi.LoggingEvent; + +/* +* 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. +*/ + +/** + * This interface is implemented to provide a rewrite + * strategy for RewriteAppender. RewriteAppender will + * call the rewrite method with a source logging event. + * The strategy may return that event, create a new event + * or return null to suppress the logging request. + */ +public interface RewritePolicy { + /** + * Rewrite a logging event. + * @param source a logging event that may be returned or + * used to create a new logging event. + * @return a logging event or null to suppress processing. + */ + LoggingEvent rewrite(final LoggingEvent source); +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/scheduler/Job.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/scheduler/Job.java b/src/main/java/org/apache/log4j/scheduler/Job.java new file mode 100644 index 0000000..a0e9be4 --- /dev/null +++ b/src/main/java/org/apache/log4j/scheduler/Job.java @@ -0,0 +1,36 @@ +/* + * 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.scheduler; + + +/** + * Job is a very simple interface. It only has a single method {@link #execute} + * which is called by the {@link Scheduler} when a task is ready for execution. + * <p/> + * It is assumed that the execution context + * are contained within the implementing + * {@link Job} itself. + * + * @author Ceki Gülcü + */ +public interface Job { + /** + * Execute job. + */ + void execute(); +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/scheduler/Scheduler.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/scheduler/Scheduler.java b/src/main/java/org/apache/log4j/scheduler/Scheduler.java new file mode 100644 index 0000000..72809f7 --- /dev/null +++ b/src/main/java/org/apache/log4j/scheduler/Scheduler.java @@ -0,0 +1,307 @@ +/* + * 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.scheduler; + +import java.util.List; +import java.util.Vector; + +/** + * A simple but still useful implementation of a Scheduler (in memory only). + * <p/> + * This implementation will work very well when the number of scheduled job is + * small, say less than 100 jobs. If a larger number of events need to be + * scheduled, than a better adapted data structure for the jobList can give + * improved performance. + * + * @author Ceki + */ +public class Scheduler extends Thread { + + /** + * Job list. + */ + List jobList; + /** + * If set true, scheduler has or should shut down. + */ + boolean shutdown = false; + + /** + * Create new instance. + */ + public Scheduler() { + super(); + jobList = new Vector(); + } + + /** + * Find the index of a given job. + * @param job job + * @return -1 if the job could not be found. + */ + int findIndex(final Job job) { + int size = jobList.size(); + boolean found = false; + + int i = 0; + for (; i < size; i++) { + ScheduledJobEntry se = (ScheduledJobEntry) jobList.get(i); + if (se.job == job) { + found = true; + break; + } + } + if (found) { + return i; + } else { + return -1; + } + } + + /** + * Delete the given job. + * @param job job. + * @return true if the job could be deleted, and + * false if the job could not be found or if the Scheduler is about to + * shutdown in which case deletions are not permitted. + */ + public synchronized boolean delete(final Job job) { + // if already shutdown in the process of shutdown, there is no + // need to remove Jobs as they will never be executed. + if (shutdown) { + return false; + } + int i = findIndex(job); + if (i != -1) { + ScheduledJobEntry se = (ScheduledJobEntry) jobList.remove(i); + if (se.job != job) { // this should never happen + new IllegalStateException("Internal programming error"); + } + // if the job is the first on the list, + // then notify the scheduler thread to schedule a new job + if (i == 0) { + this.notifyAll(); + } + return true; + } else { + return false; + } + } + + + /** + * Schedule a {@link Job} for execution at system time given by + * the <code>desiredTime</code> parameter. + * @param job job to schedule. + * @param desiredTime desired time of execution. + */ + public synchronized void schedule(final Job job, + final long desiredTime) { + schedule(new ScheduledJobEntry(job, desiredTime)); + } + + /** + * Schedule a {@link Job} for execution at system time given by + * the <code>desiredTime</code> parameter. + * <p/> + * The job will be rescheduled. It will execute with a frequency determined + * by the period parameter. + * @param job job to schedule. + * @param desiredTime desired time of execution. + * @param period repeat period. + */ + public synchronized void schedule(final Job job, + final long desiredTime, + final long period) { + schedule(new ScheduledJobEntry(job, desiredTime, period)); + } + + /** + * Change the period of a job. The original job must exist for its period + * to be changed. + * <p/> + * The method returns true if the period could be changed, and false + * otherwise. + * @param job job. + * @param newPeriod new repeat period. + * @return true if period could be changed. + */ + public synchronized boolean changePeriod(final Job job, + final long newPeriod) { + if (newPeriod <= 0) { + throw new IllegalArgumentException( + "Period must be an integer langer than zero"); + } + + int i = findIndex(job); + if (i == -1) { + return false; + } else { + ScheduledJobEntry se = (ScheduledJobEntry) jobList.get(i); + se.period = newPeriod; + return true; + } + } + + /** + * Schedule a job. + * @param newSJE new job entry. + */ + private synchronized void schedule(final ScheduledJobEntry newSJE) { + // disallow new jobs after shutdown + if (shutdown) { + return; + } + int max = jobList.size(); + long desiredExecutionTime = newSJE.desiredExecutionTime; + + // find the index i such that timeInMillis < jobList[i] + int i = 0; + for (; i < max; i++) { + + ScheduledJobEntry sje = (ScheduledJobEntry) jobList.get(i); + + if (desiredExecutionTime < sje.desiredExecutionTime) { + break; + } + } + jobList.add(i, newSJE); + // if the jobList was empty, then notify the scheduler thread + if (i == 0) { + this.notifyAll(); + } + } + + /** + * Shut down scheduler. + */ + public synchronized void shutdown() { + shutdown = true; + } + + /** + * Run scheduler. + */ + public synchronized void run() { + while (!shutdown) { + if (jobList.isEmpty()) { + linger(); + } else { + ScheduledJobEntry sje = (ScheduledJobEntry) jobList.get(0); + long now = System.currentTimeMillis(); + if (now >= sje.desiredExecutionTime) { + executeInABox(sje.job); + jobList.remove(0); + if (sje.period > 0) { + sje.desiredExecutionTime = now + sje.period; + schedule(sje); + } + } else { + linger(sje.desiredExecutionTime - now); + } + } + } + // clear out the job list to facilitate garbage collection + jobList.clear(); + jobList = null; + System.out.println("Leaving scheduler run method"); + } + + /** + * We do not want a single failure to affect the whole scheduler. + * @param job job to execute. + */ + void executeInABox(final Job job) { + try { + job.execute(); + } catch (Exception e) { + System.err.println("The execution of the job threw an exception"); + e.printStackTrace(System.err); + } + } + + /** + * Wait for notification. + */ + void linger() { + try { + while (jobList.isEmpty() && !shutdown) { + this.wait(); + } + } catch (InterruptedException ie) { + shutdown = true; + } + } + + /** + * Wait for notification or time to elapse. + * @param timeToLinger time to linger. + */ + void linger(final long timeToLinger) { + try { + this.wait(timeToLinger); + } catch (InterruptedException ie) { + shutdown = true; + } + } + + /** + * Represents an entry in job scheduler. + */ + static final class ScheduledJobEntry { + /** + * Desired execution time. + */ + long desiredExecutionTime; + /** + * Job to run. + */ + Job job; + /** + * Repeat period. + */ + long period = 0; + + /** + * Create new instance. + * @param job job + * @param desiredTime desired time. + */ + ScheduledJobEntry(final Job job, final long desiredTime) { + this(job, desiredTime, 0); + } + + /** + * Create new instance. + * @param job job + * @param desiredTime desired time + * @param period repeat period + */ + ScheduledJobEntry(final Job job, + final long desiredTime, + final long period) { + super(); + this.desiredExecutionTime = desiredTime; + this.job = job; + this.period = period; + } + } + +} + + http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/Component.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/spi/Component.java b/src/main/java/org/apache/log4j/spi/Component.java new file mode 100644 index 0000000..42ef29a --- /dev/null +++ b/src/main/java/org/apache/log4j/spi/Component.java @@ -0,0 +1,37 @@ +/* + * 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.spi; + + +/** + * A common interface shared by log4j components. + * + * @author Ceki Gulcu + */ +public interface Component { + + + /** + * Set owning logger repository for this component. This operation can + * only be performed once. + * Once set, a subsequent attempt will throw an IllegalStateException. + * + * @param repository The repository where this appender is attached. + */ + void setLoggerRepository(LoggerRepository repository); + +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/ComponentBase.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/spi/ComponentBase.java b/src/main/java/org/apache/log4j/spi/ComponentBase.java new file mode 100644 index 0000000..78932f7 --- /dev/null +++ b/src/main/java/org/apache/log4j/spi/ComponentBase.java @@ -0,0 +1,126 @@ +/* + * 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.spi; + +import org.apache.log4j.ULogger; +import org.apache.log4j.Logger; + + +/** + * Most log4j components derive from this class. + * + * @author Ceki Gulcu + */ +public class ComponentBase implements Component { + + /** + * Error count limit. + */ + private static final int ERROR_COUNT_LIMIT = 3; + + /** + * Logger repository. + */ + protected LoggerRepository repository; + /** + * Logger. + */ + private ULogger logger; + /** + * Error count. + */ + private int errorCount = 0; + + /** + * Construct a new instance. + */ + protected ComponentBase() { + super(); + } + + + /** + * Called by derived classes when they deem that the component has recovered + * from an erroneous state. + */ + protected void resetErrorCount() { + errorCount = 0; + } + + /** + * Set the owning repository. The owning repository cannot be set more than + * once. + * + * @param repository repository + */ + public void setLoggerRepository(final LoggerRepository repository) { + if (this.repository == null) { + this.repository = repository; + } else if (this.repository != repository) { + throw new IllegalStateException("Repository has been already set"); + } + } + + /** + * Return the LoggerRepository to which this component is attached. + * + * @return Owning LoggerRepository + */ + protected LoggerRepository getLoggerRepository() { + return repository; + } + + /** + * Return an instance specific logger to be used by the component itself. + * This logger is not intended to be accessed by the end-user, hence the + * protected keyword. + * <p/> + * <p>In case the repository for this component is not set, + * this implementations returns a {@link SimpleULogger} instance. + * + * @return A ULogger instance. + */ + protected ULogger getLogger() { + if (logger == null) { + if (repository != null) { + Logger l = repository.getLogger(this.getClass().getName()); + if (l instanceof ULogger) { + logger = (ULogger) l; + } else { + logger = new Log4JULogger(l); + } + } else { + logger = SimpleULogger.getLogger(this.getClass().getName()); + } + } + return logger; + } + + /** + * Frequently called methods in log4j components can invoke this method in + * order to avoid flooding the output when logging lasting error conditions. + * + * @return a regular logger, or a NOPLogger if called too frequently. + */ + protected ULogger getNonFloodingLogger() { + if (errorCount++ >= ERROR_COUNT_LIMIT) { + return NOPULogger.NOP_LOGGER; + } else { + return getLogger(); + } + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/Decoder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/spi/Decoder.java b/src/main/java/org/apache/log4j/spi/Decoder.java new file mode 100644 index 0000000..d4686ad --- /dev/null +++ b/src/main/java/org/apache/log4j/spi/Decoder.java @@ -0,0 +1,63 @@ +/* + * 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.spi; + + +import java.io.IOException; + +import java.net.URL; + +import java.util.Map; +import java.util.Vector; + + +/** + * Allow LoggingEvents to be reconstructed from a different format + * (usually XML). + * + * @author Scott Deboy ([email protected]) + */ +public interface Decoder { + /** + * Decode events from document. + * @param document document to decode. + * @return list of LoggingEvent instances. + */ + Vector decodeEvents(String document); + + /** + * Decode event from string. + * @param event string representation of event + * @return event + */ + LoggingEvent decode(String event); + + /** + * Decode event from document retreived from URL. + * @param url url of document + * @return list of LoggingEvent instances. + * @throws IOException if IO error resolving document. + */ + Vector decode(URL url) throws IOException; + + /** + * Sets additional properties. + * @param additionalProperties map of additional properties. + */ + void setAdditionalProperties(Map additionalProperties); +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/ErrorItem.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/spi/ErrorItem.java b/src/main/java/org/apache/log4j/spi/ErrorItem.java new file mode 100644 index 0000000..f6f3686 --- /dev/null +++ b/src/main/java/org/apache/log4j/spi/ErrorItem.java @@ -0,0 +1,172 @@ +/* + * 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.spi; + +import java.io.PrintStream; + +/** + * Used to store special log4j errors which cannot be logged using internal + * logging. Such errors include those occurring during the initial phases + * of log4j configuration or errors emanating from core components such as + * Logger or Hierarchy. + * + * @author Ceki Gulcu + */ +public class ErrorItem { + /** + * Message. + */ + String message; + /** + * Column. + */ + int colNumber = -1; + /** + * Line number. + */ + int lineNumber = -1; + /** + * Exception. + */ + Throwable exception; + + /** + * Create new instance. + * @param message message + * @param e exception + */ + public ErrorItem(final String message, final Exception e) { + super(); + this.message = message; + exception = e; + } + + /** + * Creaet new instance. + * @param message message. + */ + public ErrorItem(final String message) { + this(message, null); + } + + /** + * Get column number. + * @return column number. + */ + public int getColNumber() { + return colNumber; + } + + /** + * Set column number. + * @param colNumber new column number. + */ + public void setColNumber(int colNumber) { + this.colNumber = colNumber; + } + + /** + * Get exception. + * @return exception. + */ + public Throwable getException() { + return exception; + } + + /** + * Set exception. + * @param exception exception + */ + public void setException(final Throwable exception) { + this.exception = exception; + } + + /** + * Get line number. + * @return line number. + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * Set line number. + * @param lineNumber line number. + */ + public void setLineNumber(final int lineNumber) { + this.lineNumber = lineNumber; + } + + /** + * Get message. + * @return message. + */ + public String getMessage() { + return message; + } + + /** + * Set message. + * @param message message. + */ + public void setMessage(final String message) { + this.message = message; + } + + /** + * String representation of ErrorItem. + * @return string. + */ + public String toString() { + String str = + "Reported error: \"" + message + "\""; + + if (lineNumber != -1) { + str += " at line " + lineNumber + " column " + colNumber; + } + if (exception != null) { + str += (" with exception " + exception); + } + return str; + } + + /** + * Dump the details of this ErrorItem to System.out. + */ + public void dump() { + dump(System.out); + } + + /** + * Dump the details of this ErrorItem on the specified {@link PrintStream}. + * @param ps print stream. + */ + public void dump(final PrintStream ps) { + String str = + "Reported error: \"" + message + "\""; + + if (lineNumber != -1) { + str += " at line " + lineNumber + " column " + colNumber; + } + ps.println(str); + + if (exception != null) { + exception.printStackTrace(ps); + } + } +}
