View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.filter;
18  
19  import org.apache.logging.log4j.Level;
20  import org.apache.logging.log4j.Marker;
21  import org.apache.logging.log4j.core.LogEvent;
22  import org.apache.logging.log4j.core.Logger;
23  import org.apache.logging.log4j.core.config.plugins.Plugin;
24  import org.apache.logging.log4j.core.config.plugins.PluginAttr;
25  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
26  import org.apache.logging.log4j.message.Message;
27  
28  import java.util.Iterator;
29  import java.util.Queue;
30  import java.util.concurrent.ConcurrentLinkedQueue;
31  import java.util.concurrent.DelayQueue;
32  import java.util.concurrent.Delayed;
33  import java.util.concurrent.TimeUnit;
34  
35  /**
36   * The <code>BurstFilter</code> is a logging filter that regulates logging
37   * traffic. Use this filter when you want to control the maximum burst of log
38   * statements that can be sent to an appender. The filter is configured in the
39   * log4j configuration file. For example, the following configuration limits the
40   * number of INFO level (as well as DEBUG and TRACE) log statements that can be sent to the
41   * console to a burst of 100 with an average rate of 16 per second. WARN, ERROR and FATAL messages would continue to
42   * be delivered.<br>
43   * <br>
44   * <p/>
45   * <code>
46   * &lt;Console name="console"&gt;<br>
47   * &nbsp;&lt;PatternLayout pattern="%-5p %d{dd-MMM-yyyy HH:mm:ss} %x %t %m%n"/&gt;<br>
48   * &nbsp;&lt;filters&gt;<br>
49   * &nbsp;&nbsp;&lt;Burst level="INFO" rate="16" maxBurst="100"/&gt;<br>
50   * &nbsp;&lt;/filters&gt;<br>
51   * &lt;/Console&gt;<br>
52   * </code><br>
53   */
54  
55  @Plugin(name = "BurstFilter", category = "Core", elementType = "filter", printObject = true)
56  public final class BurstFilter extends AbstractFilter {
57  
58      private static final long NANOS_IN_SECONDS =  1000000000;
59  
60      private static final int DEFAULT_RATE = 10;
61  
62      private static final int DEFAULT_RATE_MULTIPLE = 100;
63  
64      private static final int HASH_SHIFT = 32;
65  
66      /**
67       * Level of messages to be filtered. Anything at or below this level will be
68       * filtered out if <code>maxBurst</code> has been exceeded. The default is
69       * WARN meaning any messages that are higher than warn will be logged
70       * regardless of the size of a burst.
71       */
72      private final Level level;
73  
74      private final long burstInterval;
75  
76      private final DelayQueue<LogDelay> history = new DelayQueue<LogDelay>();
77  
78      private final Queue<LogDelay> available = new ConcurrentLinkedQueue<LogDelay>();
79  
80      private BurstFilter(final Level level, final float rate, final long maxBurst, final Result onMatch,
81                          final Result onMismatch) {
82          super(onMatch, onMismatch);
83          this.level = level;
84          this.burstInterval = (long) (NANOS_IN_SECONDS * (maxBurst / rate));
85          for (int i = 0; i < maxBurst; ++i) {
86              available.add(new LogDelay());
87          }
88      }
89  
90      @Override
91      public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
92                           final Object... params) {
93          return filter(level);
94      }
95  
96      @Override
97      public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
98                           final Throwable t) {
99          return filter(level);
100     }
101 
102     @Override
103     public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
104                          final Throwable t) {
105         return filter(level);
106     }
107 
108     @Override
109     public Result filter(final LogEvent event) {
110         return filter(event.getLevel());
111     }
112 
113     /**
114      * Decide if we're going to log <code>event</code> based on whether the
115      * maximum burst of log statements has been exceeded.
116      *
117      * @param level The log level.
118      * @return The onMatch value if the filter passes, onMismatch otherwise.
119      */
120     private Result filter(final Level level) {
121         if (this.level.isAtLeastAsSpecificAs(level)) {
122             LogDelay delay = history.poll();
123             while (delay != null) {
124                 available.add(delay);
125                 delay = history.poll();
126             }
127             delay = available.poll();
128             if (delay != null) {
129                 delay.setDelay(burstInterval);
130                 history.add(delay);
131                 return onMatch;
132             }
133             return onMismatch;
134         }
135         return onMatch;
136 
137     }
138 
139     /**
140      * Returns the number of available slots. Used for unit testing.
141      * @return The number of available slots.
142      */
143     public int getAvailable() {
144         return available.size();
145     }
146 
147     /**
148      * Clear the history. Used for unit testing.
149      */
150     public void clear() {
151         final Iterator<LogDelay> iter = history.iterator();
152         while (iter.hasNext()) {
153             final LogDelay delay = iter.next();
154             history.remove(delay);
155             available.add(delay);
156         }
157     }
158 
159     @Override
160     public String toString() {
161         return "level=" + level.toString() + ", interval=" + burstInterval + ", max=" + history.size();
162     }
163 
164     /**
165      * Delay object to represent each log event that has occurred within the timespan.
166      */
167     private class LogDelay implements Delayed {
168 
169         private long expireTime;
170 
171         public LogDelay() {
172         }
173 
174         public void setDelay(final long delay) {
175             this.expireTime = delay + System.nanoTime();
176         }
177 
178         public long getDelay(final TimeUnit timeUnit) {
179             return timeUnit.convert(expireTime - System.nanoTime(), TimeUnit.NANOSECONDS);
180         }
181 
182         public int compareTo(final Delayed delayed) {
183             if (this.expireTime < ((LogDelay) delayed).expireTime) {
184                 return -1;
185             } else if (this.expireTime > ((LogDelay) delayed).expireTime) {
186                 return 1;
187             }
188             return 0;
189         }
190 
191         @Override
192         public boolean equals(final Object o) {
193             if (this == o) {
194                 return true;
195             }
196             if (o == null || getClass() != o.getClass()) {
197                 return false;
198             }
199 
200             final LogDelay logDelay = (LogDelay) o;
201 
202             if (expireTime != logDelay.expireTime) {
203                 return false;
204             }
205 
206             return true;
207         }
208 
209         @Override
210         public int hashCode() {
211             return (int) (expireTime ^ (expireTime >>> HASH_SHIFT));
212         }
213     }
214 
215     /**
216      * @param levelName  The logging level.
217      * @param rate   The average number of events per second to allow.
218      * @param maxBurst  The maximum number of events that can occur before events are filtered for exceeding the
219      * average rate. The default is 10 times the rate.
220      * @param match  The Result to return when the filter matches. Defaults to Result.NEUTRAL.
221      * @param mismatch The Result to return when the filter does not match. The default is Result.DENY.
222      * @return A BurstFilter.
223      */
224     @PluginFactory
225     public static BurstFilter createFilter(@PluginAttr("level") final String levelName,
226                                            @PluginAttr("rate") final String rate,
227                                            @PluginAttr("maxBurst") final String maxBurst,
228                                            @PluginAttr("onmatch") final String match,
229                                            @PluginAttr("onmismatch") final String mismatch) {
230         final Result onMatch = Result.toResult(match, Result.NEUTRAL);
231         final Result onMismatch = Result.toResult(mismatch, Result.DENY);
232         final Level level = Level.toLevel(levelName, Level.WARN);
233         float eventRate = rate == null ? DEFAULT_RATE : Float.parseFloat(rate);
234         if (eventRate <= 0) {
235             eventRate = DEFAULT_RATE;
236         }
237         final long max = maxBurst == null ? (long) (eventRate * DEFAULT_RATE_MULTIPLE) : Long.parseLong(maxBurst);
238         return new BurstFilter(level, eventRate, max, onMatch, onMismatch);
239     }
240 }