001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017
018 package org.apache.logging.log4j.core.filter;
019
020 import java.util.Iterator;
021 import java.util.Queue;
022 import java.util.concurrent.ConcurrentLinkedQueue;
023 import java.util.concurrent.DelayQueue;
024 import java.util.concurrent.Delayed;
025 import java.util.concurrent.TimeUnit;
026
027 import org.apache.logging.log4j.Level;
028 import org.apache.logging.log4j.Marker;
029 import org.apache.logging.log4j.core.Filter;
030 import org.apache.logging.log4j.core.LogEvent;
031 import org.apache.logging.log4j.core.Logger;
032 import org.apache.logging.log4j.core.config.Node;
033 import org.apache.logging.log4j.core.config.plugins.Plugin;
034 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
035 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
036 import org.apache.logging.log4j.message.Message;
037
038 /**
039 * The <code>BurstFilter</code> is a logging filter that regulates logging traffic.
040 *
041 * <p>
042 * Use this filter when you want to control the maximum burst of log statements that can be sent to an appender. The
043 * filter is configured in the log4j configuration file. For example, the following configuration limits the number of
044 * INFO level (as well as DEBUG and TRACE) log statements that can be sent to the console to a burst of 100 with an
045 * average rate of 16 per second. WARN, ERROR and FATAL messages would continue to be delivered.
046 * </p>
047 * <code>
048 * <Console name="console"><br>
049 * <PatternLayout pattern="%-5p %d{dd-MMM-yyyy HH:mm:ss} %x %t %m%n"/><br>
050 * <filters><br>
051 * <Burst level="INFO" rate="16" maxBurst="100"/><br>
052 * </filters><br>
053 * </Console><br>
054 * </code><br>
055 */
056
057 @Plugin(name = "BurstFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
058 public final class BurstFilter extends AbstractFilter {
059
060 private static final long serialVersionUID = 1L;
061
062 private static final long NANOS_IN_SECONDS = 1000000000;
063
064 private static final int DEFAULT_RATE = 10;
065
066 private static final int DEFAULT_RATE_MULTIPLE = 100;
067
068 private static final int HASH_SHIFT = 32;
069
070 /**
071 * Level of messages to be filtered. Anything at or below this level will be
072 * filtered out if <code>maxBurst</code> has been exceeded. The default is
073 * WARN meaning any messages that are higher than warn will be logged
074 * regardless of the size of a burst.
075 */
076 private final Level level;
077
078 private final long burstInterval;
079
080 private final DelayQueue<LogDelay> history = new DelayQueue<LogDelay>();
081
082 private final Queue<LogDelay> available = new ConcurrentLinkedQueue<LogDelay>();
083
084 static LogDelay createLogDelay(long expireTime) {
085 return new LogDelay(expireTime);
086 }
087
088 private BurstFilter(final Level level, final float rate, final long maxBurst, final Result onMatch,
089 final Result onMismatch) {
090 super(onMatch, onMismatch);
091 this.level = level;
092 this.burstInterval = (long) (NANOS_IN_SECONDS * (maxBurst / rate));
093 for (int i = 0; i < maxBurst; ++i) {
094 available.add(createLogDelay(0));
095 }
096 }
097
098 @Override
099 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 * Decide if we're going to log <code>event</code> based on whether the
123 * maximum burst of log statements has been exceeded.
124 *
125 * @param level The log level.
126 * @return The onMatch value if the filter passes, onMismatch otherwise.
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 * Returns the number of available slots. Used for unit testing.
149 * @return The number of available slots.
150 */
151 public int getAvailable() {
152 return available.size();
153 }
154
155 /**
156 * Clear the history. Used for unit testing.
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 * Delay object to represent each log event that has occurred within the timespan.
174 *
175 * Consider this class private, package visibility for testing.
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 * Sets the logging level to use.
248 */
249 public Builder setLevel(final Level level) {
250 this.level = level;
251 return this;
252 }
253
254 /**
255 * Sets the average number of events per second to allow. This must be a positive number.
256 */
257 public Builder setRate(final float rate) {
258 this.rate = rate;
259 return this;
260 }
261
262 /**
263 * Sets the maximum number of events that can occur before events are filtered for exceeding the average rate.
264 * The default is 10 times the rate.
265 */
266 public Builder setMaxBurst(final long maxBurst) {
267 this.maxBurst = maxBurst;
268 return this;
269 }
270
271 /**
272 * Sets the Result to return when the filter matches. Defaults to Result.NEUTRAL.
273 */
274 public Builder setOnMatch(final Result onMatch) {
275 this.onMatch = onMatch;
276 return this;
277 }
278
279 /**
280 * Sets the Result to return when the filter does not match. The default is Result.DENY.
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 }