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.Iterator; 021import java.util.Queue; 022import java.util.concurrent.ConcurrentLinkedQueue; 023import java.util.concurrent.DelayQueue; 024import java.util.concurrent.Delayed; 025import java.util.concurrent.TimeUnit; 026 027import org.apache.logging.log4j.Level; 028import org.apache.logging.log4j.Marker; 029import org.apache.logging.log4j.core.Filter; 030import org.apache.logging.log4j.core.LogEvent; 031import org.apache.logging.log4j.core.Logger; 032import org.apache.logging.log4j.core.config.Node; 033import org.apache.logging.log4j.core.config.plugins.Plugin; 034import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 035import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 036import 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) 058public 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}