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;
    18
    19  import java.io.Serializable;
    20  import java.util.ArrayList;
    21  import java.util.Iterator;
    22  import java.util.List;
    23  import java.util.Map;
    24
    25  import org.apache.logging.log4j.Level;
    26  import org.apache.logging.log4j.Marker;
    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.filter.CompositeFilter;
    30  import org.apache.logging.log4j.message.Message;
    31  import org.apache.logging.log4j.message.MessageFactory;
    32  import org.apache.logging.log4j.message.SimpleMessage;
    33  import org.apache.logging.log4j.spi.AbstractLogger;
    34  import org.apache.logging.log4j.util.Strings;
    35
    36  /**
    37   * The core implementation of the {@link org.apache.logging.log4j.Logger} interface. Besides providing an
    38   * implementation of all the Logger methods, this class also provides some convenience methods for Log4j 1.x
    39   * compatibility as well as access to the {@link org.apache.logging.log4j.core.Filter Filters} and
    40   * {@link org.apache.logging.log4j.core.Appender Appenders} associated with this Logger. Note that access to these
    41   * underlying objects is provided primarily for use in unit tests or bridging legacy Log4j 1.x code. Future versions
    42   * of this class may or may not include the various methods that are noted as not being part of the public API.
    43   *
    44   * TODO All the isEnabled methods could be pushed into a filter interface.  Not sure of the utility of having
    45   * isEnabled be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of
    46   * Logger noticeably impacts performance. The message pattern and parameters are required so that they can be
    47   * used in global filters.
    48   */
    49  public class Logger extends AbstractLogger {
    50
    51      private static final long serialVersionUID = 1L;
    52
    53      /**
    54       * Config should be consistent across threads.
    55       */
    56      protected volatile PrivateConfig config;
    57
    58      // FIXME: ditto to the above
    59      private final LoggerContext context;
    60
    61      /**
    62       * The constructor.
    63       * @param context The LoggerContext this Logger is associated with.
    64       * @param messageFactory The message factory.
    65       * @param name The name of the Logger.
    66       */
    67      protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
    68          super(name, messageFactory);
    69          this.context = context;
    70          config = new PrivateConfig(context.getConfiguration(), this);
    71      }
    72
    73      /**
    74       * This method is only used for 1.x compatibility.
    75       * Returns the parent of this Logger. If it doesn't already exist return a temporary Logger.
    76       * @return The parent Logger.
    77       */
    78      public Logger getParent() {
    79          final LoggerConfig lc = config.loggerConfig.getName().equals(getName()) ? config.loggerConfig.getParent() :
    80              config.loggerConfig;
    81          if (lc == null) {
    82              return null;
    83          }
    84          if (context.hasLogger(lc.getName())) {
    85              return context.getLogger(lc.getName(), getMessageFactory());
    86          }
    87          return new Logger(context, lc.getName(), this.getMessageFactory());
    88      }
    89
    90      /**
    91       * Returns the LoggerContext this Logger is associated with.
    92       * @return the LoggerContext.
    93       */
    94      public LoggerContext getContext() {
    95          return context;
    96      }
    97
    98      /**
    99       * This method is not exposed through the public API and is provided primarily for unit testing.
    100      * @param level The Level to use on this Logger.
    101      */
    102     public synchronized void setLevel(final Level level) {
    103         if (level != null) {
    104             config = new PrivateConfig(config, level);
    105         }
    106     }
    107
    108     @Override
    109     public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) {
    110         final Message msg = message == null ? new SimpleMessage(Strings.EMPTY) : message;
    111         config.config.getConfigurationMonitor().checkConfiguration();
    112         config.loggerConfig.log(getName(), fqcn, marker, level, msg, t);
    113     }
    114
    115     @Override
    116     public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
    117         return config.filter(level, marker, message, t);
    118     }
    119
    120     @Override
    121     public boolean isEnabled(final Level level, final Marker marker, final String message) {
    122         return config.filter(level, marker, message);
    123     }
    124
    125     @Override
    126     public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {
    127         return config.filter(level, marker, message, params);
    128     }
    129
    130     @Override
    131     public boolean isEnabled(final Level level, final Marker marker, final Object message, final Throwable t) {
    132         return config.filter(level, marker, message, t);
    133     }
    134
    135     @Override
    136     public boolean isEnabled(final Level level, final Marker marker, final Message message, final Throwable t) {
    137         return config.filter(level, marker, message, t);
    138     }
    139
    140     /**
    141      * This method is not exposed through the public API and is used primarily for unit testing.
    142      * @param appender The Appender to add to the Logger.
    143      */
    144     public void addAppender(final Appender appender) {
    145         config.config.addLoggerAppender(this, appender);
    146     }
    147
    148     /**
    149      * This method is not exposed through the public API and is used primarily for unit testing.
    150      * @param appender The Appender to remove from the Logger.
    151      */
    152     public void removeAppender(final Appender appender) {
    153         config.loggerConfig.removeAppender(appender.getName());
    154     }
    155
    156     /**
    157      * This method is not exposed through the public API and is used primarily for unit testing.
    158      * @return A Map containing the Appender's name as the key and the Appender as the value.
    159      */
    160     public Map<String, Appender> getAppenders() {
    161          return config.loggerConfig.getAppenders();
    162     }
    163
    164     /**
    165      * This method is not exposed through the public API and is used primarily for unit testing.
    166      * @return An Iterator over all the Filters associated with the Logger.
    167      */
    168     // FIXME: this really ought to be an Iterable instead of an Iterator
    169     public Iterator<Filter> getFilters() {
    170         final Filter filter = config.loggerConfig.getFilter();
    171         if (filter == null) {
    172             return new ArrayList<Filter>().iterator();
    173         } else if (filter instanceof CompositeFilter) {
    174             return ((CompositeFilter) filter).iterator();
    175         } else {
    176             final List<Filter> filters = new ArrayList<Filter>();
    177             filters.add(filter);
    178             return filters.iterator();
    179         }
    180     }
    181
    182     /**
    183      * Gets the Level associated with the Logger.
    184      *
    185      * @return the Level associate with the Logger.
    186      */
    187     @Override
    188     public Level getLevel() {
    189         return config.level;
    190     }
    191
    192     /**
    193      * This method is not exposed through the public API and is used primarily for unit testing.
    194      * @return The number of Filters associated with the Logger.
    195      */
    196     public int filterCount() {
    197         final Filter filter = config.loggerConfig.getFilter();
    198         if (filter == null) {
    199             return 0;
    200         } else if (filter instanceof CompositeFilter) {
    201             return ((CompositeFilter) filter).size();
    202         }
    203         return 1;
    204     }
    205
    206     /**
    207      * This method is not exposed through the public API and is used primarily for unit testing.
    208      * @param filter The Filter to add.
    209      */
    210     public void addFilter(final Filter filter) {
    211         config.config.addLoggerFilter(this, filter);
    212     }
    213
    214     /**
    215      * This method is not exposed through the public API and is present only to support the Log4j 1.2
    216      * compatibility bridge.
    217      * @return true if the associated LoggerConfig is additive, false otherwise.
    218      */
    219     public boolean isAdditive() {
    220         return config.loggerConfig.isAdditive();
    221     }
    222
    223     /**
    224      * This method is not exposed through the public API and is present only to support the Log4j 1.2
    225      * compatibility bridge.
    226      * @param additive Boolean value to indicate whether the Logger is additive or not.
    227      */
    228     public void setAdditive(final boolean additive) {
    229         config.config.setLoggerAdditive(this, additive);
    230     }
    231
    232     /**
    233      * Associates the Logger with a new Configuration. This method is not exposed through the
    234      * public API.
    235      *
    236      * There are two ways that could be used to guarantee all threads are aware of changes to
    237      * config. 1. synchronize this method. Accessors don't need to be synchronized as Java will
    238      * treat all variables within a synchronized block as volatile. 2. Declare the variable
    239      * volatile. Option 2 is used here as the performance cost is very low and it does a better
    240      * job at documenting how it is used.
    241      *
    242      * @param newConfig The new Configuration.
    243      */
    244     protected void updateConfiguration(final Configuration newConfig) {
    245         this.config = new PrivateConfig(newConfig, this);
    246     }
    247
    248     /**
    249      * The binding between a Logger and its configuration.
    250      */
    251     // TODO: Should not be Serializable per EJ item 74 (2nd Ed)?
    252     protected class PrivateConfig implements Serializable {
    253         private static final long serialVersionUID = 1L;
    254         // config fields are public to make them visible to Logger subclasses
    255         public final LoggerConfig loggerConfig;
    256         public final Configuration config;
    257         private final Level level;
    258         private final int intLevel;
    259         private final Logger logger;
    260
    261         public PrivateConfig(final Configuration config, final Logger logger) {
    262             this.config = config;
    263             this.loggerConfig = config.getLoggerConfig(getName());
    264             this.level = this.loggerConfig.getLevel();
    265             this.intLevel = this.level.intLevel();
    266             this.logger = logger;
    267         }
    268
    269         public PrivateConfig(final PrivateConfig pc, final Level level) {
    270             this.config = pc.config;
    271             this.loggerConfig = pc.loggerConfig;
    272             this.level = level;
    273             this.intLevel = this.level.intLevel();
    274             this.logger = pc.logger;
    275         }
    276
    277         public PrivateConfig(final PrivateConfig pc, final LoggerConfig lc) {
    278             this.config = pc.config;
    279             this.loggerConfig = lc;
    280             this.level = lc.getLevel();
    281             this.intLevel = this.level.intLevel();
    282             this.logger = pc.logger;
    283         }
    284
    285         // LOG4J2-151: changed visibility to public
    286         public void logEvent(final LogEvent event) {
    287             config.getConfigurationMonitor().checkConfiguration();
    288             loggerConfig.log(event);
    289         }
    290
    291         boolean filter(final Level level, final Marker marker, final String msg) {
    292             config.getConfigurationMonitor().checkConfiguration();
    293             final Filter filter = config.getFilter();
    294             if (filter != null) {
    295                 final Filter.Result r = filter.filter(logger, level, marker, msg);
    296                 if (r != Filter.Result.NEUTRAL) {
    297                     return r == Filter.Result.ACCEPT;
    298                 }
    299             }
    300             return level != null && intLevel >= level.intLevel();
    301         }
    302
    303         boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) {
    304             config.getConfigurationMonitor().checkConfiguration();
    305             final Filter filter = config.getFilter();
    306             if (filter != null) {
    307                 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
    308                 if (r != Filter.Result.NEUTRAL) {
    309                     return r == Filter.Result.ACCEPT;
    310                 }
    311             }
    312             return level != null && intLevel >= level.intLevel();
    313         }
    314
    315         boolean filter(final Level level, final Marker marker, final String msg, final Object... p1) {
    316             config.getConfigurationMonitor().checkConfiguration();
    317             final Filter filter = config.getFilter();
    318             if (filter != null) {
    319                 final Filter.Result r = filter.filter(logger, level, marker, msg, p1);
    320                 if (r != Filter.Result.NEUTRAL) {
    321                     return r == Filter.Result.ACCEPT;
    322                 }
    323             }
    324             return level != null && intLevel >= level.intLevel();
    325         }
    326
    327         boolean filter(final Level level, final Marker marker, final Object msg, final Throwable t) {
    328             config.getConfigurationMonitor().checkConfiguration();
    329             final Filter filter = config.getFilter();
    330             if (filter != null) {
    331                 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
    332                 if (r != Filter.Result.NEUTRAL) {
    333                     return r == Filter.Result.ACCEPT;
    334                 }
    335             }
    336             return level != null && intLevel >= level.intLevel();
    337         }
    338
    339         boolean filter(final Level level, final Marker marker, final Message msg, final Throwable t) {
    340             config.getConfigurationMonitor().checkConfiguration();
    341             final Filter filter = config.getFilter();
    342             if (filter != null) {
    343                 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
    344                 if (r != Filter.Result.NEUTRAL) {
    345                     return r == Filter.Result.ACCEPT;
    346                 }
    347             }
    348             return level != null && intLevel >= level.intLevel();
    349         }
    350     }
    351
    352     /**
    353      * Returns a String representation of this instance in the form {@code "name:level[ in context_name]"}.
    354      * @return A String describing this Logger instance.
    355      */
    356     @Override
    357     public String toString() {
    358         final String nameLevel = Strings.EMPTY + getName() + ':' + getLevel();
    359         if (context == null) {
    360             return nameLevel;
    361         }
    362         final String contextName = context.getName();
    363         return contextName == null ? nameLevel : nameLevel + " in " + contextName;
    364     }
    365 }