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