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 * <Burst 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 /** 119 * Decide if we're going to log <code>event</code> based on whether the 120 * maximum burst of log statements has been exceeded. 121 * 122 * @param level The log level. 123 * @return The onMatch value if the filter passes, onMismatch otherwise. 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 * Returns the number of available slots. Used for unit testing. 146 * @return The number of available slots. 147 */ 148 public int getAvailable() { 149 return available.size(); 150 } 151 152 /** 153 * Clear the history. Used for unit testing. 154 */ 155 public void clear() { 156 for (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 * Delay object to represent each log event that has occurred within the timespan. 169 * 170 * Consider this class private, package visibility for testing. 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 * Sets the logging level to use. 243 */ 244 public Builder setLevel(final Level level) { 245 this.level = level; 246 return this; 247 } 248 249 /** 250 * Sets the average number of events per second to allow. This must be a positive number. 251 */ 252 public Builder setRate(final float rate) { 253 this.rate = rate; 254 return this; 255 } 256 257 /** 258 * Sets the maximum number of events that can occur before events are filtered for exceeding the average rate. 259 * The default is 10 times the rate. 260 */ 261 public Builder setMaxBurst(final long maxBurst) { 262 this.maxBurst = maxBurst; 263 return this; 264 } 265 266 /** 267 * Sets the Result to return when the filter matches. Defaults to Result.NEUTRAL. 268 */ 269 public Builder setOnMatch(final Result onMatch) { 270 this.onMatch = onMatch; 271 return this; 272 } 273 274 /** 275 * Sets the Result to return when the filter does not match. The default is Result.DENY. 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}