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