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 2013/01/01 20:27:48 UTC

svn commit: r1427540 - in /logging/log4j/log4j2/trunk: core/src/main/java/org/apache/logging/log4j/core/appender/ core/src/main/java/org/apache/logging/log4j/core/helpers/ core/src/main/java/org/apache/logging/log4j/core/net/ core/src/main/java/org/apa...

Author: rgoers
Date: Tue Jan  1 19:27:47 2013
New Revision: 1427540

URL: http://svn.apache.org/viewvc?rev=1427540&view=rev
Log:
Have FailoverAppender retry after user specified interval. JMS appenders now retry after connection failure at initialization.

Added:
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/test/appender/FailOnceAppender.java
      - copied, changed from r1426987, logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/test/appender/AlwaysFailAppender.java
Modified:
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/FailoverAppender.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/helpers/Constants.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/net/JMSQueueManager.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/net/JMSTopicManager.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/pattern/CachedDateFormat.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/FailoverAppenderTest.java
    logging/log4j/log4j2/trunk/core/src/test/resources/log4j-failover.xml
    logging/log4j/log4j2/trunk/src/site/xdoc/manual/appenders.xml

Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/FailoverAppender.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/FailoverAppender.java?rev=1427540&r1=1427539&r2=1427540&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/FailoverAppender.java (original)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/FailoverAppender.java Tue Jan  1 19:27:47 2013
@@ -27,6 +27,7 @@ import org.apache.logging.log4j.core.con
 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.helpers.Constants;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -50,12 +51,21 @@ public final class FailoverAppender exte
 
     private final List<AppenderControl> failoverAppenders = new ArrayList<AppenderControl>();
 
+    private final long interval;
+
+    private long nextCheck = 0;
+
+    private volatile boolean failure = false;
+
+    private static final int DEFAULT_INTERVAL = 60 * Constants.MILLIS_IN_SECONDS;
+
     private FailoverAppender(final String name, final Filter filter, final String primary, final String[] failovers,
-                            final Configuration config, final boolean handleExceptions) {
+                             final int interval, final Configuration config, final boolean handleExceptions) {
         super(name, filter, null, handleExceptions);
         this.primaryRef = primary;
         this.failovers = failovers;
         this.config = config;
+        this.interval = interval;
     }
 
 
@@ -95,22 +105,48 @@ public final class FailoverAppender exte
             error("FailoverAppender " + getName() + " did not start successfully");
             return;
         }
+        if (!failure) {
+            callAppender(event);
+        } else {
+            long current = System.currentTimeMillis();
+            if (current >= nextCheck) {
+                callAppender(event);
+            } else {
+                failover(event, null);
+            }
+        }
+    }
+
+    private void callAppender(final LogEvent event) {
         try {
             primary.callAppender(event);
         } catch (final Exception ex) {
-            re = new LoggingException(ex);
-            boolean written = false;
-            for (final AppenderControl control : failoverAppenders) {
-                try {
-                    control.callAppender(event);
-                    written = true;
-                    break;
-                } catch (final Exception fex) {
-                    continue;
+            nextCheck = System.currentTimeMillis() + interval;
+            failure = true;
+            failover(event, ex);
+        }
+    }
+
+    private void failover(final LogEvent event, Exception ex) {
+        RuntimeException re = ex != null ? new LoggingException(ex) : null;
+        boolean written = false;
+        Exception failoverException = null;
+        for (final AppenderControl control : failoverAppenders) {
+            try {
+                control.callAppender(event);
+                written = true;
+                break;
+            } catch (final Exception fex) {
+                if (failoverException == null) {
+                    failoverException = fex;
                 }
             }
-            if (!written && !isExceptionSuppressed()) {
+        }
+        if (!written && !isExceptionSuppressed()) {
+            if (re != null) {
                 throw re;
+            } else {
+                throw new LoggingException("Unable to write to failover appenders", failoverException);
             }
         }
     }
@@ -136,6 +172,7 @@ public final class FailoverAppender exte
      * @param name The name of the Appender (required).
      * @param primary The name of the primary Appender (required).
      * @param failovers The name of one or more Appenders to fail over to (at least one is required).
+     * @param interval The retry interval.
      * @param config The current Configuration (passed by the Configuration when the appender is created).
      * @param filter A Filter (optional).
      * @param suppress "true" if exceptions should be hidden from the application, "false" otherwise.
@@ -146,6 +183,7 @@ public final class FailoverAppender exte
     public static FailoverAppender createAppender(@PluginAttr("name") final String name,
                                                   @PluginAttr("primary") final String primary,
                                                   @PluginElement("failovers") final String[] failovers,
+                                                  @PluginAttr("retryInterval") final String interval,
                                                   @PluginConfiguration final Configuration config,
                                                   @PluginElement("filters") final Filter filter,
                                                   @PluginAttr("suppressExceptions") final String suppress) {
@@ -162,8 +200,26 @@ public final class FailoverAppender exte
             return null;
         }
 
+        int retryInterval;
+        if (interval == null) {
+            retryInterval = DEFAULT_INTERVAL;
+        } else {
+            try {
+                int value = Integer.parseInt(interval);
+                if (value >= 0) {
+                    retryInterval = value * Constants.MILLIS_IN_SECONDS;
+                } else {
+                    LOGGER.warn("Interval " + interval + " is less than zero. Using default");
+                    retryInterval = DEFAULT_INTERVAL;
+                }
+            } catch (NumberFormatException nfe) {
+                LOGGER.error("Interval " + interval + " is non-numeric. Using default");
+                retryInterval = DEFAULT_INTERVAL;
+            }
+        }
+
         final boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress);
 
-        return new FailoverAppender(name, filter, primary, failovers, config, handleExceptions);
+        return new FailoverAppender(name, filter, primary, failovers, retryInterval, config, handleExceptions);
     }
 }

Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/helpers/Constants.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/helpers/Constants.java?rev=1427540&r1=1427539&r2=1427540&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/helpers/Constants.java (original)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/helpers/Constants.java Tue Jan  1 19:27:47 2013
@@ -37,6 +37,11 @@ public final class Constants {
     public static final String LINE_SEP = PropertiesUtil.getSystemProperty("line.separator", "\n");
 
     /**
+     * Number of milliseconds in a second.
+     */
+    public static final int MILLIS_IN_SECONDS = 1000;
+
+    /**
      * Prevent class instantiation.
      */
     private Constants() {

Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/net/JMSQueueManager.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/net/JMSQueueManager.java?rev=1427540&r1=1427539&r2=1427540&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/net/JMSQueueManager.java (original)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/net/JMSQueueManager.java Tue Jan  1 19:27:47 2013
@@ -102,21 +102,37 @@ public class JMSQueueManager extends Abs
         if (info == null) {
             info = connect(context, factoryBindingName, queueBindingName, userName, password, false);
         }
-        super.send(object, info.session, info.sender);
+        try {
+            super.send(object, info.session, info.sender);
+        } catch (Exception ex) {
+            cleanup(true);
+            throw ex;
+        }
     }
 
     @Override
     public void releaseSub() {
+        if (info != null) {
+            cleanup(false);
+        }
+    }
+
+    private void cleanup(boolean quiet) {
         try {
-            if (info != null) {
-                info.session.close();
-                info.conn.close();
+            info.session.close();
+        } catch (Exception e) {
+            if (!quiet) {
+                LOGGER.error("Error closing session for " + getName(), e);
+            }
+        }
+        try {
+            info.conn.close();
+        } catch (Exception e) {
+            if (!quiet) {
+                LOGGER.error("Error closing connection for " + getName(), e);
             }
-        } catch (final JMSException ex) {
-            LOGGER.error("Error closing " + getName(), ex);
-        } finally {
-            info = null;
         }
+        info = null;
     }
 
     /**

Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/net/JMSTopicManager.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/net/JMSTopicManager.java?rev=1427540&r1=1427539&r2=1427540&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/net/JMSTopicManager.java (original)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/net/JMSTopicManager.java Tue Jan  1 19:27:47 2013
@@ -103,18 +103,37 @@ public class JMSTopicManager extends Abs
             info = connect(context, factoryBindingName, topicBindingName, userName, password, false);
         }
         super.send(object, info.session, info.publisher);
+        try {
+            super.send(object, info.session, info.publisher);
+        } catch (Exception ex) {
+            cleanup(true);
+            throw ex;
+        }
     }
 
     @Override
     public void releaseSub() {
+        if (info != null) {
+            cleanup(false);
+        }
+    }
+
+    private void cleanup(boolean quiet) {
         try {
-            if (info != null) {
-                info.session.close();
-                info.conn.close();
+            info.session.close();
+        } catch (Exception e) {
+            if (!quiet) {
+                LOGGER.error("Error closing session for " + getName(), e);
+            }
+        }
+        try {
+            info.conn.close();
+        } catch (Exception e) {
+            if (!quiet) {
+                LOGGER.error("Error closing connection for " + getName(), e);
             }
-        } catch (final JMSException ex) {
-            LOGGER.error("Error closing " + getName(), ex);
         }
+        info = null;
     }
 
     /**

Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/pattern/CachedDateFormat.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/pattern/CachedDateFormat.java?rev=1427540&r1=1427539&r2=1427540&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/pattern/CachedDateFormat.java (original)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/pattern/CachedDateFormat.java Tue Jan  1 19:27:47 2013
@@ -16,6 +16,8 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import org.apache.logging.log4j.core.helpers.Constants;
+
 import java.text.DateFormat;
 import java.text.FieldPosition;
 import java.text.NumberFormat;
@@ -82,8 +84,6 @@ final class CachedDateFormat extends Dat
 
     private static final int BUF_SIZE = 50;
 
-    private static final int MILLIS_IN_SECONDS = 1000;
-
     private static final int DEFAULT_VALIDITY = 1000;
 
     private static final int THREE_DIGITS = 100;
@@ -170,10 +170,10 @@ final class CachedDateFormat extends Dat
      *         field (likely RelativeTimeDateFormat)
      */
     public static int findMillisecondStart(final long time, final String formatted, final DateFormat formatter) {
-        long slotBegin = (time / MILLIS_IN_SECONDS) * MILLIS_IN_SECONDS;
+        long slotBegin = (time / Constants.MILLIS_IN_SECONDS) * Constants.MILLIS_IN_SECONDS;
 
         if (slotBegin > time) {
-            slotBegin -= MILLIS_IN_SECONDS;
+            slotBegin -= Constants.MILLIS_IN_SECONDS;
         }
 
         final int millis = (int) (time - slotBegin);
@@ -292,10 +292,10 @@ final class CachedDateFormat extends Dat
         cache.append(formatter.format(tmpDate));
         buf.append(cache);
         previousTime = now;
-        slotBegin = (previousTime / MILLIS_IN_SECONDS) * MILLIS_IN_SECONDS;
+        slotBegin = (previousTime / Constants.MILLIS_IN_SECONDS) * Constants.MILLIS_IN_SECONDS;
 
         if (slotBegin > previousTime) {
-            slotBegin -= MILLIS_IN_SECONDS;
+            slotBegin -= Constants.MILLIS_IN_SECONDS;
         }
 
         //

Modified: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/FailoverAppenderTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/FailoverAppenderTest.java?rev=1427540&r1=1427539&r2=1427540&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/FailoverAppenderTest.java (original)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/FailoverAppenderTest.java Tue Jan  1 19:27:47 2013
@@ -23,6 +23,7 @@ import org.apache.logging.log4j.core.Log
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
 import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.test.appender.FailOnceAppender;
 import org.apache.logging.log4j.test.appender.ListAppender;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
@@ -41,6 +42,7 @@ public class FailoverAppenderTest {
     private static final String CONFIG = "log4j-failover.xml";
     private static Configuration config;
     private static ListAppender app;
+    private static FailOnceAppender foApp;
     private static LoggerContext ctx;
 
     @BeforeClass
@@ -51,7 +53,8 @@ public class FailoverAppenderTest {
         for (final Map.Entry<String, Appender> entry : config.getAppenders().entrySet()) {
             if (entry.getKey().equals("List")) {
                 app = (ListAppender) entry.getValue();
-                break;
+            } else if (entry.getKey().equals("Once")) {
+                foApp = (FailOnceAppender) entry.getValue();
             }
         }
     }
@@ -64,9 +67,11 @@ public class FailoverAppenderTest {
     }
 
     org.apache.logging.log4j.Logger logger = LogManager.getLogger("LoggerTest");
+    org.apache.logging.log4j.Logger onceLogger = LogManager.getLogger("Once");
 
     @Test
     public void testFailover() {
+        app.clear();
         logger.error("This is a test");
         List<LogEvent> events = app.getEvents();
         assertNotNull(events);
@@ -77,4 +82,21 @@ public class FailoverAppenderTest {
         assertNotNull(events);
         assertTrue("Incorrect number of events. Should be 1 is " + events.size(), events.size() == 1);
     }
+
+    @Test
+    public void testRecovery() throws Exception {
+        app.clear();
+        onceLogger.error("Fail once");
+        onceLogger.error("Fail again");
+        List<LogEvent> events = app.getEvents();
+        assertNotNull(events);
+        assertTrue("Incorrect number of events. Should be 2 is " + events.size(), events.size() == 2);
+        app.clear();
+        Thread.sleep(1100);
+        onceLogger.error("Fail after recovery interval");
+        events = app.getEvents();
+        assertTrue("Did not recoever", events.size() == 0);
+        events = foApp.getEvents();
+        assertTrue("No events in primary appender", events.size() == 1);
+    }
 }

Copied: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/test/appender/FailOnceAppender.java (from r1426987, logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/test/appender/AlwaysFailAppender.java)
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/test/appender/FailOnceAppender.java?p2=logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/test/appender/FailOnceAppender.java&p1=logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/test/appender/AlwaysFailAppender.java&r1=1426987&r2=1427540&rev=1427540&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/test/appender/AlwaysFailAppender.java (original)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/test/appender/FailOnceAppender.java Tue Jan  1 19:27:47 2013
@@ -23,28 +23,46 @@ import org.apache.logging.log4j.core.con
 import org.apache.logging.log4j.core.config.plugins.PluginAttr;
 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  *
  */
-@Plugin(name="AlwaysFail",type="Core",elementType="appender",printObject=true)
-public class AlwaysFailAppender extends AbstractAppender {
+@Plugin(name="FailOnce",type="Core",elementType="appender",printObject=true)
+public class FailOnceAppender extends AbstractAppender {
+
+    boolean fail = true;
+
+    private List<LogEvent> events = new ArrayList<LogEvent>();
 
-    private AlwaysFailAppender(final String name) {
+    private FailOnceAppender(final String name) {
         super(name, null, null, false);
     }
 
     public void append(final LogEvent event) {
-        throw new LoggingException("Always fail");
+        if (fail) {
+            fail = false;
+            throw new LoggingException("Always fail");
+        } else {
+            events.add(event);
+        }
+    }
+
+    public List<LogEvent> getEvents() {
+        List<LogEvent> list = new ArrayList<LogEvent>(events);
+        events.clear();
+        return list;
     }
 
     @PluginFactory
-    public static AlwaysFailAppender createAppender(@PluginAttr("name") final String name) {
+    public static FailOnceAppender createAppender(@PluginAttr("name") final String name) {
         if (name == null) {
             LOGGER.error("A name for the Appender must be specified");
             return null;
         }
 
-        return new AlwaysFailAppender(name);
+        return new FailOnceAppender(name);
     }
 
 }

Modified: logging/log4j/log4j2/trunk/core/src/test/resources/log4j-failover.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/resources/log4j-failover.xml?rev=1427540&r1=1427539&r2=1427540&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/resources/log4j-failover.xml (original)
+++ logging/log4j/log4j2/trunk/core/src/test/resources/log4j-failover.xml Tue Jan  1 19:27:47 2013
@@ -19,15 +19,24 @@
 <configuration status="error" name="FailoverTest" packages="org.apache.logging.log4j.test">
   <Appenders>
     <AlwaysFail name="Fail" />
+    <FailOnce name="Once"/>
     <List name="List" />
     <Failover name="Failover" primary="Fail" suppressExceptions="false">
       <Failovers>
         <appender-ref ref="List"/>
       </Failovers>
     </Failover>
+    <Failover name="FailoverOnce" primary="Once" suppressExceptions="false" retryInterval="1">
+      <Failovers>
+        <appender-ref ref="List"/>
+      </Failovers>
+    </Failover>
   </Appenders>
 
   <loggers>
+    <logger name="Once" level="error" additvity="false">
+      <appender-ref ref="FailoverOnce"/>
+    </logger>
     <root level="error">
       <appender-ref ref="Failover"/>
     </root>

Modified: logging/log4j/log4j2/trunk/src/site/xdoc/manual/appenders.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/site/xdoc/manual/appenders.xml?rev=1427540&r1=1427539&r2=1427540&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/site/xdoc/manual/appenders.xml (original)
+++ logging/log4j/log4j2/trunk/src/site/xdoc/manual/appenders.xml Tue Jan  1 19:27:47 2013
@@ -222,6 +222,11 @@
               <td>The name of the Appender.</td>
             </tr>
             <tr>
+              <td>retryInterval</td>
+              <td>integer</td>
+              <td>The number of seconds that should pass before retrying the primary Appender. The default is 60.</td>
+            </tr>
+            <tr>
               <td>suppressExceptions</td>
               <td>boolean</td>
               <td>The default is true, causing exceptions to be internally logged and then ignored. When set to