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  package org.apache.logging.log4j.core.async;
    18
    19  import java.util.Arrays;
    20  import java.util.List;
    21
    22  import org.apache.logging.log4j.Level;
    23  import org.apache.logging.log4j.LogManager;
    24  import org.apache.logging.log4j.core.Filter;
    25  import org.apache.logging.log4j.core.LogEvent;
    26  import org.apache.logging.log4j.core.config.AppenderRef;
    27  import org.apache.logging.log4j.core.config.Configuration;
    28  import org.apache.logging.log4j.core.config.LoggerConfig;
    29  import org.apache.logging.log4j.core.config.Node;
    30  import org.apache.logging.log4j.core.config.Property;
    31  import org.apache.logging.log4j.core.config.plugins.Plugin;
    32  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
    33  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
    34  import org.apache.logging.log4j.core.config.plugins.PluginElement;
    35  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
    36  import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
    37  import org.apache.logging.log4j.core.util.Booleans;
    38  import org.apache.logging.log4j.util.Strings;
    39
    40  /**
    41   * Asynchronous Logger object that is created via configuration and can be
    42   * combined with synchronous loggers.
    43   * <p>
    44   * AsyncLoggerConfig is a logger designed for high throughput and low latency
    45   * logging. It does not perform any I/O in the calling (application) thread, but
    46   * instead hands off the work to another thread as soon as possible. The actual
    47   * logging is performed in the background thread. It uses the LMAX Disruptor
    48   * library for inter-thread communication. (<a
    49   * href="http://lmax-exchange.github.com/disruptor/"
    50   * >http://lmax-exchange.github.com/disruptor/</a>)
    51   * <p>
    52   * To use AsyncLoggerConfig, specify {@code <asyncLogger>} or
    53   * {@code <asyncRoot>} in configuration.
    54   * <p>
    55   * Note that for performance reasons, this logger does not include source
    56   * location by default. You need to specify {@code includeLocation="true"} in
    57   * the configuration or any %class, %location or %line conversion patterns in
    58   * your log4j.xml configuration will produce either a "?" character or no output
    59   * at all.
    60   * <p>
    61   * For best performance, use AsyncLoggerConfig with the RandomAccessFileAppender or
    62   * RollingRandomAccessFileAppender, with immediateFlush=false. These appenders have
    63   * built-in support for the batching mechanism used by the Disruptor library,
    64   * and they will flush to disk at the end of each batch. This means that even
    65   * with immediateFlush=false, there will never be any items left in the buffer;
    66   * all log events will all be written to disk in a very efficient manner.
    67   */
    68  @Plugin(name = "asyncLogger", category = Node.CATEGORY, printObject = true)
    69  public class AsyncLoggerConfig extends LoggerConfig {
    70
    71      private static final long serialVersionUID = 1L;
    72
    73      private AsyncLoggerConfigHelper helper;
    74
    75      /**
    76       * Default constructor.
    77       */
    78      public AsyncLoggerConfig() {
    79          super();
    80      }
    81
    82      /**
    83       * Constructor that sets the name, level and additive values.
    84       *
    85       * @param name The Logger name.
    86       * @param level The Level.
    87       * @param additive true if the Logger is additive, false otherwise.
    88       */
    89      public AsyncLoggerConfig(final String name, final Level level,
    90              final boolean additive) {
    91          super(name, level, additive);
    92      }
    93
    94      protected AsyncLoggerConfig(final String name,
    95              final List<AppenderRef> appenders, final Filter filter,
    96              final Level level, final boolean additive,
    97              final Property[] properties, final Configuration config,
    98              final boolean includeLocation) {
    99          super(name, appenders, filter, level, additive, properties, config,
    100                 includeLocation);
    101     }
    102
    103     /**
    104      * Passes on the event to a separate thread that will call
    105      * {@link #asyncCallAppenders(LogEvent)}.
    106      */
    107     @Override
    108     protected void callAppenders(final LogEvent event) {
    109         // populate lazily initialized fields
    110         event.getSource();
    111         event.getThreadName();
    112
    113         // pass on the event to a separate thread
    114         if (!helper.callAppendersFromAnotherThread(event)) {
    115             super.callAppenders(event);
    116         }
    117     }
    118
    119     /** Called by AsyncLoggerConfigHelper.RingBufferLog4jEventHandler. */
    120     void asyncCallAppenders(final LogEvent event) {
    121         super.callAppenders(event);
    122     }
    123
    124     private String displayName() {
    125         return LogManager.ROOT_LOGGER_NAME.equals(getName()) ? "root" : getName();
    126     }
    127
    128     @Override
    129     public void start() {
    130         LOGGER.trace("AsyncLoggerConfig[{}] starting...", displayName());
    131         this.setStarting();
    132         if (helper == null) {
    133             helper = new AsyncLoggerConfigHelper(this);
    134         } else {
    135             AsyncLoggerConfigHelper.claim(); // LOG4J2-336
    136         }
    137         super.start();
    138     }
    139
    140     @Override
    141     public void stop() {
    142         LOGGER.trace("AsyncLoggerConfig[{}] stopping...", displayName());
    143         this.setStopping();
    144         AsyncLoggerConfigHelper.release();
    145         super.stop();
    146     }
    147
    148     /**
    149      * Creates and returns a new {@code RingBufferAdmin} that instruments the
    150      * ringbuffer of this {@code AsyncLoggerConfig}.
    151      *
    152      * @param contextName name of the {@code LoggerContext}
    153      */
    154     public RingBufferAdmin createRingBufferAdmin(final String contextName) {
    155         return helper.createRingBufferAdmin(contextName, getName());
    156     }
    157
    158     /**
    159      * Factory method to create a LoggerConfig.
    160      *
    161      * @param additivity True if additive, false otherwise.
    162      * @param levelName The Level to be associated with the Logger.
    163      * @param loggerName The name of the Logger.
    164      * @param includeLocation "true" if location should be passed downstream
    165      * @param refs An array of Appender names.
    166      * @param properties Properties to pass to the Logger.
    167      * @param config The Configuration.
    168      * @param filter A Filter.
    169      * @return A new LoggerConfig.
    170      */
    171     @PluginFactory
    172     public static LoggerConfig createLogger(
    173             @PluginAttribute("additivity") final String additivity,
    174             @PluginAttribute("level") final String levelName,
    175             @PluginAttribute("name") final String loggerName,
    176             @PluginAttribute("includeLocation") final String includeLocation,
    177             @PluginElement("AppenderRef") final AppenderRef[] refs,
    178             @PluginElement("Properties") final Property[] properties,
    179             @PluginConfiguration final Configuration config,
    180             @PluginElement("Filter") final Filter filter) {
    181         if (loggerName == null) {
    182             LOGGER.error("Loggers cannot be configured without a name");
    183             return null;
    184         }
    185
    186         final List<AppenderRef> appenderRefs = Arrays.asList(refs);
    187         Level level;
    188         try {
    189             level = Level.toLevel(levelName, Level.ERROR);
    190         } catch (final Exception ex) {
    191             LOGGER.error(
    192                     "Invalid Log level specified: {}. Defaulting to Error",
    193                     levelName);
    194             level = Level.ERROR;
    195         }
    196         final String name = loggerName.equals("root") ? Strings.EMPTY : loggerName;
    197         final boolean additive = Booleans.parseBoolean(additivity, true);
    198
    199         return new AsyncLoggerConfig(name, appenderRefs, filter, level,
    200                 additive, properties, config, includeLocation(includeLocation));
    201     }
    202
    203     // Note: for asynchronous loggers, includeLocation default is FALSE
    204     protected static boolean includeLocation(final String includeLocationConfigValue) {
    205         return Boolean.parseBoolean(includeLocationConfigValue);
    206     }
    207
    208     /**
    209      * An asynchronous root Logger.
    210      */
    211     @Plugin(name = "asyncRoot", category = "Core", printObject = true)
    212     public static class RootLogger extends LoggerConfig {
    213
    214         private static final long serialVersionUID = 1L;
    215
    216         @PluginFactory
    217         public static LoggerConfig createLogger(
    218                 @PluginAttribute("additivity") final String additivity,
    219                 @PluginAttribute("level") final String levelName,
    220                 @PluginAttribute("includeLocation") final String includeLocation,
    221                 @PluginElement("AppenderRef") final AppenderRef[] refs,
    222                 @PluginElement("Properties") final Property[] properties,
    223                 @PluginConfiguration final Configuration config,
    224                 @PluginElement("Filter") final Filter filter) {
    225             final List<AppenderRef> appenderRefs = Arrays.asList(refs);
    226             Level level;
    227             try {
    228                 level = Level.toLevel(levelName, Level.ERROR);
    229             } catch (final Exception ex) {
    230                 LOGGER.error(
    231                         "Invalid Log level specified: {}. Defaulting to Error",
    232                         levelName);
    233                 level = Level.ERROR;
    234             }
    235             final boolean additive = Booleans.parseBoolean(additivity, true);
    236
    237             return new AsyncLoggerConfig(LogManager.ROOT_LOGGER_NAME,
    238                     appenderRefs, filter, level, additive, properties, config,
    239                     AsyncLoggerConfig.includeLocation(includeLocation));
    240         }
    241     }
    242 }