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