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 }