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