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      private BurstFilter(final Level level, final float rate, final long maxBurst, final Result onMatch,
85                          final Result onMismatch) {
86          super(onMatch, onMismatch);
87          this.level = level;
88          this.burstInterval = (long) (NANOS_IN_SECONDS * (maxBurst / rate));
89          for (int i = 0; i < maxBurst; ++i) {
90              available.add(new LogDelay());
91          }
92      }
93  
94      @Override
95      public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
96                           final Object... params) {
97          return filter(level);
98      }
99  
100     @Override
101     public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
102                          final Throwable t) {
103         return filter(level);
104     }
105 
106     @Override
107     public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
108                          final Throwable t) {
109         return filter(level);
110     }
111 
112     @Override
113     public Result filter(final LogEvent event) {
114         return filter(event.getLevel());
115     }
116 
117     /**
118      * Decide if we're going to log <code>event</code> based on whether the
119      * maximum burst of log statements has been exceeded.
120      *
121      * @param level The log level.
122      * @return The onMatch value if the filter passes, onMismatch otherwise.
123      */
124     private Result filter(final Level level) {
125         if (this.level.isMoreSpecificThan(level)) {
126             LogDelay delay = history.poll();
127             while (delay != null) {
128                 available.add(delay);
129                 delay = history.poll();
130             }
131             delay = available.poll();
132             if (delay != null) {
133                 delay.setDelay(burstInterval);
134                 history.add(delay);
135                 return onMatch;
136             }
137             return onMismatch;
138         }
139         return onMatch;
140 
141     }
142 
143     /**
144      * Returns the number of available slots. Used for unit testing.
145      * @return The number of available slots.
146      */
147     public int getAvailable() {
148         return available.size();
149     }
150 
151     /**
152      * Clear the history. Used for unit testing.
153      */
154     public void clear() {
155         final Iterator<LogDelay> iter = history.iterator();
156         while (iter.hasNext()) {
157             final LogDelay delay = iter.next();
158             history.remove(delay);
159             available.add(delay);
160         }
161     }
162 
163     @Override
164     public String toString() {
165         return "level=" + level.toString() + ", interval=" + burstInterval + ", max=" + history.size();
166     }
167 
168     /**
169      * Delay object to represent each log event that has occurred within the timespan.
170      */
171     private class LogDelay implements Delayed {
172 
173         private long expireTime;
174 
175         public LogDelay() {
176         }
177 
178         public void setDelay(final long delay) {
179             this.expireTime = delay + System.nanoTime();
180         }
181 
182         @Override
183         public long getDelay(final TimeUnit timeUnit) {
184             return timeUnit.convert(expireTime - System.nanoTime(), TimeUnit.NANOSECONDS);
185         }
186 
187         @Override
188         public int compareTo(final Delayed delayed) {
189             if (this.expireTime < ((LogDelay) delayed).expireTime) {
190                 return -1;
191             } else if (this.expireTime > ((LogDelay) delayed).expireTime) {
192                 return 1;
193             }
194             return 0;
195         }
196 
197         @Override
198         public boolean equals(final Object o) {
199             if (this == o) {
200                 return true;
201             }
202             if (o == null || getClass() != o.getClass()) {
203                 return false;
204             }
205 
206             final LogDelay logDelay = (LogDelay) o;
207 
208             if (expireTime != logDelay.expireTime) {
209                 return false;
210             }
211 
212             return true;
213         }
214 
215         @Override
216         public int hashCode() {
217             return (int) (expireTime ^ (expireTime >>> HASH_SHIFT));
218         }
219     }
220 
221     @PluginBuilderFactory
222     public static Builder newBuilder() {
223         return new Builder();
224     }
225 
226     public static class Builder implements org.apache.logging.log4j.core.util.Builder<BurstFilter> {
227 
228         @PluginBuilderAttribute
229         private Level level = Level.WARN;
230 
231         @PluginBuilderAttribute
232         private float rate = DEFAULT_RATE;
233 
234         @PluginBuilderAttribute
235         private long maxBurst;
236 
237         @PluginBuilderAttribute
238         private Result onMatch = Result.NEUTRAL;
239 
240         @PluginBuilderAttribute
241         private Result onMismatch = Result.DENY;
242 
243         /**
244          * Sets the logging level to use.
245          */
246         public Builder setLevel(final Level level) {
247             this.level = level;
248             return this;
249         }
250 
251         /**
252          * Sets the average number of events per second to allow. This must be a positive number.
253          */
254         public Builder setRate(final float rate) {
255             this.rate = rate;
256             return this;
257         }
258 
259         /**
260          * Sets the maximum number of events that can occur before events are filtered for exceeding the average rate.
261          * The default is 10 times the rate.
262          */
263         public Builder setMaxBurst(final long maxBurst) {
264             this.maxBurst = maxBurst;
265             return this;
266         }
267 
268         /**
269          * Sets the Result to return when the filter matches. Defaults to Result.NEUTRAL.
270          */
271         public Builder setOnMatch(final Result onMatch) {
272             this.onMatch = onMatch;
273             return this;
274         }
275 
276         /**
277          * Sets the Result to return when the filter does not match. The default is Result.DENY.
278          */
279         public Builder setOnMismatch(final Result onMismatch) {
280             this.onMismatch = onMismatch;
281             return this;
282         }
283 
284         @Override
285         public BurstFilter build() {
286             if (this.rate <= 0) {
287                 this.rate = DEFAULT_RATE;
288             }
289             if (this.maxBurst <= 0) {
290                 this.maxBurst = (long) (this.rate * DEFAULT_RATE_MULTIPLE);
291             }
292             return new BurstFilter(this.level, this.rate, this.maxBurst, this.onMatch, this.onMismatch);
293         }
294     }
295 }