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.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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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
69
70
71
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
120
121
122
123
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
146
147
148 public int getAvailable() {
149 return available.size();
150 }
151
152
153
154
155 public void clear() {
156 for (final 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
169
170
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
243
244 public Builder setLevel(final Level level) {
245 this.level = level;
246 return this;
247 }
248
249
250
251
252 public Builder setRate(final float rate) {
253 this.rate = rate;
254 return this;
255 }
256
257
258
259
260
261 public Builder setMaxBurst(final long maxBurst) {
262 this.maxBurst = maxBurst;
263 return this;
264 }
265
266
267
268
269 public Builder setOnMatch(final Result onMatch) {
270 this.onMatch = onMatch;
271 return this;
272 }
273
274
275
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 }