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  
34  /**
35   * @doubt All the isEnabled methods could be pushed into a filter interface.  Not sure of the utility of having
36   * isEnabled be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of
37   * Logger noticeably impacts performance. The message pattern and parameters are required so that they can be
38   * used in global filters.
39   */
40  public class Logger extends AbstractLogger {
41  
42      private static final long serialVersionUID = 1L;
43  
44      /**
45       * config should be consistent across threads.
46       */
47      protected volatile PrivateConfig config;
48  
49      private final LoggerContext context;
50  
51      /**
52       * The constructor.
53       * @param context The LoggerContext this Logger is associated with.
54       * @param messageFactory The message factory.
55       * @param name The name of the Logger.
56       */
57      protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
58          super(name, messageFactory);
59          this.context = context;
60          config = new PrivateConfig(context.getConfiguration(), this);
61      }
62  
63      /**
64       * This method is only used for 1.x compatibility.
65       * Returns the parent of this Logger. If it doesn't already exist return a temporary Logger.
66       * @return The parent Logger.
67       */
68      public Logger getParent() {
69          final LoggerConfig lc = config.loggerConfig.getName().equals(getName()) ? config.loggerConfig.getParent() :
70              config.loggerConfig;
71          if (lc == null) {
72              return null;
73          }
74          if (context.hasLogger(lc.getName())) {
75              return context.getLogger(lc.getName(), getMessageFactory());
76          }
77          return new Logger(context, lc.getName(), this.getMessageFactory());
78      }
79  
80      /**
81       * Returns the LoggerContext this Logger is associated with.
82       * @return the LoggerContext.
83       */
84      public LoggerContext getContext() {
85          return context;
86      }
87  
88      /**
89       * This method is not exposed through the public API and is provided primarily for unit testing.
90       * @param level The Level to use on this Logger.
91       */
92      public synchronized void setLevel(final Level level) {
93          if (level != null) {
94              config = new PrivateConfig(config, level);
95          }
96      }
97  
98      /**
99       * Returns the Level associated with the Logger.
100      * @return the Level associate with the Logger.
101      */
102     public Level getLevel() {
103         return config.level;
104     }
105 
106     @Override
107     public void log(final Marker marker, final String fqcn, final Level level, Message data, final Throwable t) {
108         if (data == null) {
109             data = new SimpleMessage("");
110         }
111         config.config.getConfigurationMonitor().checkConfiguration();
112         config.loggerConfig.log(getName(), marker, fqcn, level, data, t);
113     }
114 
115     @Override
116     public boolean isEnabled(final Level level, final Marker marker, final String msg) {
117         return config.filter(level, marker, msg);
118     }
119 
120     @Override
121     public boolean isEnabled(final Level level, final Marker marker, final String msg, final Throwable t) {
122         return config.filter(level, marker, msg, t);
123     }
124 
125     @Override
126     public boolean isEnabled(final Level level, final Marker marker, final String msg, final Object... p1) {
127         return config.filter(level, marker, msg, p1);
128     }
129 
130     @Override
131     public boolean isEnabled(final Level level, final Marker marker, final Object msg, final Throwable t) {
132         return config.filter(level, marker, msg, t);
133     }
134 
135     @Override
136     public boolean isEnabled(final Level level, final Marker marker, final Message msg, final Throwable t) {
137         return config.filter(level, marker, msg, t);
138     }
139 
140     /**
141      * This method is not exposed through the public API and is used primarily for unit testing.
142      * @param appender The Appender to add to the Logger.
143      */
144     public void addAppender(final Appender appender) {
145         config.config.addLoggerAppender(this, appender);
146     }
147 
148     /**
149      * This method is not exposed through the public API and is used primarily for unit testing.
150      * @param appender The Appender to remove from the Logger.
151      */
152     public void removeAppender(final Appender appender) {
153         config.loggerConfig.removeAppender(appender.getName());
154     }
155 
156     /**
157      * This method is not exposed through the public API and is used primarily for unit testing.
158      * @return A Map containing the Appender's name as the key and the Appender as the value.
159      */
160     public Map<String, Appender> getAppenders() {
161          return config.loggerConfig.getAppenders();
162     }
163 
164     /**
165      * This method is not exposed through the public API and is used primarily for unit testing.
166      * @return An Iterator over all the Filters associated with the Logger.
167      */
168     public Iterator<Filter> getFilters() {
169         final Filter filter = config.loggerConfig.getFilter();
170         if (filter == null) {
171             return new ArrayList<Filter>().iterator();
172         } else if (filter instanceof CompositeFilter) {
173             return ((CompositeFilter) filter).iterator();
174         } else {
175             final List<Filter> filters = new ArrayList<Filter>();
176             filters.add(filter);
177             return filters.iterator();
178         }
179     }
180 
181     /**
182      * This method is not exposed through the public API and is used primarily for unit testing.
183      * @return The number of Filters associated with the Logger.
184      */
185     public int filterCount() {
186         final Filter filter = config.loggerConfig.getFilter();
187         if (filter == null) {
188             return 0;
189         } else if (filter instanceof CompositeFilter) {
190             return ((CompositeFilter) filter).size();
191         }
192         return 1;
193     }
194 
195     /**
196      * This method is not exposed through the public API and is used primarily for unit testing.
197      * @param filter The Filter to add.
198      */
199     public void addFilter(final Filter filter) {
200         config.config.addLoggerFilter(this, filter);
201     }
202 
203     /**
204      * This method is not exposed through the public API and is present only to support the Log4j 1.2
205      * compatibility bridge.
206      * @return true if the associated LoggerConfig is additive, false otherwise.
207      */
208     public boolean isAdditive() {
209         return config.loggerConfig.isAdditive();
210     }
211 
212     /**
213      * This method is not exposed through the public API and is present only to support the Log4j 1.2
214      * compatibility bridge.
215      * @param additive Boolean value to indicate whether the Logger is additive or not.
216      */
217     public void setAdditive(final boolean additive) {
218         config.config.setLoggerAdditive(this, additive);
219     }
220 
221     /**
222      * Associates the Logger with a new Configuration. This method is not exposed through the
223      * public API.
224      *
225      * There are two ways that could be used to guarantee all threads are aware of changes to
226      * config. 1. synchronize this method. Accessors don't need to be synchronized as Java will
227      * treat all variables within a synchronized block as volatile. 2. Declare the variable
228      * volatile. Option 2 is used here as the performance cost is very low and it does a better
229      * job at documenting how it is used.
230      *
231      * @param config The new Configuration.
232      */
233     void updateConfiguration(final Configuration config) {
234         this.config = new PrivateConfig(config, this);
235     }
236 
237     /**
238      * The binding between a Logger and its configuration.
239      */
240     protected class PrivateConfig {
241         // config fields are public to make them visible to Logger subclasses
242         public final LoggerConfig loggerConfig;
243         public final Configuration config;
244         private final Level level;
245         private final int intLevel;
246         private final Logger logger;
247 
248         public PrivateConfig(final Configuration config, final Logger logger) {
249             this.config = config;
250             this.loggerConfig = config.getLoggerConfig(getName());
251             this.level = this.loggerConfig.getLevel();
252             this.intLevel = this.level.intLevel();
253             this.logger = logger;
254         }
255 
256         public PrivateConfig(final PrivateConfig pc, final Level level) {
257             this.config = pc.config;
258             this.loggerConfig = pc.loggerConfig;
259             this.level = level;
260             this.intLevel = this.level.intLevel();
261             this.logger = pc.logger;
262         }
263 
264         public PrivateConfig(final PrivateConfig pc, final LoggerConfig lc) {
265             this.config = pc.config;
266             this.loggerConfig = lc;
267             this.level = lc.getLevel();
268             this.intLevel = this.level.intLevel();
269             this.logger = pc.logger;
270         }
271 
272         // LOG4J2-151: changed visibility to public
273         public void logEvent(final LogEvent event) {
274             config.getConfigurationMonitor().checkConfiguration();
275             loggerConfig.log(event);
276         }
277 
278         boolean filter(final Level level, final Marker marker, final String msg) {
279             config.getConfigurationMonitor().checkConfiguration();
280             final Filter filter = config.getFilter();
281             if (filter != null) {
282                 final Filter.Result r = filter.filter(logger, level, marker, msg);
283                 if (r != Filter.Result.NEUTRAL) {
284                     return r == Filter.Result.ACCEPT;
285                 }
286             }
287 
288             return intLevel >= level.intLevel();
289         }
290 
291         boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) {
292             config.getConfigurationMonitor().checkConfiguration();
293             final Filter filter = config.getFilter();
294             if (filter != null) {
295                 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
296                 if (r != Filter.Result.NEUTRAL) {
297                     return r == Filter.Result.ACCEPT;
298                 }
299             }
300 
301             return intLevel >= level.intLevel();
302         }
303 
304         boolean filter(final Level level, final Marker marker, final String msg, final Object... p1) {
305             config.getConfigurationMonitor().checkConfiguration();
306             final Filter filter = config.getFilter();
307             if (filter != null) {
308                 final Filter.Result r = filter.filter(logger, level, marker, msg, p1);
309                 if (r != Filter.Result.NEUTRAL) {
310                     return r == Filter.Result.ACCEPT;
311                 }
312             }
313 
314             return intLevel >= level.intLevel();
315         }
316 
317         boolean filter(final Level level, final Marker marker, final Object msg, final Throwable t) {
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, t);
322                 if (r != Filter.Result.NEUTRAL) {
323                     return r == Filter.Result.ACCEPT;
324                 }
325             }
326 
327             return intLevel >= level.intLevel();
328         }
329 
330         boolean filter(final Level level, final Marker marker, final Message msg, final Throwable t) {
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, t);
335                 if (r != Filter.Result.NEUTRAL) {
336                     return r == Filter.Result.ACCEPT;
337                 }
338             }
339 
340             return intLevel >= level.intLevel();
341         }
342     }
343 
344     /**
345      * Returns a String representation of this instance in the form {@code "name:level[ in context_name]"}.
346      * @return A String describing this Logger instance.
347      */
348     @Override
349     public String toString() {
350         final String nameLevel = "" + getName() + ":" + getLevel();
351         if (context == null) {
352             return nameLevel;
353         }
354         final String contextName = context.getName();
355         return contextName == null ? nameLevel : nameLevel + " in " + contextName;
356     }
357 }