You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2012/09/17 07:38:38 UTC

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...

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<Serializable> 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<Serializable>(queueSize);
+        this.blocking = blocking;
+        this.config = config;
+        this.appenderRefs = appenderRefs;
+        this.errorRef = errorRef;
+    }
+
+    @Override
+    public void start() {
+        Map<String, Appender> map = config.getAppenders();
+        List<AppenderControl> appenders = new ArrayList<AppenderControl>();
+        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<AppenderControl> appenders;
+        private final BlockingQueue<Serializable> queue;
+
+        public AsynchThread(List<AppenderControl> appenders, BlockingQueue<Serializable> 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<LogEvent> 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<String> 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<String, String> 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 @@
       <PatternLayout pattern="%m%n"/>
     </Console>
     <List name="List">
-      <ThresholdFilter level="debug"/>
+      <PatternLayout pattern="%C %M %m"/>
     </List>
-    <Rewrite name="Rewrite">
-      <MapRewritePolicy>
-        <KeyValuePair key="Key1" Value="Apache"/>
-        <KeyValuePair key="Key3" Value="Log4j"/>
-      </MapRewritePolicy>
+    <Asynch name="Asynch">
       <appender-ref ref="List"/>
-
-    </Rewrite>
+    </Asynch>
   </appenders>
 
   <loggers>
-    <logger name="EventLogger" level="trace" additivity="false">
-      <appender-ref ref="Rewrite"/>
-    </logger>
-
-    <root level="error">
-      <appender-ref ref="STDOUT"/>
+    <root level="debug">
+      <appender-ref ref="Asynch"/>
     </root>
   </loggers>