Return-Path: X-Original-To: apmail-logging-commits-archive@minotaur.apache.org Delivered-To: apmail-logging-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 49F10D688 for ; Mon, 17 Sep 2012 05:39:28 +0000 (UTC) Received: (qmail 16143 invoked by uid 500); 17 Sep 2012 05:39:28 -0000 Delivered-To: apmail-logging-commits-archive@logging.apache.org Received: (qmail 16045 invoked by uid 500); 17 Sep 2012 05:39:25 -0000 Mailing-List: contact commits-help@logging.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@logging.apache.org Delivered-To: mailing list commits@logging.apache.org Received: (qmail 15921 invoked by uid 99); 17 Sep 2012 05:39:24 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 17 Sep 2012 05:39:24 +0000 X-ASF-Spam-Status: No, hits=-1998.6 required=5.0 tests=ALL_TRUSTED,FILL_THIS_FORM_FRAUD_PHISH,FILL_THIS_FORM_SHORT X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 17 Sep 2012 05:39:22 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 1E49623889E2; Mon, 17 Sep 2012 05:38:39 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1386479 - in /logging/log4j/log4j2/trunk/core/src: main/java/org/apache/logging/log4j/core/appender/ main/java/org/apache/logging/log4j/core/config/ main/java/org/apache/logging/log4j/core/impl/ test/java/org/apache/logging/log4j/core/appe... Date: Mon, 17 Sep 2012 05:38:38 -0000 To: commits@logging.apache.org From: rgoers@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120917053839.1E49623889E2@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: rgoers Date: Mon Sep 17 05:38:38 2012 New Revision: 1386479 URL: http://svn.apache.org/viewvc?rev=1386479&view=rev Log: Add AsynchAppender Added: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/AsynchAppender.java - copied, changed from r1385344, logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/JMSQueueAppender.java logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/AsynchAppenderTest.java - copied, changed from r1385344, logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/RewriteAppenderTest.java logging/log4j/log4j2/trunk/core/src/test/resources/log4j-asynch.xml - copied, changed from r1385344, logging/log4j/log4j2/trunk/core/src/test/resources/log4j-rewrite.xml Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/config/BaseConfiguration.java logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java Copied: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/AsynchAppender.java (from r1385344, logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/JMSQueueAppender.java) URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/AsynchAppender.java?p2=logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/AsynchAppender.java&p1=logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/JMSQueueAppender.java&r1=1385344&r2=1386479&rev=1386479&view=diff ============================================================================== --- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/JMSQueueAppender.java (original) +++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/AsynchAppender.java Mon Sep 17 05:38:38 2012 @@ -16,28 +16,93 @@ */ package org.apache.logging.log4j.core.appender; +import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Filter; -import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.config.AppenderControl; +import org.apache.logging.log4j.core.config.AppenderRef; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.ConfigurationException; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginAttr; +import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; -import org.apache.logging.log4j.core.layout.SerializedLayout; -import org.apache.logging.log4j.core.net.JMSQueueManager; +import org.apache.logging.log4j.core.impl.Log4jLogEvent; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; /** - * Appender to write to a JMS Queue. + * Appender to write to one or more Appenders asynchronously. The AsynchAppender can be configrued with one + * or more Appenders and an Appender to write to if the queue is full. The AsynchAppender does not allow + * filter to be specified on the Appender references. */ -@Plugin(name = "JMSQueue", type = "Core", elementType = "appender", printObject = true) -public final class JMSQueueAppender extends AppenderBase { +@Plugin(name = "Asynch", type = "Core", elementType = "appender", printObject = true) +public final class AsynchAppender extends AppenderBase { + + private final BlockingQueue queue; + private final boolean blocking; + private final Configuration config; + private final AppenderRef[] appenderRefs; + private final String errorRef; + private AppenderControl errorAppender = null; + private AsynchThread thread = null; + + private static final int DEFAULT_QUEUE_SIZE = 128; + + private AsynchAppender(String name, Filter filter, AppenderRef[] appenderRefs, String errorRef, + int queueSize, boolean blocking, + boolean handleExceptions, Configuration config) { + super(name, filter, null, handleExceptions); + this.queue = new ArrayBlockingQueue(queueSize); + this.blocking = blocking; + this.config = config; + this.appenderRefs = appenderRefs; + this.errorRef = errorRef; + } + + @Override + public void start() { + Map map = config.getAppenders(); + List appenders = new ArrayList(); + for (AppenderRef appenderRef : appenderRefs) { + if (map.containsKey(appenderRef.getRef())) { + appenders.add(new AppenderControl(map.get(appenderRef.getRef()), null, null)); + } else { + LOGGER.error("No appender named {} was configured", appenderRef); + } + } + if (errorRef != null) { + if (map.containsKey(errorRef)) { + errorAppender = new AppenderControl(map.get(errorRef), null, null); + } else { + LOGGER.error("Unable to set up error Appender. No appender named {} was configured", errorRef); + } + } + if (appenders.size() > 0 ) { + thread = new AsynchThread(appenders, queue); + } else if (errorRef == null) { + throw new ConfigurationException("No appenders are available for AsynchAppender " + getName()); + } - private final JMSQueueManager manager; + thread.start(); + super.start(); + } - private JMSQueueAppender(String name, Filter filter, Layout layout, JMSQueueManager manager, - boolean handleExceptions) { - super(name, filter, layout, handleExceptions); - this.manager = manager; + @Override + public void stop() { + super.stop(); + thread.shutdown(); + try { + thread.join(); + } catch (InterruptedException ex) { + LOGGER.warn("Interrupted while stopping AsynchAppender {}", getName()); + } } /** @@ -46,55 +111,103 @@ public final class JMSQueueAppender exte * @param event The LogEvent. */ public void append(LogEvent event) { - try { - manager.send(getLayout().formatAs(event)); - } catch (Exception ex) { - throw new AppenderRuntimeException(ex); + if (!isStarted()) { + throw new IllegalStateException("AsynchAppender " + getName() + " is not active"); + } + if (event instanceof Log4jLogEvent) { + if (blocking && queue.remainingCapacity() > 0) { + try { + queue.add(Log4jLogEvent.serialize((Log4jLogEvent) event)); + return; + } catch (IllegalStateException ex) { + error("Appender " + getName() + " is unable to write primary appenders. queue is full"); + } + } + if (errorAppender != null) { + if (!blocking) { + error("Appender " + getName() + " is unable to write primary appenders. queue is full"); + } + errorAppender.callAppender(event); + } } } /** - * Create a JMSQueueAppender. - * @param factoryName The fully qualified class name of the InitialContextFactory. - * @param providerURL The URL of the provider to use. - * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that - * will create a URL context factory - * @param securityPrincipalName The name of the identity of the Principal. - * @param securityCredentials The security credentials of the Principal. - * @param factoryBindingName The name to locate in the Context that provides the QueueConnectionFactory. - * @param queueBindingName The name to use to locate the Queue. - * @param userName The userid to use to create the Queue Connection. - * @param password The password to use to create the Queue Connection. - * @param layout The layout to use (defaults to SerlializedLayout). + * Create an AsynchAppender. + * @param appenderRefs The Appenders to reference. + * @param errorRef An optional Appender to write to if the queue is full or other errors occur. + * @param blocking True if the Appender should wait when the queue is full. The default is true. + * @param size The size of the event queue. The default is 128. + * @param name The name of the Appender. * @param filter The Filter or null. + * @param config The Configuration. * @param suppress "true" if exceptions should be hidden from the application, "false" otherwise. * The default is "true". - * @return The JMSQueueAppender. + * @return The AsynchAppender. */ @PluginFactory - public static JMSQueueAppender createAppender(@PluginAttr("factoryName") String factoryName, - @PluginAttr("providerURL") String providerURL, - @PluginAttr("urlPkgPrefixes") String urlPkgPrefixes, - @PluginAttr("securityPrincipalName") String securityPrincipalName, - @PluginAttr("securityCredentials") String securityCredentials, - @PluginAttr("factoryBindingName") String factoryBindingName, - @PluginAttr("queueBindingName") String queueBindingName, - @PluginAttr("userName") String userName, - @PluginAttr("password") String password, - @PluginElement("layout") Layout layout, - @PluginElement("filter") Filter filter, - @PluginAttr("suppressExceptions") String suppress) { + public static AsynchAppender createAppender(@PluginElement("appender-ref") AppenderRef[] appenderRefs, + @PluginAttr("error-ref") String errorRef, + @PluginAttr("blocking") String blocking, + @PluginAttr("bufferSize") String size, + @PluginAttr("name") String name, + @PluginElement("filter") Filter filter, + @PluginConfiguration Configuration config, + @PluginAttr("suppressExceptions") String suppress) { + if (name == null) { + LOGGER.error("No name provided for AsynchAppender"); + return null; + } + if (appenderRefs == null) { + LOGGER.error("No appender references provided to AsynchAppender {}", name); + } + + boolean isBlocking = blocking == null ? true : Boolean.valueOf(blocking); + int queueSize = size == null ? DEFAULT_QUEUE_SIZE : Integer.parseInt(size); - String name = "JMSQueue" + factoryBindingName + "." + queueBindingName; boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress); - JMSQueueManager manager = JMSQueueManager.getJMSQueueManager(factoryName, providerURL, urlPkgPrefixes, - securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password); - if (manager == null) { - return null; + + return new AsynchAppender(name, filter, appenderRefs, errorRef, queueSize, isBlocking, handleExceptions, config); + } + + private class AsynchThread extends Thread { + + private volatile boolean shutdown = false; + private final List appenders; + private final BlockingQueue queue; + + public AsynchThread(List appenders, BlockingQueue queue) { + this.appenders = appenders; + this.queue = queue; } - if (layout == null) { - layout = SerializedLayout.createLayout(); + + public void run() { + while (!shutdown) { + try { + Log4jLogEvent event = Log4jLogEvent.deserialize(queue.take()); + for (AppenderControl control : appenders) { + control.callAppender(event); + } + } catch (InterruptedException ex) { + // May have been interrupted to shut down. + } + } + // Process any remaining items in the queue. + while (!queue.isEmpty()) { + try { + Log4jLogEvent event = Log4jLogEvent.deserialize(queue.take()); + for (AppenderControl control : appenders) { + control.callAppender(event); + } + } catch (InterruptedException ex) { + // May have been interrupted to shut down. + } + } + } + + public void shutdown() { + shutdown = true; + this.interrupt(); } - return new JMSQueueAppender(name, filter, layout, manager, handleExceptions); } } Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/config/BaseConfiguration.java URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/config/BaseConfiguration.java?rev=1386479&r1=1386478&r2=1386479&view=diff ============================================================================== --- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/config/BaseConfiguration.java (original) +++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/config/BaseConfiguration.java Mon Sep 17 05:38:38 2012 @@ -126,8 +126,10 @@ public class BaseConfiguration extends F logger.clearAppenders(); logger.stopFilter(); } - for (Appender appender : appenders.values()) { - appender.stop(); + // Stop the appenders in reverse order in case they still have activity. + Appender[] array = appenders.values().toArray(new Appender[appenders.size()]); + for (int i = array.length - 1; i > 0; --i) { + array[i].stop(); } stopFilter(); } Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java?rev=1386479&r1=1386478&r2=1386479&view=diff ============================================================================== --- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java (original) +++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java Mon Sep 17 05:38:38 2012 @@ -223,6 +223,22 @@ public class Log4jLogEvent implements Lo return new LogEventProxy(this); } + public static Serializable serialize(Log4jLogEvent event) { + return new LogEventProxy(event); + } + + public static Log4jLogEvent deserialize(Serializable event) { + if (event == null) { + throw new NullPointerException("Event cannot be null"); + } + if (event instanceof LogEventProxy) { + LogEventProxy proxy = (LogEventProxy) event; + return new Log4jLogEvent(proxy.name, proxy.marker, proxy.fqcnOfLogger, proxy.level, proxy.message, + proxy.throwable, proxy.mdc, proxy.ndc, proxy.threadName, proxy.location, proxy.timestamp); + } + throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString()); + } + private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("Proxy required"); } Copied: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/AsynchAppenderTest.java (from r1385344, logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/RewriteAppenderTest.java) URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/AsynchAppenderTest.java?p2=logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/AsynchAppenderTest.java&p1=logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/RewriteAppenderTest.java&r1=1385344&r2=1386479&rev=1386479&view=diff ============================================================================== --- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/RewriteAppenderTest.java (original) +++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/AsynchAppenderTest.java Mon Sep 17 05:38:38 2012 @@ -14,20 +14,16 @@ * See the license for the specific language governing permissions and * limitations under the license. */ -package org.apache.logging.log4j.core.appender.rewrite; +package org.apache.logging.log4j.core.appender; -import org.apache.logging.log4j.EventLogger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.Appender; -import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.LoggerContext; -import org.apache.logging.log4j.test.appender.ListAppender; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.XMLConfigurationFactory; -import org.apache.logging.log4j.message.MapMessage; -import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.message.StructuredDataMessage; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.test.appender.ListAppender; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -35,15 +31,13 @@ import org.junit.Test; import java.util.List; import java.util.Map; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; /** * */ -public class RewriteAppenderTest { - private static final String CONFIG = "log4j-rewrite.xml"; +public class AsynchAppenderTest { + private static final String CONFIG = "log4j-asynch.xml"; private static Configuration config; private static ListAppender app; private static LoggerContext ctx; @@ -69,22 +63,19 @@ public class RewriteAppenderTest { } @Test - public void rewriteTest() { - StructuredDataMessage msg = new StructuredDataMessage("Test", "This is a test", "Service"); - msg.put("Key1", "Value1"); - msg.put("Key2", "Value2"); - EventLogger.logEvent(msg); - List list = app.getEvents(); + public void rewriteTest() throws Exception { + Logger logger = LogManager.getLogger(AsynchAppender.class); + logger.error("This is a test"); + logger.warn("Hello world!"); + Thread.sleep(100); + List list = app.getMessages(); assertNotNull("No events generated", list); - assertTrue("Incorrect number of events. Expected 1, got " + list.size(), list.size() == 1); - LogEvent event = list.get(0); - Message m = event.getMessage(); - assertTrue("Message is not a MapMessage", m instanceof MapMessage); - MapMessage message = (MapMessage) m; - Map map = message.getData(); - assertNotNull("No Map", map); - assertTrue("Incorrect number of map entries, expected 3 got " + map.size(), map.size() == 3); - String value = map.get("Key1"); - assertEquals("Apache", value); + assertTrue("Incorrect number of events. Expected 2, got " + list.size(), list.size() == 2); + String msg = list.get(0); + String expected = AsynchAppenderTest.class.getName() + " rewriteTest This is a test"; + assertTrue("Expected " + expected + ", Actual " + msg, expected.equals(msg)); + msg = list.get(1); + expected = AsynchAppenderTest.class.getName() + " rewriteTest Hello world!"; + assertTrue("Expected " + expected + ", Actual " + msg, expected.equals(msg)); } } Copied: logging/log4j/log4j2/trunk/core/src/test/resources/log4j-asynch.xml (from r1385344, logging/log4j/log4j2/trunk/core/src/test/resources/log4j-rewrite.xml) URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/resources/log4j-asynch.xml?p2=logging/log4j/log4j2/trunk/core/src/test/resources/log4j-asynch.xml&p1=logging/log4j/log4j2/trunk/core/src/test/resources/log4j-rewrite.xml&r1=1385344&r2=1386479&rev=1386479&view=diff ============================================================================== --- logging/log4j/log4j2/trunk/core/src/test/resources/log4j-rewrite.xml (original) +++ logging/log4j/log4j2/trunk/core/src/test/resources/log4j-asynch.xml Mon Sep 17 05:38:38 2012 @@ -23,25 +23,16 @@ - + - - - - - + - - + - - - - - - + +