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  
18  package org.apache.logging.log4j.core.filter;
19  
20  import java.util.Iterator;
21  import java.util.Queue;
22  import java.util.concurrent.ConcurrentLinkedQueue;
23  import java.util.concurrent.DelayQueue;
24  import java.util.concurrent.Delayed;
25  import java.util.concurrent.TimeUnit;
26  
27  import org.apache.logging.log4j.Level;
28  import org.apache.logging.log4j.Marker;
29  import org.apache.logging.log4j.core.Filter;
30  import org.apache.logging.log4j.core.LogEvent;
31  import org.apache.logging.log4j.core.Logger;
32  import org.apache.logging.log4j.core.config.Node;
33  import org.apache.logging.log4j.core.config.plugins.Plugin;
34  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
35  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
36  import org.apache.logging.log4j.message.Message;
37  
38  /**
39   * The <code>BurstFilter</code> is a logging filter that regulates logging traffic.
40   * 
41   * <p>
42   * Use this filter when you want to control the maximum burst of log statements that can be sent to an appender. The
43   * filter is configured in the log4j configuration file. For example, the following configuration limits the number of
44   * INFO level (as well as DEBUG and TRACE) log statements that can be sent to the console to a burst of 100 with an
45   * average rate of 16 per second. WARN, ERROR and FATAL messages would continue to be delivered.
46   * </p>
47   * <code>
48   * &lt;Console name="console"&gt;<br>
49   * &nbsp;&lt;PatternLayout pattern="%-5p %d{dd-MMM-yyyy HH:mm:ss} %x %t %m%n"/&gt;<br>
50   * &nbsp;&lt;filters&gt;<br>
51   * &nbsp;&nbsp;&lt;Burst level="INFO" rate="16" maxBurst="100"/&gt;<br>
52   * &nbsp;&lt;/filters&gt;<br>
53   * &lt;/Console&gt;<br>
54   * </code><br>
55   */
56  
57  @Plugin(name = "BurstFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
58  public final class BurstFilter extends AbstractFilter {
59  
60      private static final long serialVersionUID = 1L;
61  
62      private static final long NANOS_IN_SECONDS = 1000000000;
63  
64      private static final int DEFAULT_RATE = 10;
65  
66      private static final int DEFAULT_RATE_MULTIPLE = 100;
67  
68      private static final int HASH_SHIFT = 32;
69  
70      /**
71       * Level of messages to be filtered. Anything at or below this level will be
72       * filtered out if <code>maxBurst</code> has been exceeded. The default is
73       * WARN meaning any messages that are higher than warn will be logged
74       * regardless of the size of a burst.
75       */
76      private final Level level;
77  
78      private final long burstInterval;
79  
80      private final DelayQueue<LogDelay> history = new DelayQueue<LogDelay>();
81  
82      private final Queue<LogDelay> available = new ConcurrentLinkedQueue<LogDelay>();
83  
84      static LogDelay createLogDelay(long expireTime) {
85          return new LogDelay(expireTime);
86      }
87      
88      private BurstFilter(final Level level, final float rate, final long maxBurst, final Result onMatch,
89                          final Result onMismatch) {
90          super(onMatch, onMismatch);
91          this.level = level;
92          this.burstInterval = (long) (NANOS_IN_SECONDS * (maxBurst / rate));
93          for (int i = 0; i < maxBurst; ++i) {
94              available.add(createLogDelay(0));
95          }
96      }
97  
98      @Override
99      public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
100                          final Object... params) {
101         return filter(level);
102     }
103 
104     @Override
105     public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
106                          final Throwable t) {
107         return filter(level);
108     }
109 
110     @Override
111     public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
112                          final Throwable t) {
113         return filter(level);
114     }
115 
116     @Override
117     public Result filter(final LogEvent event) {
118         return filter(event.getLevel());
119     }
120 
121     /**
122      * Decide if we're going to log <code>event</code> based on whether the
123      * maximum burst of log statements has been exceeded.
124      *
125      * @param level The log level.
126      * @return The onMatch value if the filter passes, onMismatch otherwise.
127      */
128     private Result filter(final Level level) {
129         if (this.level.isMoreSpecificThan(level)) {
130             LogDelay delay = history.poll();
131             while (delay != null) {
132                 available.add(delay);
133                 delay = history.poll();
134             }
135             delay = available.poll();
136             if (delay != null) {
137                 delay.setDelay(burstInterval);
138                 history.add(delay);
139                 return onMatch;
140             }
141             return onMismatch;
142         }
143         return onMatch;
144 
145     }
146 
147     /**
148      * Returns the number of available slots. Used for unit testing.
149      * @return The number of available slots.
150      */
151     public int getAvailable() {
152         return available.size();
153     }
154 
155     /**
156      * Clear the history. Used for unit testing.
157      */
158     public void clear() {
159         final Iterator<LogDelay> iter = history.iterator();
160         while (iter.hasNext()) {
161             final LogDelay delay = iter.next();
162             history.remove(delay);
163             available.add(delay);
164         }
165     }
166 
167     @Override
168     public String toString() {
169         return "level=" + level.toString() + ", interval=" + burstInterval + ", max=" + history.size();
170     }
171 
172     /**
173      * Delay object to represent each log event that has occurred within the timespan.
174      * 
175      * Consider this class private, package visibility for testing.
176      */
177     private static class LogDelay implements Delayed {
178 
179         LogDelay(long expireTime) {
180             this.expireTime = expireTime;
181         }
182 
183         private long expireTime;
184 
185         public void setDelay(final long delay) {
186             this.expireTime = delay + System.nanoTime();
187         }
188 
189         @Override
190         public long getDelay(final TimeUnit timeUnit) {
191             return timeUnit.convert(expireTime - System.nanoTime(), TimeUnit.NANOSECONDS);
192         }
193 
194         @Override
195         public int compareTo(final Delayed delayed) {
196             final long diff = this.expireTime - ((LogDelay) delayed).expireTime;
197             return Long.signum(diff);
198         }
199 
200         @Override
201         public boolean equals(final Object o) {
202             if (this == o) {
203                 return true;
204             }
205             if (o == null || getClass() != o.getClass()) {
206                 return false;
207             }
208 
209             final LogDelay logDelay = (LogDelay) o;
210 
211             if (expireTime != logDelay.expireTime) {
212                 return false;
213             }
214 
215             return true;
216         }
217 
218         @Override
219         public int hashCode() {
220             return (int) (expireTime ^ (expireTime >>> HASH_SHIFT));
221         }
222     }
223 
224     @PluginBuilderFactory
225     public static Builder newBuilder() {
226         return new Builder();
227     }
228 
229     public static class Builder implements org.apache.logging.log4j.core.util.Builder<BurstFilter> {
230 
231         @PluginBuilderAttribute
232         private Level level = Level.WARN;
233 
234         @PluginBuilderAttribute
235         private float rate = DEFAULT_RATE;
236 
237         @PluginBuilderAttribute
238         private long maxBurst;
239 
240         @PluginBuilderAttribute
241         private Result onMatch = Result.NEUTRAL;
242 
243         @PluginBuilderAttribute
244         private Result onMismatch = Result.DENY;
245 
246         /**
247          * Sets the logging level to use.
248          */
249         public Builder setLevel(final Level level) {
250             this.level = level;
251             return this;
252         }
253 
254         /**
255          * Sets the average number of events per second to allow. This must be a positive number.
256          */
257         public Builder setRate(final float rate) {
258             this.rate = rate;
259             return this;
260         }
261 
262         /**
263          * Sets the maximum number of events that can occur before events are filtered for exceeding the average rate.
264          * The default is 10 times the rate.
265          */
266         public Builder setMaxBurst(final long maxBurst) {
267             this.maxBurst = maxBurst;
268             return this;
269         }
270 
271         /**
272          * Sets the Result to return when the filter matches. Defaults to Result.NEUTRAL.
273          */
274         public Builder setOnMatch(final Result onMatch) {
275             this.onMatch = onMatch;
276             return this;
277         }
278 
279         /**
280          * Sets the Result to return when the filter does not match. The default is Result.DENY.
281          */
282         public Builder setOnMismatch(final Result onMismatch) {
283             this.onMismatch = onMismatch;
284             return this;
285         }
286 
287         @Override
288         public BurstFilter build() {
289             if (this.rate <= 0) {
290                 this.rate = DEFAULT_RATE;
291             }
292             if (this.maxBurst <= 0) {
293                 this.maxBurst = (long) (this.rate * DEFAULT_RATE_MULTIPLE);
294             }
295             return new BurstFilter(this.level, this.rate, this.maxBurst, this.onMatch, this.onMismatch);
296         }
297     }
298 }