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