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.ObjectStreamException;
20  import java.io.Serializable;
21  import java.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.logging.log4j.Level;
27  import org.apache.logging.log4j.Marker;
28  import org.apache.logging.log4j.core.config.Configuration;
29  import org.apache.logging.log4j.core.config.LoggerConfig;
30  import org.apache.logging.log4j.core.config.ReliabilityStrategy;
31  import org.apache.logging.log4j.core.filter.CompositeFilter;
32  import org.apache.logging.log4j.message.Message;
33  import org.apache.logging.log4j.message.MessageFactory;
34  import org.apache.logging.log4j.message.SimpleMessage;
35  import org.apache.logging.log4j.spi.AbstractLogger;
36  import org.apache.logging.log4j.util.Strings;
37  import org.apache.logging.log4j.util.Supplier;
38  
39  /**
40   * The core implementation of the {@link org.apache.logging.log4j.Logger} interface. Besides providing an implementation
41   * of all the Logger methods, this class also provides some convenience methods for Log4j 1.x compatibility as well as
42   * access to the {@link org.apache.logging.log4j.core.Filter Filters} and {@link org.apache.logging.log4j.core.Appender
43   * Appenders} associated with this Logger. Note that access to these underlying objects is provided primarily for use in
44   * unit tests or bridging legacy Log4j 1.x code. Future versions of this class may or may not include the various
45   * methods that are noted as not being part of the public API.
46   *
47   * TODO All the isEnabled methods could be pushed into a filter interface. Not sure of the utility of having isEnabled
48   * be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of Logger noticeably
49   * impacts performance. The message pattern and parameters are required so that they can be 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       * 
66       * @param context The LoggerContext this Logger is associated with.
67       * @param messageFactory The message factory.
68       * @param name The name of the Logger.
69       */
70      protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
71          super(name, messageFactory);
72          this.context = context;
73          privateConfig = new PrivateConfig(context.getConfiguration(), this);
74      }
75  
76      protected Object writeReplace() throws ObjectStreamException {
77          return new LoggerProxy(getName(), getMessageFactory());
78      }
79  
80      /**
81       * This method is only used for 1.x compatibility. Returns the parent of this Logger. If it doesn't already exist
82       * return a temporary Logger.
83       *
84       * @return The parent Logger.
85       */
86      public Logger getParent() {
87          final LoggerConfig lc = privateConfig.loggerConfig.getName().equals(getName()) ? privateConfig.loggerConfig
88                  .getParent() : privateConfig.loggerConfig;
89          if (lc == null) {
90              return null;
91          }
92          final String lcName = lc.getName();
93          final MessageFactory messageFactory = getMessageFactory();
94          if (context.hasLogger(lcName, messageFactory)) {
95              return context.getLogger(lcName, messageFactory);
96          }
97          return new Logger(context, lcName, messageFactory);
98      }
99  
100     /**
101      * Returns the LoggerContext this Logger is associated with.
102      * 
103      * @return the LoggerContext.
104      */
105     public LoggerContext getContext() {
106         return context;
107     }
108 
109     /**
110      * This method is not exposed through the public API and is provided primarily for unit testing.
111      * <p>
112      * If the new level is null, this logger inherits the level from its parent.
113      * </p>
114      * 
115      * @param level The Level to use on this Logger, may be null.
116      */
117     public synchronized void setLevel(final Level level) {
118         if (level == getLevel()) {
119             return;
120         }
121         Level actualLevel;
122         if (level != null) {
123             actualLevel = level;
124         } else {
125             final Logger parent = getParent();
126             actualLevel = parent != null ? parent.getLevel() : privateConfig.loggerConfigLevel;
127         }
128         privateConfig = new PrivateConfig(privateConfig, actualLevel);
129     }
130 
131     /*
132      * (non-Javadoc)
133      * 
134      * @see org.apache.logging.log4j.util.Supplier#get()
135      */
136     @Override
137     public LoggerConfig get() {
138         return privateConfig.loggerConfig;
139     }
140 
141     @Override
142     public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message,
143             final Throwable t) {
144         final Message msg = message == null ? new SimpleMessage(Strings.EMPTY) : message;
145 
146         final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy();
147         strategy.log(this, getName(), fqcn, marker, level, msg, t);
148     }
149 
150     @Override
151     public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
152         return privateConfig.filter(level, marker, message, t);
153     }
154 
155     @Override
156     public boolean isEnabled(final Level level, final Marker marker, final String message) {
157         return privateConfig.filter(level, marker, message);
158     }
159 
160     @Override
161     public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {
162         return privateConfig.filter(level, marker, message, params);
163     }
164 
165     @Override
166     public boolean isEnabled(final Level level, final Marker marker, final Object message, final Throwable t) {
167         return privateConfig.filter(level, marker, message, t);
168     }
169 
170     @Override
171     public boolean isEnabled(final Level level, final Marker marker, final Message message, final Throwable t) {
172         return privateConfig.filter(level, marker, message, t);
173     }
174 
175     /**
176      * This method is not exposed through the public API and is used primarily for unit testing.
177      * 
178      * @param appender The Appender to add to the Logger.
179      */
180     public void addAppender(final Appender appender) {
181         privateConfig.config.addLoggerAppender(this, appender);
182     }
183 
184     /**
185      * This method is not exposed through the public API and is used primarily for unit testing.
186      * 
187      * @param appender The Appender to remove from the Logger.
188      */
189     public void removeAppender(final Appender appender) {
190         privateConfig.loggerConfig.removeAppender(appender.getName());
191     }
192 
193     /**
194      * This method is not exposed through the public API and is used primarily for unit testing.
195      * 
196      * @return A Map containing the Appender's name as the key and the Appender as the value.
197      */
198     public Map<String, Appender> getAppenders() {
199         return privateConfig.loggerConfig.getAppenders();
200     }
201 
202     /**
203      * This method is not exposed through the public API and is used primarily for unit testing.
204      * 
205      * @return An Iterator over all the Filters associated with the Logger.
206      */
207     // FIXME: this really ought to be an Iterable instead of an Iterator
208     public Iterator<Filter> getFilters() {
209         final Filter filter = privateConfig.loggerConfig.getFilter();
210         if (filter == null) {
211             return new ArrayList<Filter>().iterator();
212         } else if (filter instanceof CompositeFilter) {
213             return ((CompositeFilter) filter).iterator();
214         } else {
215             final List<Filter> filters = new ArrayList<>();
216             filters.add(filter);
217             return filters.iterator();
218         }
219     }
220 
221     /**
222      * Gets the Level associated with the Logger.
223      *
224      * @return the Level associate with the Logger.
225      */
226     @Override
227     public Level getLevel() {
228         return privateConfig.loggerConfigLevel;
229     }
230 
231     /**
232      * This method is not exposed through the public API and is used primarily for unit testing.
233      * 
234      * @return The number of Filters associated with the Logger.
235      */
236     public int filterCount() {
237         final Filter filter = privateConfig.loggerConfig.getFilter();
238         if (filter == null) {
239             return 0;
240         } else if (filter instanceof CompositeFilter) {
241             return ((CompositeFilter) filter).size();
242         }
243         return 1;
244     }
245 
246     /**
247      * This method is not exposed through the public API and is used primarily for unit testing.
248      * 
249      * @param filter The Filter to add.
250      */
251     public void addFilter(final Filter filter) {
252         privateConfig.config.addLoggerFilter(this, filter);
253     }
254 
255     /**
256      * This method is not exposed through the public API and is present only to support the Log4j 1.2 compatibility
257      * bridge.
258      * 
259      * @return true if the associated LoggerConfig is additive, false otherwise.
260      */
261     public boolean isAdditive() {
262         return privateConfig.loggerConfig.isAdditive();
263     }
264 
265     /**
266      * This method is not exposed through the public API and is present only to support the Log4j 1.2 compatibility
267      * bridge.
268      * 
269      * @param additive Boolean value to indicate whether the Logger is additive or not.
270      */
271     public void setAdditive(final boolean additive) {
272         privateConfig.config.setLoggerAdditive(this, additive);
273     }
274 
275     /**
276      * Associates this Logger with a new Configuration. This method is not
277      * exposed through the public API.
278      * <p>
279      * There are two ways this could be used to guarantee all threads are aware
280      * of changes to config.
281      * <ol>
282      * <li>Synchronize this method. Accessors don't need to be synchronized as
283      * Java will treat all variables within a synchronized block as volatile.
284      * </li>
285      * <li>Declare the variable volatile. Option 2 is used here as the
286      * performance cost is very low and it does a better job at documenting how
287      * it is used.</li>
288      *
289      * @param newConfig
290      *            The new Configuration.
291      */
292     protected void updateConfiguration(final Configuration newConfig) {
293         this.privateConfig = new PrivateConfig(newConfig, this);
294     }
295 
296     /**
297      * The binding between a Logger and its configuration.
298      */
299     protected class PrivateConfig {
300         // config fields are public to make them visible to Logger subclasses
301         /** LoggerConfig to delegate the actual logging to. */
302         public final LoggerConfig loggerConfig; // SUPPRESS CHECKSTYLE
303         /** The current Configuration associated with the LoggerConfig. */
304         public final Configuration config; // SUPPRESS CHECKSTYLE
305         private final Level loggerConfigLevel;
306         private final int intLevel;
307         private final Logger logger;
308 
309         public PrivateConfig(final Configuration config, final Logger logger) {
310             this.config = config;
311             this.loggerConfig = config.getLoggerConfig(getName());
312             this.loggerConfigLevel = this.loggerConfig.getLevel();
313             this.intLevel = this.loggerConfigLevel.intLevel();
314             this.logger = logger;
315         }
316 
317         public PrivateConfig(final PrivateConfig pc, final Level level) {
318             this.config = pc.config;
319             this.loggerConfig = pc.loggerConfig;
320             this.loggerConfigLevel = level;
321             this.intLevel = this.loggerConfigLevel.intLevel();
322             this.logger = pc.logger;
323         }
324 
325         public PrivateConfig(final PrivateConfig pc, final LoggerConfig lc) {
326             this.config = pc.config;
327             this.loggerConfig = lc;
328             this.loggerConfigLevel = lc.getLevel();
329             this.intLevel = this.loggerConfigLevel.intLevel();
330             this.logger = pc.logger;
331         }
332 
333         // LOG4J2-151: changed visibility to public
334         public void logEvent(final LogEvent event) {
335             loggerConfig.log(event);
336         }
337 
338         boolean filter(final Level level, final Marker marker, final String msg) {
339             final Filter filter = config.getFilter();
340             if (filter != null) {
341                 final Filter.Result r = filter.filter(logger, level, marker, msg);
342                 if (r != Filter.Result.NEUTRAL) {
343                     return r == Filter.Result.ACCEPT;
344                 }
345             }
346             return level != null && intLevel >= level.intLevel();
347         }
348 
349         boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) {
350             final Filter filter = config.getFilter();
351             if (filter != null) {
352                 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
353                 if (r != Filter.Result.NEUTRAL) {
354                     return r == Filter.Result.ACCEPT;
355                 }
356             }
357             return level != null && intLevel >= level.intLevel();
358         }
359 
360         boolean filter(final Level level, final Marker marker, final String msg, final Object... p1) {
361             final Filter filter = config.getFilter();
362             if (filter != null) {
363                 final Filter.Result r = filter.filter(logger, level, marker, msg, p1);
364                 if (r != Filter.Result.NEUTRAL) {
365                     return r == Filter.Result.ACCEPT;
366                 }
367             }
368             return level != null && intLevel >= level.intLevel();
369         }
370 
371         boolean filter(final Level level, final Marker marker, final Object msg, final Throwable t) {
372             final Filter filter = config.getFilter();
373             if (filter != null) {
374                 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
375                 if (r != Filter.Result.NEUTRAL) {
376                     return r == Filter.Result.ACCEPT;
377                 }
378             }
379             return level != null && intLevel >= level.intLevel();
380         }
381 
382         boolean filter(final Level level, final Marker marker, final Message msg, final Throwable t) {
383             final Filter filter = config.getFilter();
384             if (filter != null) {
385                 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
386                 if (r != Filter.Result.NEUTRAL) {
387                     return r == Filter.Result.ACCEPT;
388                 }
389             }
390             return level != null && intLevel >= level.intLevel();
391         }
392     }
393 
394     /**
395      * Serialization proxy class for Logger. Since the LoggerContext and config information can be reconstructed on the
396      * fly, the only information needed for a Logger are what's available in AbstractLogger.
397      *
398      * @since 2.5
399      */
400     protected static class LoggerProxy implements Serializable {
401         private static final long serialVersionUID = 1L;
402 
403         private final String name;
404         private final MessageFactory messageFactory;
405 
406         public LoggerProxy(String name, MessageFactory messageFactory) {
407             this.name = name;
408             this.messageFactory = messageFactory;
409         }
410 
411         protected Object readResolve() throws ObjectStreamException {
412             return new Logger(LoggerContext.getContext(), name, messageFactory);
413         }
414     }
415 
416     /**
417      * Returns a String representation of this instance in the form {@code "name:level[ in context_name]"}.
418      * 
419      * @return A String describing this Logger instance.
420      */
421     @Override
422     public String toString() {
423         final String nameLevel = Strings.EMPTY + getName() + ':' + getLevel();
424         if (context == null) {
425             return nameLevel;
426         }
427         final String contextName = context.getName();
428         return contextName == null ? nameLevel : nameLevel + " in " + contextName;
429     }
430 
431     @Override
432     public boolean equals(final Object o) {
433         if (this == o) {
434             return true;
435         }
436         if (o == null || getClass() != o.getClass()) {
437             return false;
438         }
439         final Logger that = (Logger) o;
440         return getName().equals(that.getName());
441     }
442 
443     @Override
444     public int hashCode() {
445         return getName().hashCode();
446     }
447 }