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 018package org.apache.logging.log4j.core.filter; 019 020import java.util.Queue; 021import java.util.concurrent.ConcurrentLinkedQueue; 022import java.util.concurrent.DelayQueue; 023import java.util.concurrent.Delayed; 024import java.util.concurrent.TimeUnit; 025 026import org.apache.logging.log4j.Level; 027import org.apache.logging.log4j.Marker; 028import org.apache.logging.log4j.core.Filter; 029import org.apache.logging.log4j.core.LogEvent; 030import org.apache.logging.log4j.core.Logger; 031import org.apache.logging.log4j.core.config.Node; 032import org.apache.logging.log4j.core.config.plugins.Plugin; 033import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 034import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 035import org.apache.logging.log4j.message.Message; 036 037/** 038 * The <code>BurstFilter</code> is a logging filter that regulates logging traffic. 039 * 040 * <p> 041 * Use this filter when you want to control the maximum burst of log statements that can be sent to an appender. The 042 * filter is configured in the log4j configuration file. For example, the following configuration limits the number of 043 * INFO level (as well as DEBUG and TRACE) log statements that can be sent to the console to a burst of 100 with an 044 * average rate of 16 per second. WARN, ERROR and FATAL messages would continue to be delivered. 045 * </p> 046 * <code> 047 * <Console name="console"><br> 048 * <PatternLayout pattern="%-5p %d{dd-MMM-yyyy HH:mm:ss} %x %t %m%n"/><br> 049 * <Filters><br> 050 * <BurstFilter level="INFO" rate="16" maxBurst="100"/><br> 051 * </Filters><br> 052 * </Console><br> 053 * </code><br> 054 */ 055 056@Plugin(name = "BurstFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true) 057public final class BurstFilter extends AbstractFilter { 058 059 private static final long NANOS_IN_SECONDS = 1000000000; 060 061 private static final int DEFAULT_RATE = 10; 062 063 private static final int DEFAULT_RATE_MULTIPLE = 100; 064 065 private static final int HASH_SHIFT = 32; 066 067 /** 068 * Level of messages to be filtered. Anything at or below this level will be 069 * filtered out if <code>maxBurst</code> has been exceeded. The default is 070 * WARN meaning any messages that are higher than warn will be logged 071 * regardless of the size of a burst. 072 */ 073 private final Level level; 074 075 private final long burstInterval; 076 077 private final DelayQueue<LogDelay> history = new DelayQueue<>(); 078 079 private final Queue<LogDelay> available = new ConcurrentLinkedQueue<>(); 080 081 static LogDelay createLogDelay(final long expireTime) { 082 return new LogDelay(expireTime); 083 } 084 085 private BurstFilter(final Level level, final float rate, final long maxBurst, final Result onMatch, 086 final Result onMismatch) { 087 super(onMatch, onMismatch); 088 this.level = level; 089 this.burstInterval = (long) (NANOS_IN_SECONDS * (maxBurst / rate)); 090 for (int i = 0; i < maxBurst; ++i) { 091 available.add(createLogDelay(0)); 092 } 093 } 094 095 @Override 096 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 097 final Object... params) { 098 return filter(level); 099 } 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 @Override 119 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 120 final Object p0) { 121 return filter(level); 122 } 123 124 @Override 125 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 126 final Object p0, final Object p1) { 127 return filter(level); 128 } 129 130 @Override 131 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 132 final Object p0, final Object p1, final Object p2) { 133 return filter(level); 134 } 135 136 @Override 137 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 138 final Object p0, final Object p1, final Object p2, final Object p3) { 139 return filter(level); 140 } 141 142 @Override 143 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 144 final Object p0, final Object p1, final Object p2, final Object p3, 145 final Object p4) { 146 return filter(level); 147 } 148 149 @Override 150 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 151 final Object p0, final Object p1, final Object p2, final Object p3, 152 final Object p4, final Object p5) { 153 return filter(level); 154 } 155 156 @Override 157 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 158 final Object p0, final Object p1, final Object p2, final Object p3, 159 final Object p4, final Object p5, final Object p6) { 160 return filter(level); 161 } 162 163 @Override 164 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 165 final Object p0, final Object p1, final Object p2, final Object p3, 166 final Object p4, final Object p5, final Object p6, 167 final Object p7) { 168 return filter(level); 169 } 170 171 @Override 172 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 173 final Object p0, final Object p1, final Object p2, final Object p3, 174 final Object p4, final Object p5, final Object p6, 175 final Object p7, final Object p8) { 176 return filter(level); 177 } 178 179 @Override 180 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 181 final Object p0, final Object p1, final Object p2, final Object p3, 182 final Object p4, final Object p5, final Object p6, 183 final Object p7, final Object p8, final Object p9) { 184 return filter(level); 185 } 186 187 /** 188 * Decide if we're going to log <code>event</code> based on whether the 189 * maximum burst of log statements has been exceeded. 190 * 191 * @param level The log level. 192 * @return The onMatch value if the filter passes, onMismatch otherwise. 193 */ 194 private Result filter(final Level level) { 195 if (this.level.isMoreSpecificThan(level)) { 196 LogDelay delay = history.poll(); 197 while (delay != null) { 198 available.add(delay); 199 delay = history.poll(); 200 } 201 delay = available.poll(); 202 if (delay != null) { 203 delay.setDelay(burstInterval); 204 history.add(delay); 205 return onMatch; 206 } 207 return onMismatch; 208 } 209 return onMatch; 210 211 } 212 213 /** 214 * Returns the number of available slots. Used for unit testing. 215 * @return The number of available slots. 216 */ 217 public int getAvailable() { 218 return available.size(); 219 } 220 221 /** 222 * Clear the history. Used for unit testing. 223 */ 224 public void clear() { 225 for (final LogDelay delay : history) { 226 history.remove(delay); 227 available.add(delay); 228 } 229 } 230 231 @Override 232 public String toString() { 233 return "level=" + level.toString() + ", interval=" + burstInterval + ", max=" + history.size(); 234 } 235 236 /** 237 * Delay object to represent each log event that has occurred within the timespan. 238 * 239 * Consider this class private, package visibility for testing. 240 */ 241 private static class LogDelay implements Delayed { 242 243 LogDelay(final long expireTime) { 244 this.expireTime = expireTime; 245 } 246 247 private long expireTime; 248 249 public void setDelay(final long delay) { 250 this.expireTime = delay + System.nanoTime(); 251 } 252 253 @Override 254 public long getDelay(final TimeUnit timeUnit) { 255 return timeUnit.convert(expireTime - System.nanoTime(), TimeUnit.NANOSECONDS); 256 } 257 258 @Override 259 public int compareTo(final Delayed delayed) { 260 final long diff = this.expireTime - ((LogDelay) delayed).expireTime; 261 return Long.signum(diff); 262 } 263 264 @Override 265 public boolean equals(final Object o) { 266 if (this == o) { 267 return true; 268 } 269 if (o == null || getClass() != o.getClass()) { 270 return false; 271 } 272 273 final LogDelay logDelay = (LogDelay) o; 274 275 if (expireTime != logDelay.expireTime) { 276 return false; 277 } 278 279 return true; 280 } 281 282 @Override 283 public int hashCode() { 284 return (int) (expireTime ^ (expireTime >>> HASH_SHIFT)); 285 } 286 } 287 288 @PluginBuilderFactory 289 public static Builder newBuilder() { 290 return new Builder(); 291 } 292 293 public static class Builder extends AbstractFilterBuilder<Builder> implements org.apache.logging.log4j.core.util.Builder<BurstFilter> { 294 295 @PluginBuilderAttribute 296 private Level level = Level.WARN; 297 298 @PluginBuilderAttribute 299 private float rate = DEFAULT_RATE; 300 301 @PluginBuilderAttribute 302 private long maxBurst; 303 304 /** 305 * Sets the logging level to use. 306 * @param level the logging level to use. 307 * @return this 308 */ 309 public Builder setLevel(final Level level) { 310 this.level = level; 311 return this; 312 } 313 314 /** 315 * Sets the average number of events per second to allow. 316 * @param rate the average number of events per second to allow. This must be a positive number. 317 * @return this 318 */ 319 public Builder setRate(final float rate) { 320 this.rate = rate; 321 return this; 322 } 323 324 /** 325 * Sets the maximum number of events that can occur before events are filtered for exceeding the average rate. 326 * @param maxBurst Sets the maximum number of events that can occur before events are filtered for exceeding the average rate. 327 * The default is 10 times the rate. 328 * @return this 329 */ 330 public Builder setMaxBurst(final long maxBurst) { 331 this.maxBurst = maxBurst; 332 return this; 333 } 334 335 @Override 336 public BurstFilter build() { 337 if (this.rate <= 0) { 338 this.rate = DEFAULT_RATE; 339 } 340 if (this.maxBurst <= 0) { 341 this.maxBurst = (long) (this.rate * DEFAULT_RATE_MULTIPLE); 342 } 343 return new BurstFilter(this.level, this.rate, this.maxBurst, this.getOnMatch(), this.getOnMismatch()); 344 } 345 } 346}