View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core;
18  
19  import java.io.Serializable;
20  import java.util.ArrayList;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.apache.logging.log4j.Level;
26  import org.apache.logging.log4j.Marker;
27  import org.apache.logging.log4j.core.config.Configuration;
28  import org.apache.logging.log4j.core.config.LoggerConfig;
29  import org.apache.logging.log4j.core.config.ReliabilityStrategy;
30  import org.apache.logging.log4j.core.filter.CompositeFilter;
31  import org.apache.logging.log4j.message.Message;
32  import org.apache.logging.log4j.message.MessageFactory;
33  import org.apache.logging.log4j.message.SimpleMessage;
34  import org.apache.logging.log4j.spi.AbstractLogger;
35  import org.apache.logging.log4j.util.Strings;
36  import org.apache.logging.log4j.util.Supplier;
37  
38  /**
39   * The core implementation of the {@link org.apache.logging.log4j.Logger} interface. Besides providing an
40   * implementation of all the Logger methods, this class also provides some convenience methods for Log4j 1.x
41   * compatibility as well as access to the {@link org.apache.logging.log4j.core.Filter Filters} and
42   * {@link org.apache.logging.log4j.core.Appender Appenders} associated with this Logger. Note that access to these
43   * underlying objects is provided primarily for use in unit tests or bridging legacy Log4j 1.x code. Future versions
44   * of this class may or may not include the various methods that are noted as not being part of the public API.
45   *
46   * TODO All the isEnabled methods could be pushed into a filter interface.  Not sure of the utility of having
47   * isEnabled be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of
48   * Logger noticeably impacts performance. The message pattern and parameters are required so that they can be
49   * used in global filters.
50   */
51  public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
52  
53      private static final long serialVersionUID = 1L;
54  
55      /**
56       * Config should be consistent across threads.
57       */
58      protected volatile PrivateConfig privateConfig;
59  
60      // FIXME: ditto to the above
61      private final LoggerContext context;
62  
63      /**
64       * The constructor.
65       * @param context The LoggerContext this Logger is associated with.
66       * @param messageFactory The message factory.
67       * @param name The name of the Logger.
68       */
69      protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
70          super(name, messageFactory);
71          this.context = context;
72          privateConfig = new PrivateConfig(context.getConfiguration(), this);
73      }
74  
75      /**
76       * This method is only used for 1.x compatibility.
77       * Returns the parent of this Logger. If it doesn't already exist return a temporary Logger.
78       * @return The parent Logger.
79       */
80      public Logger getParent() {
81          final LoggerConfig lc = privateConfig.loggerConfig.getName().equals(getName()) ? privateConfig.loggerConfig.getParent() :
82              privateConfig.loggerConfig;
83          if (lc == null) {
84              return null;
85          }
86          if (context.hasLogger(lc.getName())) {
87              return context.getLogger(lc.getName(), getMessageFactory());
88          }
89          return new Logger(context, lc.getName(), this.getMessageFactory());
90      }
91  
92      /**
93       * Returns the LoggerContext this Logger is associated with.
94       * @return the LoggerContext.
95       */
96      public LoggerContext getContext() {
97          return context;
98      }
99  
100     /**
101      * This method is not exposed through the public API and is provided primarily for unit testing.
102      * <p>
103      * If the new level is null, this logger inherits the level from its parent.
104      * </p>
105      * 
106      * @param level The Level to use on this Logger, may be null.
107      */
108     public synchronized void setLevel(final Level level) {
109         if (level == getLevel()) {
110             return;
111         }
112         Level actualLevel;
113         if (level != null) {
114             actualLevel = level;
115         } else {
116             final Logger parent = getParent();
117             actualLevel = parent != null ? parent.getLevel() : privateConfig.level;
118         }
119         privateConfig = new PrivateConfig(privateConfig, actualLevel);
120     }
121     
122     /*
123      * (non-Javadoc)
124      * @see org.apache.logging.log4j.util.Supplier#get()
125      */
126     public LoggerConfig get() {
127         return privateConfig.loggerConfig;
128     }
129 
130     @Override
131     public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) {
132         final Message msg = message == null ? new SimpleMessage(Strings.EMPTY) : message;
133         
134         // check if we need to reconfigure
135         privateConfig.config.getConfigurationMonitor().checkConfiguration();
136         
137         final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy();
138         strategy.log(this, getName(), fqcn, marker, level, msg, t);
139     }
140 
141     @Override
142     public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
143         return privateConfig.filter(level, marker, message, t);
144     }
145 
146     @Override
147     public boolean isEnabled(final Level level, final Marker marker, final String message) {
148         return privateConfig.filter(level, marker, message);
149     }
150 
151     @Override
152     public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {
153         return privateConfig.filter(level, marker, message, params);
154     }
155 
156     @Override
157     public boolean isEnabled(final Level level, final Marker marker, final Object message, final Throwable t) {
158         return privateConfig.filter(level, marker, message, t);
159     }
160 
161     @Override
162     public boolean isEnabled(final Level level, final Marker marker, final Message message, final Throwable t) {
163         return privateConfig.filter(level, marker, message, t);
164     }
165 
166     /**
167      * This method is not exposed through the public API and is used primarily for unit testing.
168      * @param appender The Appender to add to the Logger.
169      */
170     public void addAppender(final Appender appender) {
171         privateConfig.config.addLoggerAppender(this, appender);
172     }
173 
174     /**
175      * This method is not exposed through the public API and is used primarily for unit testing.
176      * @param appender The Appender to remove from the Logger.
177      */
178     public void removeAppender(final Appender appender) {
179         privateConfig.loggerConfig.removeAppender(appender.getName());
180     }
181 
182     /**
183      * This method is not exposed through the public API and is used primarily for unit testing.
184      * @return A Map containing the Appender's name as the key and the Appender as the value.
185      */
186     public Map<String, Appender> getAppenders() {
187          return privateConfig.loggerConfig.getAppenders();
188     }
189 
190     /**
191      * This method is not exposed through the public API and is used primarily for unit testing.
192      * @return An Iterator over all the Filters associated with the Logger.
193      */
194     // FIXME: this really ought to be an Iterable instead of an Iterator
195     public Iterator<Filter> getFilters() {
196         final Filter filter = privateConfig.loggerConfig.getFilter();
197         if (filter == null) {
198             return new ArrayList<Filter>().iterator();
199         } else if (filter instanceof CompositeFilter) {
200             return ((CompositeFilter) filter).iterator();
201         } else {
202             final List<Filter> filters = new ArrayList<>();
203             filters.add(filter);
204             return filters.iterator();
205         }
206     }
207 
208     /**
209      * Gets the Level associated with the Logger.
210      *
211      * @return the Level associate with the Logger.
212      */
213     @Override
214     public Level getLevel() {
215         return privateConfig.level;
216     }
217 
218     /**
219      * This method is not exposed through the public API and is used primarily for unit testing.
220      * @return The number of Filters associated with the Logger.
221      */
222     public int filterCount() {
223         final Filter filter = privateConfig.loggerConfig.getFilter();
224         if (filter == null) {
225             return 0;
226         } else if (filter instanceof CompositeFilter) {
227             return ((CompositeFilter) filter).size();
228         }
229         return 1;
230     }
231 
232     /**
233      * This method is not exposed through the public API and is used primarily for unit testing.
234      * @param filter The Filter to add.
235      */
236     public void addFilter(final Filter filter) {
237         privateConfig.config.addLoggerFilter(this, filter);
238     }
239 
240     /**
241      * This method is not exposed through the public API and is present only to support the Log4j 1.2
242      * compatibility bridge.
243      * @return true if the associated LoggerConfig is additive, false otherwise.
244      */
245     public boolean isAdditive() {
246         return privateConfig.loggerConfig.isAdditive();
247     }
248 
249     /**
250      * This method is not exposed through the public API and is present only to support the Log4j 1.2
251      * compatibility bridge.
252      * @param additive Boolean value to indicate whether the Logger is additive or not.
253      */
254     public void setAdditive(final boolean additive) {
255         privateConfig.config.setLoggerAdditive(this, additive);
256     }
257 
258     /**
259      * Associates the Logger with a new Configuration. This method is not exposed through the
260      * public API.
261      *
262      * There are two ways that could be used to guarantee all threads are aware of changes to
263      * config. 1. synchronize this method. Accessors don't need to be synchronized as Java will
264      * treat all variables within a synchronized block as volatile. 2. Declare the variable
265      * volatile. Option 2 is used here as the performance cost is very low and it does a better
266      * job at documenting how it is used.
267      *
268      * @param newConfig The new Configuration.
269      */
270     protected void updateConfiguration(final Configuration newConfig) {
271         this.privateConfig = new PrivateConfig(newConfig, this);
272     }
273 
274     /**
275      * The binding between a Logger and its configuration.
276      */
277     // TODO: Should not be Serializable per EJ item 74 (2nd Ed)?
278     protected class PrivateConfig implements Serializable {
279         private static final long serialVersionUID = 1L;
280         // config fields are public to make them visible to Logger subclasses
281         public final LoggerConfig loggerConfig;
282         public final Configuration config;
283         private final Level level;
284         private final int intLevel;
285         private final Logger logger;
286 
287         public PrivateConfig(final Configuration config, final Logger logger) {
288             this.config = config;
289             this.loggerConfig = config.getLoggerConfig(getName());
290             this.level = this.loggerConfig.getLevel();
291             this.intLevel = this.level.intLevel();
292             this.logger = logger;
293         }
294 
295         public PrivateConfig(final PrivateConfig pc, final Level level) {
296             this.config = pc.config;
297             this.loggerConfig = pc.loggerConfig;
298             this.level = level;
299             this.intLevel = this.level.intLevel();
300             this.logger = pc.logger;
301         }
302 
303         public PrivateConfig(final PrivateConfig pc, final LoggerConfig lc) {
304             this.config = pc.config;
305             this.loggerConfig = lc;
306             this.level = lc.getLevel();
307             this.intLevel = this.level.intLevel();
308             this.logger = pc.logger;
309         }
310 
311         // LOG4J2-151: changed visibility to public
312         public void logEvent(final LogEvent event) {
313             config.getConfigurationMonitor().checkConfiguration();
314             loggerConfig.log(event);
315         }
316 
317         boolean filter(final Level level, final Marker marker, final String msg) {
318             config.getConfigurationMonitor().checkConfiguration();
319             final Filter filter = config.getFilter();
320             if (filter != null) {
321                 final Filter.Result r = filter.filter(logger, level, marker, msg);
322                 if (r != Filter.Result.NEUTRAL) {
323                     return r == Filter.Result.ACCEPT;
324                 }
325             }
326             return level != null && intLevel >= level.intLevel();
327         }
328 
329         boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) {
330             config.getConfigurationMonitor().checkConfiguration();
331             final Filter filter = config.getFilter();
332             if (filter != null) {
333                 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
334                 if (r != Filter.Result.NEUTRAL) {
335                     return r == Filter.Result.ACCEPT;
336                 }
337             }
338             return level != null && intLevel >= level.intLevel();
339         }
340 
341         boolean filter(final Level level, final Marker marker, final String msg, final Object... p1) {
342             config.getConfigurationMonitor().checkConfiguration();
343             final Filter filter = config.getFilter();
344             if (filter != null) {
345                 final Filter.Result r = filter.filter(logger, level, marker, msg, p1);
346                 if (r != Filter.Result.NEUTRAL) {
347                     return r == Filter.Result.ACCEPT;
348                 }
349             }
350             return level != null && intLevel >= level.intLevel();
351         }
352 
353         boolean filter(final Level level, final Marker marker, final Object msg, final Throwable t) {
354             config.getConfigurationMonitor().checkConfiguration();
355             final Filter filter = config.getFilter();
356             if (filter != null) {
357                 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
358                 if (r != Filter.Result.NEUTRAL) {
359                     return r == Filter.Result.ACCEPT;
360                 }
361             }
362             return level != null && intLevel >= level.intLevel();
363         }
364 
365         boolean filter(final Level level, final Marker marker, final Message msg, final Throwable t) {
366             config.getConfigurationMonitor().checkConfiguration();
367             final Filter filter = config.getFilter();
368             if (filter != null) {
369                 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
370                 if (r != Filter.Result.NEUTRAL) {
371                     return r == Filter.Result.ACCEPT;
372                 }
373             }
374             return level != null && intLevel >= level.intLevel();
375         }
376     }
377 
378     /**
379      * Returns a String representation of this instance in the form {@code "name:level[ in context_name]"}.
380      * @return A String describing this Logger instance.
381      */
382     @Override
383     public String toString() {
384         final String nameLevel = Strings.EMPTY + getName() + ':' + getLevel();
385         if (context == null) {
386             return nameLevel;
387         }
388         final String contextName = context.getName();
389         return contextName == null ? nameLevel : nameLevel + " in " + contextName;
390     }
391 }