View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17
    18  package org.apache.logging.log4j.core.filter;
    19
    20  import java.util.Iterator;
    21  import java.util.Queue;
    22  import java.util.concurrent.ConcurrentLinkedQueue;
    23  import java.util.concurrent.DelayQueue;
    24  import java.util.concurrent.Delayed;
    25  import java.util.concurrent.TimeUnit;
    26
    27  import org.apache.logging.log4j.Level;
    28  import org.apache.logging.log4j.Marker;
    29  import org.apache.logging.log4j.core.Filter;
    30  import org.apache.logging.log4j.core.LogEvent;
    31  import org.apache.logging.log4j.core.Logger;
    32  import org.apache.logging.log4j.core.config.Node;
    33  import org.apache.logging.log4j.core.config.plugins.Plugin;
    34  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
    35  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
    36  import org.apache.logging.log4j.message.Message;
    37
    38  /**
    39   * The <code>BurstFilter</code> is a logging filter that regulates logging traffic.
    40   *
    41   * <p>
    42   * Use this filter when you want to control the maximum burst of log statements that can be sent to an appender. The
    43   * filter is configured in the log4j configuration file. For example, the following configuration limits the number of
    44   * INFO level (as well as DEBUG and TRACE) log statements that can be sent to the console to a burst of 100 with an
    45   * average rate of 16 per second. WARN, ERROR and FATAL messages would continue to be delivered.
    46   * </p>
    47   * <code>
    48   * &lt;Console name="console"&gt;<br>
    49   * &nbsp;&lt;PatternLayout pattern="%-5p %d{dd-MMM-yyyy HH:mm:ss} %x %t %m%n"/&gt;<br>
    50   * &nbsp;&lt;filters&gt;<br>
    51   * &nbsp;&nbsp;&lt;Burst level="INFO" rate="16" maxBurst="100"/&gt;<br>
    52   * &nbsp;&lt;/filters&gt;<br>
    53   * &lt;/Console&gt;<br>
    54   * </code><br>
    55   */
    56
    57  @Plugin(name = "BurstFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
    58  public final class BurstFilter extends AbstractFilter {
    59
    60      private static final long serialVersionUID = 1L;
    61
    62      private static final long NANOS_IN_SECONDS = 1000000000;
    63
    64      private static final int DEFAULT_RATE = 10;
    65
    66      private static final int DEFAULT_RATE_MULTIPLE = 100;
    67
    68      private static final int HASH_SHIFT = 32;
    69
    70      /**
    71       * Level of messages to be filtered. Anything at or below this level will be
    72       * filtered out if <code>maxBurst</code> has been exceeded. The default is
    73       * WARN meaning any messages that are higher than warn will be logged
    74       * regardless of the size of a burst.
    75       */
    76      private final Level level;
    77
    78      private final long burstInterval;
    79
    80      private final DelayQueue<LogDelay> history = new DelayQueue<LogDelay>();
    81
    82      private final Queue<LogDelay> available = new ConcurrentLinkedQueue<LogDelay>();
    83
    84      static LogDelay createLogDelay(long expireTime) {
    85          return new LogDelay(expireTime);
    86      }
    87
    88      private BurstFilter(final Level level, final float rate, final long maxBurst, final Result onMatch,
    89                          final Result onMismatch) {
    90          super(onMatch, onMismatch);
    91          this.level = level;
    92          this.burstInterval = (long) (NANOS_IN_SECONDS * (maxBurst / rate));
    93          for (int i = 0; i < maxBurst; ++i) {
    94              available.add(createLogDelay(0));
    95          }
    96      }
    97
    98      @Override
    99      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 }