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