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.Locale;
30  import java.util.Queue;
31  import java.util.concurrent.ConcurrentLinkedQueue;
32  import java.util.concurrent.DelayQueue;
33  import java.util.concurrent.Delayed;
34  import java.util.concurrent.TimeUnit;
35  
36  /**
37   * The <code>BurstFilter</code> is a logging filter that regulates logging
38   * traffic. Use this filter when you want to control the maximum burst of log
39   * statements that can be sent to an appender. The filter is configured in the
40   * log4j configuration file. For example, the following configuration limits the
41   * number of INFO level (as well as DEBUG and TRACE) log statements that can be sent to the
42   * console to a burst of 100 with an average rate of 16 per second. WARN, ERROR and FATAL messages would continue to
43   * be delivered.<br>
44   * <br>
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", type = "Core", elementType = "filter", 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<LogDelay>();
78  
79      private final Queue<LogDelay> available = new ConcurrentLinkedQueue<LogDelay>();
80  
81      private BurstFilter(Level level, float rate, long maxBurst, Result onMatch, 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(Logger logger, Level level, Marker marker, String msg, Object[] params) {
92          return filter(level);
93      }
94  
95      @Override
96      public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
97          return filter(level);
98      }
99  
100     @Override
101     public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
102         return filter(level);
103     }
104 
105     @Override
106     public Result filter(LogEvent event) {
107         return filter(event.getLevel());
108     }
109 
110     /**
111      * Decide if we're going to log <code>event</code> based on whether the
112      * maximum burst of log statements has been exceeded.
113      *
114      * @param level The log level.
115      * @return The onMatch value if the filter passes, onMismatch otherwise.
116      */
117     private Result filter(Level level) {
118         if (this.level.isAtLeastAsSpecificAs(level)) {
119             LogDelay delay = history.poll();
120             while (delay != null) {
121                 available.add(delay);
122                 delay = history.poll();
123             }
124             delay = available.poll();
125             if (delay != null) {
126                 delay.setDelay(burstInterval);
127                 history.add(delay);
128                 return onMatch;
129             }
130             return onMismatch;
131         }
132         return onMatch;
133 
134     }
135 
136     /**
137      * Returns the number of available slots. Used for unit testing.
138      * @return The number of available slots.
139      */
140     public int getAvailable() {
141         return available.size();
142     }
143 
144     /**
145      * Clear the history. Used for unit testing.
146      */
147     public void clear() {
148         Iterator<LogDelay> iter = history.iterator();
149         while (iter.hasNext()) {
150             LogDelay delay = iter.next();
151             history.remove(delay);
152             available.add(delay);
153         }
154     }
155 
156     @Override
157     public String toString() {
158         return "level=" + level.toString() + ", interval=" + burstInterval + ", max=" + history.size();
159     }
160 
161     /**
162      * Delay object to represent each log event that has occurred within the timespan.
163      */
164     private class LogDelay implements Delayed {
165 
166         private long expireTime;
167 
168         public LogDelay() {
169         }
170 
171         public void setDelay(long delay) {
172             this.expireTime = delay + System.nanoTime();
173         }
174 
175         public long getDelay(TimeUnit timeUnit) {
176             return timeUnit.convert(expireTime - System.nanoTime(), TimeUnit.NANOSECONDS);
177         }
178 
179         public int compareTo(Delayed delayed) {
180             if (this.expireTime < ((LogDelay) delayed).expireTime) {
181                 return -1;
182             } else if (this.expireTime > ((LogDelay) delayed).expireTime) {
183                 return 1;
184             }
185             return 0;
186         }
187 
188         @Override
189         public boolean equals(Object o) {
190             if (this == o) {
191                 return true;
192             }
193             if (o == null || getClass() != o.getClass()) {
194                 return false;
195             }
196 
197             LogDelay logDelay = (LogDelay) o;
198 
199             if (expireTime != logDelay.expireTime) {
200                 return false;
201             }
202 
203             return true;
204         }
205 
206         @Override
207         public int hashCode() {
208             return (int) (expireTime ^ (expireTime >>> HASH_SHIFT));
209         }
210     }
211 
212     /**
213      * @param levelName  The logging level.
214      * @param rate   The average number of events per second to allow.
215      * @param maxBurst  The maximum number of events that can occur before events are filtered for exceeding the
216      * average rate. The default is 10 times the rate.
217      * @param match  The Result to return when the filter matches. Defaults to Result.NEUTRAL.
218      * @param mismatch The Result to return when the filter does not match. The default is Result.DENY.
219      * @return A BurstFilter.
220      */
221     @PluginFactory
222     public static BurstFilter createFilter(@PluginAttr("level") String levelName,
223                                            @PluginAttr("rate") String rate,
224                                            @PluginAttr("maxBurst") String maxBurst,
225                                            @PluginAttr("onmatch") String match,
226                                            @PluginAttr("onmismatch") String mismatch) {
227         Result onMatch = Result.toResult(match, Result.NEUTRAL);
228         Result onMismatch = Result.toResult(mismatch, Result.DENY);
229         Level level = Level.toLevel(levelName, Level.WARN);
230         float eventRate = rate == null ? DEFAULT_RATE : Float.parseFloat(rate);
231         if (eventRate <= 0) {
232             eventRate = DEFAULT_RATE;
233         }
234         long max = maxBurst == null ? (long) (eventRate * DEFAULT_RATE_MULTIPLE) : Long.parseLong(maxBurst);
235         return new BurstFilter(level, eventRate, max, onMatch, onMismatch);
236     }
237 }