    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    package org.apache.logging.log4j.core;
    018
    019    import java.io.Serializable;
    020    import java.util.ArrayList;
    021    import java.util.Iterator;
    022    import java.util.List;
    023    import java.util.Map;
    024
    025    import org.apache.logging.log4j.Level;
    026    import org.apache.logging.log4j.Marker;
    027    import org.apache.logging.log4j.core.config.Configuration;
    028    import org.apache.logging.log4j.core.config.LoggerConfig;
    029    import org.apache.logging.log4j.core.filter.CompositeFilter;
    030    import org.apache.logging.log4j.message.Message;
    031    import org.apache.logging.log4j.message.MessageFactory;
    032    import org.apache.logging.log4j.message.SimpleMessage;
    033    import org.apache.logging.log4j.spi.AbstractLogger;
    034    import org.apache.logging.log4j.util.Strings;
    035
    036    /**
    037     * The core implementation of the {@link org.apache.logging.log4j.Logger} interface. Besides providing an
    038     * implementation of all the Logger methods, this class also provides some convenience methods for Log4j 1.x
    039     * compatibility as well as access to the {@link org.apache.logging.log4j.core.Filter Filters} and
    040     * {@link org.apache.logging.log4j.core.Appender Appenders} associated with this Logger. Note that access to these
    041     * underlying objects is provided primarily for use in unit tests or bridging legacy Log4j 1.x code. Future versions
    042     * of this class may or may not include the various methods that are noted as not being part of the public API.
    043     *
    044     * TODO All the isEnabled methods could be pushed into a filter interface.  Not sure of the utility of having
    045     * isEnabled be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of
    046     * Logger noticeably impacts performance. The message pattern and parameters are required so that they can be
    047     * used in global filters.
    048     */
    049    public class Logger extends AbstractLogger {
    050
    051        private static final long serialVersionUID = 1L;
    052
    053        /**
    054         * Config should be consistent across threads.
    055         */
    056        protected volatile PrivateConfig config;
    057
    058        // FIXME: ditto to the above
    059        private final LoggerContext context;
    060
    061        /**
    062         * The constructor.
    063         * @param context The LoggerContext this Logger is associated with.
    064         * @param messageFactory The message factory.
    065         * @param name The name of the Logger.
    066         */
    067        protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
    068            super(name, messageFactory);
    069            this.context = context;
    070            config = new PrivateConfig(context.getConfiguration(), this);
    071        }
    072
    073        /**
    074         * This method is only used for 1.x compatibility.
    075         * Returns the parent of this Logger. If it doesn't already exist return a temporary Logger.
    076         * @return The parent Logger.
    077         */
    078        public Logger getParent() {
    079            final LoggerConfig lc = config.loggerConfig.getName().equals(getName()) ? config.loggerConfig.getParent() :
    080                config.loggerConfig;
    081            if (lc == null) {
    082                return null;
    083            }
    084            if (context.hasLogger(lc.getName())) {
    085                return context.getLogger(lc.getName(), getMessageFactory());
    086            }
    087            return new Logger(context, lc.getName(), this.getMessageFactory());
    088        }
    089
    090        /**
    091         * Returns the LoggerContext this Logger is associated with.
    092         * @return the LoggerContext.
    093         */
    094        public LoggerContext getContext() {
    095            return context;
    096        }
    097
    098        /**
    099         * 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    }