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.util.ArrayList;
020    import java.util.Iterator;
021    import java.util.List;
022    import java.util.Map;
023    
024    import org.apache.logging.log4j.Level;
025    import org.apache.logging.log4j.Marker;
026    import org.apache.logging.log4j.core.config.Configuration;
027    import org.apache.logging.log4j.core.config.LoggerConfig;
028    import org.apache.logging.log4j.core.filter.CompositeFilter;
029    import org.apache.logging.log4j.message.Message;
030    import org.apache.logging.log4j.message.MessageFactory;
031    import org.apache.logging.log4j.message.SimpleMessage;
032    import org.apache.logging.log4j.spi.AbstractLogger;
033    
034    /**
035     * @doubt All the isEnabled methods could be pushed into a filter interface.  Not sure of the utility of having
036     * isEnabled be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of
037     * Logger noticeably impacts performance. The message pattern and parameters are required so that they can be
038     * used in global filters.
039     */
040    public class Logger extends AbstractLogger {
041    
042        /**
043         * config should be consistent across threads.
044         */
045        protected volatile PrivateConfig config;
046    
047        private final LoggerContext context;
048    
049        /**
050         * The constructor.
051         * @param context The LoggerContext this Logger is associated with.
052         * @param messageFactory The message factory.
053         * @param name The name of the Logger.
054         */
055        protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
056            super(name, messageFactory);
057            this.context = context;
058            config = new PrivateConfig(context.getConfiguration(), this);
059        }
060    
061        /**
062         * Returns the parent of this Logger. If it doesn't already exist return a temporary Logger.
063         * @return The parent Logger.
064         */
065        public Logger getParent() {
066            final LoggerConfig lc = config.loggerConfig.getParent();
067            if (lc == null) {
068                return null;
069            }
070            if (context.hasLogger(lc.getName())) {
071                return context.getLogger(getName(), getMessageFactory());
072            }
073            return new Logger(context, getName(), this.getMessageFactory());
074        }
075    
076        /**
077         * Returns the LoggerContext this Logger is associated with.
078         * @return the LoggerContext.
079         */
080        public LoggerContext getContext() {
081            return context;
082        }
083    
084        /**
085         * This method is not exposed through the public API and is provided primarily for unit testing.
086         * @param level The Level to use on this Logger.
087         */
088        public synchronized void setLevel(final Level level) {
089            if (level != null) {
090                config = new PrivateConfig(config, level);
091            }
092        }
093    
094        /**
095         * Returns the Level associated with the Logger.
096         * @return the Level associate with the Logger.
097         */
098        public Level getLevel() {
099            return config.level;
100        }
101    
102        @Override
103        public void log(final Marker marker, final String fqcn, final Level level, Message data, final Throwable t) {
104            if (data == null) {
105                data = new SimpleMessage("");
106            }
107            config.config.getConfigurationMonitor().checkConfiguration();
108            config.loggerConfig.log(getName(), marker, fqcn, level, data, t);
109        }
110    
111        @Override
112        public boolean isEnabled(final Level level, final Marker marker, final String msg) {
113            return config.filter(level, marker, msg);
114        }
115    
116        @Override
117        public boolean isEnabled(final Level level, final Marker marker, final String msg, final Throwable t) {
118            return config.filter(level, marker, msg, t);
119        }
120    
121        @Override
122        public boolean isEnabled(final Level level, final Marker marker, final String msg, final Object... p1) {
123            return config.filter(level, marker, msg, p1);
124        }
125    
126        @Override
127        public boolean isEnabled(final Level level, final Marker marker, final Object msg, final Throwable t) {
128            return config.filter(level, marker, msg, t);
129        }
130    
131        @Override
132        public boolean isEnabled(final Level level, final Marker marker, final Message msg, final Throwable t) {
133            return config.filter(level, marker, msg, t);
134        }
135    
136        /**
137         * This method is not exposed through the public API and is used primarily for unit testing.
138         * @param appender The Appender to add to the Logger.
139         */
140        public void addAppender(final Appender<?> appender) {
141            config.config.addLoggerAppender(this, appender);
142        }
143    
144        /**
145         * This method is not exposed through the public API and is used primarily for unit testing.
146         * @param appender The Appender to remove from the Logger.
147         */
148        public void removeAppender(final Appender<?> appender) {
149            config.loggerConfig.removeAppender(appender.getName());
150        }
151    
152        /**
153         * This method is not exposed through the public API and is used primarily for unit testing.
154         * @return A Map containing the Appender's name as the key and the Appender as the value.
155         */
156        public Map<String, Appender<?>> getAppenders() {
157             return config.loggerConfig.getAppenders();
158        }
159    
160        /**
161         * This method is not exposed through the public API and is used primarily for unit testing.
162         * @return An Iterator over all the Filters associated with the Logger.
163         */
164        public Iterator<Filter> getFilters() {
165            final Filter filter = config.loggerConfig.getFilter();
166            if (filter == null) {
167                return new ArrayList<Filter>().iterator();
168            } else if (filter instanceof CompositeFilter) {
169                return ((CompositeFilter) filter).iterator();
170            } else {
171                final List<Filter> filters = new ArrayList<Filter>();
172                filters.add(filter);
173                return filters.iterator();
174            }
175        }
176    
177        /**
178         * This method is not exposed through the public API and is used primarily for unit testing.
179         * @return The number of Filters associated with the Logger.
180         */
181        public int filterCount() {
182            final Filter filter = config.loggerConfig.getFilter();
183            if (filter == null) {
184                return 0;
185            } else if (filter instanceof CompositeFilter) {
186                return ((CompositeFilter) filter).size();
187            }
188            return 1;
189        }
190    
191        /**
192         * This method is not exposed through the public API and is used primarily for unit testing.
193         * @param filter The Filter to add.
194         */
195        public void addFilter(final Filter filter) {
196            config.config.addLoggerFilter(this, filter);
197        }
198    
199        /**
200         * This method is not exposed through the public API and is present only to support the Log4j 1.2
201         * compatibility bridge.
202         * @return true if the associated LoggerConfig is additive, false otherwise.
203         */
204        public boolean isAdditive() {
205            return config.loggerConfig.isAdditive();
206        }
207    
208        /**
209         * This method is not exposed through the public API and is present only to support the Log4j 1.2
210         * compatibility bridge.
211         * @param additive Boolean value to indicate whether the Logger is additive or not.
212         */
213        public void setAdditive(final boolean additive) {
214            config.config.setLoggerAdditive(this, additive);
215        }
216    
217        /**
218         * Associates the Logger with a new Configuration. This method is not exposed through the
219         * public API.
220         *
221         * There are two ways that could be used to guarantee all threads are aware of changes to
222         * config. 1. synchronize this method. Accessors don't need to be synchronized as Java will
223         * treat all variables within a synchronized block as volatile. 2. Declare the variable
224         * volatile. Option 2 is used here as the performance cost is very low and it does a better
225         * job at documenting how it is used.
226         *
227         * @param config The new Configuration.
228         */
229        void updateConfiguration(final Configuration config) {
230            this.config = new PrivateConfig(config, this);
231        }
232    
233        /**
234         * The binding between a Logger and its configuration.
235         */
236        protected class PrivateConfig {
237            // config fields are public to make them visible to Logger subclasses
238            public final LoggerConfig loggerConfig;
239            public final Configuration config;
240            private final Level level;
241            private final int intLevel;
242            private final Logger logger;
243    
244            public PrivateConfig(final Configuration config, final Logger logger) {
245                this.config = config;
246                this.loggerConfig = config.getLoggerConfig(getName());
247                this.level = this.loggerConfig.getLevel();
248                this.intLevel = this.level.intLevel();
249                this.logger = logger;
250            }
251    
252            public PrivateConfig(final PrivateConfig pc, final Level level) {
253                this.config = pc.config;
254                this.loggerConfig = pc.loggerConfig;
255                this.level = level;
256                this.intLevel = this.level.intLevel();
257                this.logger = pc.logger;
258            }
259    
260            public PrivateConfig(final PrivateConfig pc, final LoggerConfig lc) {
261                this.config = pc.config;
262                this.loggerConfig = lc;
263                this.level = lc.getLevel();
264                this.intLevel = this.level.intLevel();
265                this.logger = pc.logger;
266            }
267    
268            // LOG4J2-151: changed visibility to public
269            public void logEvent(LogEvent event) {
270                config.getConfigurationMonitor().checkConfiguration();
271                loggerConfig.log(event);
272            }
273    
274            boolean filter(final Level level, final Marker marker, final String msg) {
275                config.getConfigurationMonitor().checkConfiguration();
276                final Filter filter = config.getFilter();
277                if (filter != null) {
278                    final Filter.Result r = filter.filter(logger, level, marker, msg);
279                    if (r != Filter.Result.NEUTRAL) {
280                        return r == Filter.Result.ACCEPT;
281                    }
282                }
283    
284                return intLevel >= level.intLevel();
285            }
286    
287            boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) {
288                config.getConfigurationMonitor().checkConfiguration();
289                final Filter filter = config.getFilter();
290                if (filter != null) {
291                    final Filter.Result r = filter.filter(logger, level, marker, msg, t);
292                    if (r != Filter.Result.NEUTRAL) {
293                        return r == Filter.Result.ACCEPT;
294                    }
295                }
296    
297                return intLevel >= level.intLevel();
298            }
299    
300            boolean filter(final Level level, final Marker marker, final String msg, final Object... p1) {
301                config.getConfigurationMonitor().checkConfiguration();
302                final Filter filter = config.getFilter();
303                if (filter != null) {
304                    final Filter.Result r = filter.filter(logger, level, marker, msg, p1);
305                    if (r != Filter.Result.NEUTRAL) {
306                        return r == Filter.Result.ACCEPT;
307                    }
308                }
309    
310                return intLevel >= level.intLevel();
311            }
312    
313            boolean filter(final Level level, final Marker marker, final Object msg, final Throwable t) {
314                config.getConfigurationMonitor().checkConfiguration();
315                final Filter filter = config.getFilter();
316                if (filter != null) {
317                    final Filter.Result r = filter.filter(logger, level, marker, msg, t);
318                    if (r != Filter.Result.NEUTRAL) {
319                        return r == Filter.Result.ACCEPT;
320                    }
321                }
322    
323                return intLevel >= level.intLevel();
324            }
325    
326            boolean filter(final Level level, final Marker marker, final Message msg, final Throwable t) {
327                config.getConfigurationMonitor().checkConfiguration();
328                final Filter filter = config.getFilter();
329                if (filter != null) {
330                    final Filter.Result r = filter.filter(logger, level, marker, msg, t);
331                    if (r != Filter.Result.NEUTRAL) {
332                        return r == Filter.Result.ACCEPT;
333                    }
334                }
335    
336                return intLevel >= level.intLevel();
337            }
338        }
339    
340        /**
341         * Returns a String representation of this instance in the form {@code "name:level[ in context_name]"}.
342         * @return A String describing this Logger instance.
343         */
344        @Override
345        public String toString() {
346            final String nameLevel = "" + getName() + ":" + getLevel();
347            if (context == null) {
348                return nameLevel;
349            }
350            final String contextName = context.getName();
351            return contextName == null ? nameLevel : nameLevel + " in " + contextName;
352        }
353    }