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