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