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