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.config;
18  
19  import org.apache.logging.log4j.Level;
20  import org.apache.logging.log4j.LogManager;
21  import org.apache.logging.log4j.Logger;
22  import org.apache.logging.log4j.Marker;
23  import org.apache.logging.log4j.core.Appender;
24  import org.apache.logging.log4j.core.Filter;
25  import org.apache.logging.log4j.core.LifeCycle;
26  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
27  import org.apache.logging.log4j.core.filter.AbstractFilterable;
28  import org.apache.logging.log4j.core.impl.Log4jLogEvent;
29  import org.apache.logging.log4j.core.LogEvent;
30  import org.apache.logging.log4j.core.impl.LogEventFactory;
31  import org.apache.logging.log4j.core.config.plugins.Plugin;
32  import org.apache.logging.log4j.core.config.plugins.PluginAttr;
33  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
34  import org.apache.logging.log4j.core.config.plugins.PluginElement;
35  import org.apache.logging.log4j.status.StatusLogger;
36  import org.apache.logging.log4j.message.Message;
37  
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.Collection;
41  import java.util.HashMap;
42  import java.util.Iterator;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.concurrent.ConcurrentHashMap;
46  import java.util.concurrent.atomic.AtomicInteger;
47  
48  /**
49   * Logger object that is created via configuration.
50   */
51  @Plugin(name = "logger", type = "Core", printObject = true)
52  public class LoggerConfig extends AbstractFilterable implements LogEventFactory {
53  
54      private static final Logger LOGGER = StatusLogger.getLogger();
55      private static final int MAX_RETRIES = 3;
56      private static final long WAIT_TIME = 1000;
57  
58      private List<AppenderRef> appenderRefs = new ArrayList<AppenderRef>();
59      private final Map<String, AppenderControl<?>> appenders = new ConcurrentHashMap<String, AppenderControl<?>>();
60      private final String name;
61      private LogEventFactory logEventFactory;
62      private Level level;
63      private boolean additive = true;
64      private LoggerConfig parent;
65      private final AtomicInteger counter = new AtomicInteger();
66      private boolean shutdown = false;
67      private final Map<Property, Boolean> properties;
68      private final Configuration config;
69  
70  
71      /**
72       * Default constructor.
73       */
74      public LoggerConfig() {
75          this.logEventFactory = this;
76          this.level = Level.ERROR;
77          this.name = "";
78          this.properties = null;
79          this.config = null;
80      }
81  
82      /**
83       * Constructor that sets the name, level and additive values.
84       * @param name The Logger name.
85       * @param level The Level.
86       * @param additive true if the Logger is additive, false otherwise.
87       */
88      public LoggerConfig(final String name, final Level level, final boolean additive) {
89          this.logEventFactory = this;
90          this.name = name;
91          this.level = level;
92          this.additive = additive;
93          this.properties = null;
94          this.config = null;
95      }
96  
97      protected LoggerConfig(final String name, final List<AppenderRef> appenders, final Filter filter, final Level level,
98                             final boolean additive, final Property[] properties, final Configuration config) {
99          super(filter);
100         this.logEventFactory = this;
101         this.name = name;
102         this.appenderRefs = appenders;
103         this.level = level;
104         this.additive = additive;
105         this.config = config;
106         if (properties != null && properties.length > 0) {
107             this.properties = new HashMap<Property, Boolean>(properties.length);
108             for (final Property prop : properties) {
109                 final boolean interpolate = prop.getValue().contains("${");
110                 this.properties.put(prop, interpolate);
111             }
112         } else {
113             this.properties = null;
114         }
115     }
116 
117     @Override
118     public Filter getFilter() {
119         return super.getFilter();
120     }
121 
122     /**
123      * Returns the name of the LoggerConfig.
124      * @return the name of the LoggerConfig.
125      */
126     public String getName() {
127         return name;
128     }
129 
130     /**
131      * Sets the parent of this LoggerConfig.
132      * @param parent the parent LoggerConfig.
133      */
134     public void setParent(final LoggerConfig parent) {
135         this.parent = parent;
136     }
137 
138     /**
139      * Returns the parent of this LoggerConfig.
140      * @return the LoggerConfig that is the parent of this one.
141      */
142     public LoggerConfig getParent() {
143         return this.parent;
144     }
145 
146     /**
147      * Adds an Appender to the LoggerConfig.
148      * @param appender The Appender to add.
149      * @param level The Level to use.
150      * @param filter A Filter for the Appender reference.
151      */
152     public void addAppender(final Appender appender, final Level level, final Filter filter) {
153         appenders.put(appender.getName(), new AppenderControl(appender, level, filter));
154     }
155 
156     /**
157      * Removes the Appender with the specific name.
158      * @param name The name of the Appender.
159      */
160     public void removeAppender(final String name) {
161         final AppenderControl ctl = appenders.remove(name);
162         if (ctl != null) {
163             cleanupFilter(ctl);
164         }
165     }
166 
167     /**
168      * Returns all Appenders as a Map.
169      * @return a Map with the Appender name as the key and the Appender as the value.
170      */
171     public Map<String, Appender<?>> getAppenders() {
172         final Map<String, Appender<?>> map = new HashMap<String, Appender<?>>();
173         for (final Map.Entry<String, AppenderControl<?>> entry : appenders.entrySet()) {
174             map.put(entry.getKey(), entry.getValue().getAppender());
175         }
176         return map;
177     }
178 
179     /**
180      * Removes all Appenders.
181      */
182     protected void clearAppenders() {
183         waitForCompletion();
184         final Collection<AppenderControl<?>> controls = appenders.values();
185         final Iterator<AppenderControl<?>> iterator = controls.iterator();
186         while (iterator.hasNext()) {
187             final AppenderControl<?> ctl = iterator.next();
188             iterator.remove();
189             cleanupFilter(ctl);
190         }
191     }
192 
193     private void cleanupFilter(final AppenderControl ctl) {
194         final Filter filter = ctl.getFilter();
195         if (filter != null) {
196             ctl.removeFilter(filter);
197             if (filter instanceof LifeCycle) {
198                 ((LifeCycle) filter).stop();
199             }
200         }
201     }
202 
203     /**
204      * Returns the Appender references.
205      * @return a List of all the Appender names attached to this LoggerConfig.
206      */
207     public List<AppenderRef> getAppenderRefs() {
208         return appenderRefs;
209     }
210 
211     /**
212      * Sets the logging Level.
213      * @param level The logging Level.
214      */
215     public void setLevel(final Level level) {
216         this.level = level;
217     }
218 
219     /**
220      * Returns the logging Level.
221      * @return the logging Level.
222      */
223     public Level getLevel() {
224         return level;
225     }
226 
227     /**
228      * Returns the LogEventFactory.
229      * @return the LogEventFactory.
230      */
231     public LogEventFactory getLogEventFactory() {
232         return logEventFactory;
233     }
234 
235     /**
236      * Sets the LogEventFactory. Usually the LogEventFactory will be this LoggerConfig.
237      * @param logEventFactory the LogEventFactory.
238      */
239     public void setLogEventFactory(final LogEventFactory logEventFactory) {
240         this.logEventFactory = logEventFactory;
241     }
242 
243     /**
244      * Returns the valid of the additive flag.
245      * @return true if the LoggerConfig is additive, false otherwise.
246      */
247     public boolean isAdditive() {
248         return additive;
249     }
250 
251     /**
252      * Sets the additive setting.
253      * @param additive true if thee LoggerConfig should be additive, false otherwise.
254      */
255     public void setAdditive(final boolean additive) {
256         this.additive = additive;
257     }
258 
259     /**
260      * Logs an event.
261      * @param loggerName The name of the Logger.
262      * @param marker A Marker or null if none is present.
263      * @param fqcn The fully qualified class name of the caller.
264      * @param level The event Level.
265      * @param data The Message.
266      * @param t A Throwable or null.
267      */
268     public void log(final String loggerName, final Marker marker, final String fqcn, final Level level,
269                     final Message data, final Throwable t) {
270         List<Property> props = null;
271         if (properties != null) {
272             props = new ArrayList<Property>(properties.size());
273 
274             for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) {
275                 final Property prop = entry.getKey();
276                 final String value = entry.getValue() ? config.getSubst().replace(prop.getValue()) : prop.getValue();
277                 props.add(Property.createProperty(prop.getName(), value));
278             }
279         }
280         final LogEvent event = logEventFactory.createEvent(loggerName, marker, fqcn, level, data, props, t);
281         log(event);
282     }
283 
284     /**
285      * Waits for all log events to complete before shutting down this loggerConfig.
286      */
287     private synchronized void waitForCompletion() {
288         if (shutdown) {
289             return;
290         }
291         shutdown = true;
292         int retries = 0;
293         while (counter.get() > 0) {
294             try {
295                 wait(WAIT_TIME * (retries + 1));
296             } catch (final InterruptedException ie) {
297                 if (++retries > MAX_RETRIES) {
298                     break;
299                 }
300             }
301         }
302     }
303 
304     /**
305      * Logs an event.
306      * @param event The log event.
307      */
308     public void log(final LogEvent event) {
309 
310         counter.incrementAndGet();
311         try {
312             if (isFiltered(event)) {
313                 return;
314             }
315 
316             callAppenders(event);
317 
318             if (additive && parent != null) {
319                 parent.log(event);
320             }
321         } finally {
322             if (counter.decrementAndGet() == 0) {
323                 synchronized (this) {
324                     if (shutdown) {
325                         notifyAll();
326                     }
327                 }
328 
329             }
330         }
331     }
332 
333     private void callAppenders(final LogEvent event) {
334         for (final AppenderControl control : appenders.values()) {
335             control.callAppender(event);
336         }
337     }
338 
339     /**
340      * Creates a log event.
341      * @param loggerName The name of the Logger.
342      * @param marker An optional Marker.
343      * @param fqcn The fully qualified class name of the caller.
344      * @param level The event Level.
345      * @param data The Message.
346      * @param properties Properties to be added to the log event.
347      * @param t An optional Throwable.
348      * @return The LogEvent.
349      */
350     public LogEvent createEvent(final String loggerName, final Marker marker, final String fqcn, final Level level,
351                                 final Message data, final List<Property> properties, final Throwable t) {
352         return new Log4jLogEvent(loggerName, marker, fqcn, level, data, properties, t);
353     }
354 
355     @Override
356     public String toString() {
357         return name == null || name.length() == 0 ? "root" : name;
358     }
359 
360     /**
361      * Factory method to create a LoggerConfig.
362      * @param additivity True if additive, false otherwise.
363      * @param levelName The Level to be associated with the Logger.
364      * @param loggerName The name of the Logger.
365      * @param refs An array of Appender names.
366      * @param properties Properties to pass to the Logger.
367      * @param config The Configuration.
368      * @param filter A Filter.
369      * @return A new LoggerConfig.
370      */
371     @PluginFactory
372     public static LoggerConfig createLogger(@PluginAttr("additivity") final String additivity,
373                                             @PluginAttr("level") final String levelName,
374                                             @PluginAttr("name") final String loggerName,
375                                             @PluginElement("appender-ref") final AppenderRef[] refs,
376                                             @PluginElement("properties") final Property[] properties,
377                                             @PluginConfiguration final Configuration config,
378                                             @PluginElement("filters") final Filter filter) {
379         if (loggerName == null) {
380             LOGGER.error("Loggers cannot be configured without a name");
381             return null;
382         }
383 
384         final List<AppenderRef> appenderRefs = Arrays.asList(refs);
385         Level level;
386         try {
387             level = Level.toLevel(levelName, Level.ERROR);
388         } catch (final Exception ex) {
389             LOGGER.error("Invalid Log level specified: {}. Defaulting to Error", levelName);
390             level = Level.ERROR;
391         }
392         final String name = loggerName.equals("root") ? "" : loggerName;
393         final boolean additive = additivity == null ? true : Boolean.parseBoolean(additivity);
394 
395         return new LoggerConfig(name, appenderRefs, filter, level, additive, properties, config);
396     }
397 
398     /**
399      * The root Logger.
400      */
401     @Plugin(name = "root", type = "Core", printObject = true)
402     public static class RootLogger extends LoggerConfig {
403 
404         @PluginFactory
405         public static LoggerConfig createLogger(@PluginAttr("additivity") final String additivity,
406                                             @PluginAttr("level") final String levelName,
407                                             @PluginElement("appender-ref") final AppenderRef[] refs,
408                                             @PluginElement("properties") final Property[] properties,
409                                             @PluginConfiguration final Configuration config,
410                                             @PluginElement("filters") final Filter filter) {
411             final List<AppenderRef> appenderRefs = Arrays.asList(refs);
412             Level level;
413             try {
414                 level = Level.toLevel(levelName, Level.ERROR);
415             } catch (final Exception ex) {
416                 LOGGER.error("Invalid Log level specified: {}. Defaulting to Error", levelName);
417                 level = Level.ERROR;
418             }
419             final boolean additive = additivity == null ? true : Boolean.parseBoolean(additivity);
420 
421             return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs, filter, level, additive, properties,
422                 config);
423         }
424     }
425 
426 }