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