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>