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.util.ArrayList;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.apache.logging.log4j.Level;
25  import org.apache.logging.log4j.Marker;
26  import org.apache.logging.log4j.core.config.Configuration;
27  import org.apache.logging.log4j.core.config.LoggerConfig;
28  import org.apache.logging.log4j.core.filter.CompositeFilter;
29  import org.apache.logging.log4j.message.Message;
30  import org.apache.logging.log4j.message.MessageFactory;
31  import org.apache.logging.log4j.message.SimpleMessage;
32  import org.apache.logging.log4j.spi.AbstractLogger;
33  import org.apache.logging.log4j.util.Strings;
34  
35  /**
36   * @doubt All the isEnabled methods could be pushed into a filter interface.  Not sure of the utility of having
37   * isEnabled be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of
38   * Logger noticeably impacts performance. The message pattern and parameters are required so that they can be
39   * used in global filters.
40   */
41  public class Logger extends AbstractLogger {
42  
43      private static final long serialVersionUID = 1L;
44  
45      /**
46       * config should be consistent across threads.
47       */
48      protected volatile PrivateConfig config;
49  
50      private final LoggerContext context;
51  
52      /**
53       * The constructor.
54       * @param context The LoggerContext this Logger is associated with.
55       * @param messageFactory The message factory.
56       * @param name The name of the Logger.
57       */
58      protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
59          super(name, messageFactory);
60          this.context = context;
61          config = new PrivateConfig(context.getConfiguration(), this);
62      }
63  
64      /**
65       * This method is only used for 1.x compatibility.
66       * Returns the parent of this Logger. If it doesn't already exist return a temporary Logger.
67       * @return The parent Logger.
68       */
69      public Logger getParent() {
70          final LoggerConfig lc = config.loggerConfig.getName().equals(getName()) ? config.loggerConfig.getParent() :
71              config.loggerConfig;
72          if (lc == null) {
73              return null;
74          }
75          if (context.hasLogger(lc.getName())) {
76              return context.getLogger(lc.getName(), getMessageFactory());
77          }
78          return new Logger(context, lc.getName(), this.getMessageFactory());
79      }
80  
81      /**
82       * Returns the LoggerContext this Logger is associated with.
83       * @return the LoggerContext.
84       */
85      public LoggerContext getContext() {
86          return context;
87      }
88  
89      /**
90       * This method is not exposed through the public API and is provided primarily for unit testing.
91       * @param level The Level to use on this Logger.
92       */
93      public synchronized void setLevel(final Level level) {
94          if (level != null) {
95              config = new PrivateConfig(config, level);
96          }
97      }
98  
99      @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 }