1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
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  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
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  
72  
73  
74  
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 
123 
124 
125 
126 
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 
149 
150 
151     public int getAvailable() {
152         return available.size();
153     }
154 
155     
156 
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 
174 
175 
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 
248 
249         public Builder setLevel(final Level level) {
250             this.level = level;
251             return this;
252         }
253 
254         
255 
256 
257         public Builder setRate(final float rate) {
258             this.rate = rate;
259             return this;
260         }
261 
262         
263 
264 
265 
266         public Builder setMaxBurst(final long maxBurst) {
267             this.maxBurst = maxBurst;
268             return this;
269         }
270 
271         
272 
273 
274         public Builder setOnMatch(final Result onMatch) {
275             this.onMatch = onMatch;
276             return this;
277         }
278 
279         
280 
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 }