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 private BurstFilter(final Level level, final float rate, final long maxBurst, final Result onMatch, 085 final Result onMismatch) { 086 super(onMatch, onMismatch); 087 this.level = level; 088 this.burstInterval = (long) (NANOS_IN_SECONDS * (maxBurst / rate)); 089 for (int i = 0; i < maxBurst; ++i) { 090 available.add(new LogDelay()); 091 } 092 } 093 094 @Override 095 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 096 final Object... params) { 097 return filter(level); 098 } 099 100 @Override 101 public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg, 102 final Throwable t) { 103 return filter(level); 104 } 105 106 @Override 107 public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg, 108 final Throwable t) { 109 return filter(level); 110 } 111 112 @Override 113 public Result filter(final LogEvent event) { 114 return filter(event.getLevel()); 115 } 116 117 /** 118 * Decide if we're going to log <code>event</code> based on whether the 119 * maximum burst of log statements has been exceeded. 120 * 121 * @param level The log level. 122 * @return The onMatch value if the filter passes, onMismatch otherwise. 123 */ 124 private Result filter(final Level level) { 125 if (this.level.isMoreSpecificThan(level)) { 126 LogDelay delay = history.poll(); 127 while (delay != null) { 128 available.add(delay); 129 delay = history.poll(); 130 } 131 delay = available.poll(); 132 if (delay != null) { 133 delay.setDelay(burstInterval); 134 history.add(delay); 135 return onMatch; 136 } 137 return onMismatch; 138 } 139 return onMatch; 140 141 } 142 143 /** 144 * Returns the number of available slots. Used for unit testing. 145 * @return The number of available slots. 146 */ 147 public int getAvailable() { 148 return available.size(); 149 } 150 151 /** 152 * Clear the history. Used for unit testing. 153 */ 154 public void clear() { 155 final Iterator<LogDelay> iter = history.iterator(); 156 while (iter.hasNext()) { 157 final LogDelay delay = iter.next(); 158 history.remove(delay); 159 available.add(delay); 160 } 161 } 162 163 @Override 164 public String toString() { 165 return "level=" + level.toString() + ", interval=" + burstInterval + ", max=" + history.size(); 166 } 167 168 /** 169 * Delay object to represent each log event that has occurred within the timespan. 170 */ 171 private class LogDelay implements Delayed { 172 173 private long expireTime; 174 175 public LogDelay() { 176 } 177 178 public void setDelay(final long delay) { 179 this.expireTime = delay + System.nanoTime(); 180 } 181 182 @Override 183 public long getDelay(final TimeUnit timeUnit) { 184 return timeUnit.convert(expireTime - System.nanoTime(), TimeUnit.NANOSECONDS); 185 } 186 187 @Override 188 public int compareTo(final Delayed delayed) { 189 if (this.expireTime < ((LogDelay) delayed).expireTime) { 190 return -1; 191 } else if (this.expireTime > ((LogDelay) delayed).expireTime) { 192 return 1; 193 } 194 return 0; 195 } 196 197 @Override 198 public boolean equals(final Object o) { 199 if (this == o) { 200 return true; 201 } 202 if (o == null || getClass() != o.getClass()) { 203 return false; 204 } 205 206 final LogDelay logDelay = (LogDelay) o; 207 208 if (expireTime != logDelay.expireTime) { 209 return false; 210 } 211 212 return true; 213 } 214 215 @Override 216 public int hashCode() { 217 return (int) (expireTime ^ (expireTime >>> HASH_SHIFT)); 218 } 219 } 220 221 @PluginBuilderFactory 222 public static Builder newBuilder() { 223 return new Builder(); 224 } 225 226 public static class Builder implements org.apache.logging.log4j.core.util.Builder<BurstFilter> { 227 228 @PluginBuilderAttribute 229 private Level level = Level.WARN; 230 231 @PluginBuilderAttribute 232 private float rate = DEFAULT_RATE; 233 234 @PluginBuilderAttribute 235 private long maxBurst; 236 237 @PluginBuilderAttribute 238 private Result onMatch = Result.NEUTRAL; 239 240 @PluginBuilderAttribute 241 private Result onMismatch = Result.DENY; 242 243 /** 244 * Sets the logging level to use. 245 */ 246 public Builder setLevel(final Level level) { 247 this.level = level; 248 return this; 249 } 250 251 /** 252 * Sets the average number of events per second to allow. This must be a positive number. 253 */ 254 public Builder setRate(final float rate) { 255 this.rate = rate; 256 return this; 257 } 258 259 /** 260 * Sets the maximum number of events that can occur before events are filtered for exceeding the average rate. 261 * The default is 10 times the rate. 262 */ 263 public Builder setMaxBurst(final long maxBurst) { 264 this.maxBurst = maxBurst; 265 return this; 266 } 267 268 /** 269 * Sets the Result to return when the filter matches. Defaults to Result.NEUTRAL. 270 */ 271 public Builder setOnMatch(final Result onMatch) { 272 this.onMatch = onMatch; 273 return this; 274 } 275 276 /** 277 * Sets the Result to return when the filter does not match. The default is Result.DENY. 278 */ 279 public Builder setOnMismatch(final Result onMismatch) { 280 this.onMismatch = onMismatch; 281 return this; 282 } 283 284 @Override 285 public BurstFilter build() { 286 if (this.rate <= 0) { 287 this.rate = DEFAULT_RATE; 288 } 289 if (this.maxBurst <= 0) { 290 this.maxBurst = (long) (this.rate * DEFAULT_RATE_MULTIPLE); 291 } 292 return new BurstFilter(this.level, this.rate, this.maxBurst, this.onMatch, this.onMismatch); 293 } 294 } 295 }