logging-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rgo...@apache.org
Subject svn commit: r1178134 - in /logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src: main/java/org/apache/logging/log4j/core/filter/ test/java/org/apache/logging/log4j/core/filter/ test/resources/
Date Sun, 02 Oct 2011 07:13:19 GMT
Author: rgoers
Date: Sun Oct  2 07:13:19 2011
New Revision: 1178134

URL: http://svn.apache.org/viewvc?rev=1178134&view=rev
Log:
Add BurstFilter

Added:
    logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/BurstFilter.java
    logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/BurstFilterTest.java
    logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-burst.xml

Added: logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/BurstFilter.java
URL: http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/BurstFilter.java?rev=1178134&view=auto
==============================================================================
--- logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/BurstFilter.java
(added)
+++ logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/BurstFilter.java
Sun Oct  2 07:13:19 2011
@@ -0,0 +1,228 @@
+/* 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.logging.log4j.core.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+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.PluginFactory;
+import org.apache.logging.log4j.message.Message;
+
+import java.util.Iterator;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The <code>BurstFilter</code> is a logging filter that regulates logging
+ * traffic. Use this filter when you want to control the maximum burst of log
+ * statements that can be sent to an appender. The filter is configured in the
+ * log4j configuration file. For example, the following configuration limits the
+ * number of INFO level (as well as DEBUG and TRACE) log statements that can be sent to the
+ * console to a burst of 100 within 6 seconds. WARN, ERROR and FATAL messages would continue
to
+ * be delivered.<br>
+ * <br>
+ * <p/>
+ * <code>
+ * &lt;Console name="console"&gt;<br>
+ * &nbsp;&lt;PatternLayout pattern="%-5p %d{dd-MMM-yyyy HH:mm:ss} %x %t %m%n"/&gt;<br>
+ * &nbsp;$lt;filters&gt;<br>
+ * &nbsp;&nbsp;&lt;Burst level="INFO" burstInterval="6" maxBurst="100"/&gt;<br>
+ * &nbsp;&lt;/filters&gt;<br>
+ * &lt;/Console&gt;<br>
+ * </code><br>
+ */
+
+@Plugin(name = "Burst", type = "Core", elementType = "filter")
+public class BurstFilter extends FilterBase {
+
+    private static final long NANOS_IN_SECONDS =  1000000000;
+    /**
+     * Level of messages to be filtered. Anything at or below this level will be
+     * filtered out if <code>maxBurst</code> has been exceeded. The default is
+     * WARN meaning any messages that are higher than warn will be logged
+     * regardless of the size of a burst.
+     */
+    private final Level level;
+
+    private final long burstInterval;
+
+    private final DelayQueue<LogDelay> history = new DelayQueue<LogDelay>();
+
+    private final Queue<LogDelay> available = new ConcurrentLinkedQueue<LogDelay>();
+
+    /**
+     * Time of last token removal.
+     */
+    private long lastTokenRemovedTime;
+
+    private BurstFilter(Level level, long burstInterval, long maxBurst,
+                        Result onMatch, Result onMismatch) {
+        super(onMatch, onMismatch);
+        this.level = level;
+        this.burstInterval = burstInterval;
+        for (int i = 0; i < maxBurst; ++i) {
+            available.add(new LogDelay());
+        }
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, String msg, Object[]
params) {
+        return filter(level);
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable
t) {
+        return filter(level);
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable
t) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(LogEvent event) {
+        return filter(event.getLevel());
+    }
+
+    /**
+     * Decide if we're going to log <code>event</code> based on whether the
+     * maximum burst of log statements has been exceeded.
+     *
+     * @param level The log level.
+     * @return The onMatch value if the filter passes, onMismatch otherwise.
+     */
+    private Result filter(Level level) {
+        if (this.level.isAtLeastAsSpecificAs(level)) {
+            LogDelay delay = history.poll();
+            while (delay != null) {
+                available.add(delay);
+                delay = history.poll();
+            }
+            delay = available.poll();
+            if (delay != null) {
+                delay.setDelay(burstInterval);
+                history.add(delay);
+                return onMatch;
+            }
+            return onMismatch;
+        }
+        return onMatch;
+
+    }
+
+    public int getAvailable() {
+        return available.size();
+    }
+
+    public void clear() {
+        Iterator<LogDelay> iter = history.iterator();
+        while (iter.hasNext()) {
+            LogDelay delay = iter.next();
+            history.remove(delay);
+            available.add(delay);
+        }
+    }
+
+    private class LogDelay implements Delayed {
+
+        private long expireTime;
+
+        public LogDelay() {
+        }
+
+        public void setDelay(long delay) {
+            this.expireTime = (delay * NANOS_IN_SECONDS) + System.nanoTime();
+        }
+
+        public long getDelay(TimeUnit timeUnit) {
+            return timeUnit.convert(expireTime - System.nanoTime(), TimeUnit.NANOSECONDS);
+        }
+
+        public int compareTo(Delayed delayed) {
+            if (this.expireTime < ((LogDelay) delayed).expireTime) {
+                return -1;
+            } else if (this.expireTime > ((LogDelay) delayed).expireTime) {
+                return 1;
+            }
+            return 0;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            LogDelay logDelay = (LogDelay) o;
+
+            if (expireTime != logDelay.expireTime) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return (int) (expireTime ^ (expireTime >>> 32));
+        }
+    }
+
+    /**
+     * @param level                 The logging level.
+     * @param burstInterval Interval, in seconds, at which to add to the number of log statements
+     *                              that will be allowed following a burst. This value specifies
how often
+     *                              <code>burstRecoverAmount</code> statements
will be added to the total number
+     *                              allowed for every <code>burstRecoveryInterval</code>
that passes
+     *                              following a burst, up to but not exceeding <code>maxBurst</code>.
+     * @param maxBurst              This value dictates the maximum traffic burst that can
be logged to any appender
+     *                              that uses the <code>BurstFilter</code>, i.e.
there can never be more than
+     *                              <code>maxBurst</code> log statements sent
to an appender in
+     *                              <code>burstRecoveryInterval</code> seconds.
+     * @param match
+     * @param mismatch
+     * @return
+     */
+    @PluginFactory
+    public static BurstFilter createFilter(@PluginAttr("level") String level,
+                                           @PluginAttr("burstInterval") String burstInterval,
+                                           @PluginAttr("maxBurst") String maxBurst,
+                                           @PluginAttr("onmatch") String match,
+                                           @PluginAttr("onmismatch") String mismatch) {
+        Result onMatch = match == null ? null : Result.valueOf(match);
+        Result onMismatch = mismatch == null ? null : Result.valueOf(mismatch);
+        Level lvl = Level.toLevel(level, Level.WARN);
+        long brInterval = burstInterval == null ? 0 : Long.parseLong(burstInterval);
+        long max = maxBurst == null ? 0 : Long.parseLong(maxBurst);
+        if (onMatch == null) {
+            onMatch = Result.NEUTRAL;
+        }
+        if (onMismatch == null) {
+            onMismatch = Result.DENY;
+        }
+        return new BurstFilter(lvl, brInterval, max, onMatch, onMismatch);
+    }
+}
\ No newline at end of file

Added: logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/BurstFilterTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/BurstFilterTest.java?rev=1178134&view=auto
==============================================================================
--- logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/BurstFilterTest.java
(added)
+++ logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/BurstFilterTest.java
Sun Oct  2 07:13:19 2011
@@ -0,0 +1,164 @@
+/* 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.logging.log4j.core.filter;
+
+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.Filter;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ListAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+/**
+ * Unit test for <code>BurstFilter</code>.
+ */
+public class BurstFilterTest {
+
+    private static final String CONFIG = "log4j-burst.xml";
+
+    private static Configuration config;
+    private static ListAppender app;
+    private static BurstFilter filter;
+    private static LoggerContext ctx;
+
+    @BeforeClass
+    public static void setupClass() {
+        System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY, CONFIG);
+        ctx = (LoggerContext) LogManager.getContext(false);
+        config = ctx.getConfiguration();
+        for (Map.Entry<String, Appender> entry : config.getAppenders().entrySet())
{
+            if (entry.getKey().equals("ListAppender")) {
+                app = (ListAppender) entry.getValue();
+                Iterator<Filter> iter = app.getFilters();
+                while (iter.hasNext()) {
+                    Filter f = iter.next();
+                    if (f instanceof BurstFilter) {
+                        filter = (BurstFilter) f;
+                        break;
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    private Logger logger = LogManager.getLogger(BurstFilterTest.class.getName());
+
+    /**
+     * Test BurstFilter by surpassing maximum number of log messages allowed by filter and
+     * making sure only the maximum number are indeed logged, then wait for while and make
+     * sure the filter allows the appropriate number of messages to be logged.
+     */
+    @Test
+    public void test() throws Exception {
+        assertNotNull("No ListAppender", app);
+        assertNotNull("No BurstFilter", filter);
+        // exceed the burst limit and make sure no more than 100 errors get logged
+        long start = System.nanoTime();
+        for (int i = 0; i < 110; i++) {
+            if (i % 10 == 0) {
+                Thread.sleep(200);
+            }
+            logger.info("Logging 110 messages, should only see 100 logs # " + (i + 1));
+            assertTrue("Incorrect number of available slots", filter.getAvailable() <
100);
+        }
+        List<String> msgs = app.getMessages();
+        assertTrue("Incorrect message count. Should be 100, actual " + msgs.size(), msgs.size()
== 100);
+        app.clear();
+
+        assertTrue("Incorrect number of available slots", filter.getAvailable() < 100);
+        // Allow some of the events to clear
+        Thread.sleep(1500);
+
+        for (int i = 0; i < 110; i++) {
+            logger.info("Waited 1.5 seconds and trying to log again, should see more than
0 and less than 100" + (i + 1));
+        }
+
+        msgs = app.getMessages();
+        assertTrue("Incorrect message count. Should be > 0 and < 100, actual " + msgs.size(),
+            msgs.size() > 0 && msgs.size() < 100);
+        app.clear();
+
+        filter.clear();
+
+        for (int i = 0; i < 110; i++) {
+            logger.info("Waited 1.5 seconds and trying to log again, should see more than
0 and less than 100" + (i + 1));
+        }
+        assertTrue("", filter.getAvailable() == 0);
+        app.clear();
+
+
+        // now log 100 debugs, they shouldn't get through because there are no available
slots.
+        for (int i = 0; i < 110; i++) {
+            logger.debug(
+                "TEST FAILED! Logging 110 debug messages, shouldn't see any of them because
they are debugs #" + (i + 1));
+        }
+
+        msgs = app.getMessages();
+        assertTrue("Incorrect message count. Should be 0, actual " + msgs.size(), msgs.size()
== 0);
+        app.clear();
+
+        // now log 100 warns, they should all get through because the filter's level is set
at info
+        for (int i = 0; i < 110; i++) {
+            logger.warn("Logging 110 warn messages, should see all of them because they are
warns #" + (i + 1));
+        }
+
+        msgs = app.getMessages();
+        assertTrue("Incorrect message count. Should be 110, actual " + msgs.size(), msgs.size()
== 110);
+        app.clear();
+
+        // now log 100 errors, they should all get through because the filter level is set
at info
+        for (int i = 0; i < 110; i++) {
+            logger.error("Logging 110 error messages, should see all of them because they
are errors #" + (i + 1));
+        }
+
+        msgs = app.getMessages();
+        assertTrue("Incorrect message count. Should be 110, actual " + msgs.size(), msgs.size()
== 110);
+        app.clear();
+
+        // now log 100 fatals, they should all get through because the filter level is set
at info
+        for (int i = 0; i < 110; i++) {
+            logger.fatal("Logging 110 fatal messages, should see all of them because they
are fatals #" + (i + 1));
+        }
+
+        msgs = app.getMessages();
+        assertTrue("Incorrect message count. Should be 110, actual " + msgs.size(), msgs.size()
== 110);
+        app.clear();
+
+        // wait and make sure we can log messages again despite the fact we just logged a
bunch of warns, errors, fatals
+        Thread.sleep(3100);
+
+        for (int i = 0; i < 110; i++) {
+            logger.debug("Waited 3+ seconds, should see 100 logs #" + (i + 1));
+        }
+        msgs = app.getMessages();
+        assertTrue("Incorrect message count. Should be 100, actual " + msgs.size(), msgs.size()
== 100);
+        app.clear();
+
+    }
+}

Added: logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-burst.xml
URL: http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-burst.xml?rev=1178134&view=auto
==============================================================================
--- logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-burst.xml
(added)
+++ logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-burst.xml
Sun Oct  2 07:13:19 2011
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration status="warn" name="BurstTest">
+  <appenders>
+
+    <List name="ListAppender">
+      <PatternLayout pattern="%-5p %d{dd-MMM-yyyy HH:mm:ss} %t %m%n"/>
+      <filters>
+        <Burst level="INFO" burstInterval="3" maxBurst="100"/>
+      </filters>
+    </List>
+  </appenders>
+
+  <loggers>
+    <root level="TRACE">
+      <appender-ref ref="ListAppender"/>
+    </root>
+  </loggers>
+</configuration>
\ No newline at end of file



Mime
View raw message