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.core.LoggerContext;
23  import org.apache.logging.log4j.core.impl.Log4jContextFactory;
24  import org.apache.logging.log4j.core.util.NetUtils;
25  import org.apache.logging.log4j.spi.LoggerContextFactory;
26  import org.apache.logging.log4j.status.StatusLogger;
27  import org.apache.logging.log4j.util.Strings;
28  
29  import java.net.URI;
30  import java.util.ArrayList;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.concurrent.TimeUnit;
34  
35  /**
36   * Initializes and configure the Logging system. This class provides several ways to construct a LoggerContext using
37   * the location of a configuration file, a context name, and various optional parameters.
38   */
39  public final class Configurator {
40  
41      private static final String FQCN = Configurator.class.getName();
42  
43      private static final Logger LOGGER = StatusLogger.getLogger();
44  
45      private static Log4jContextFactory getFactory() {
46          final LoggerContextFactory factory = LogManager.getFactory();
47          if (factory instanceof Log4jContextFactory) {
48              return (Log4jContextFactory) factory;
49          } else {
50              if (factory != null) {
51                  LOGGER.error("LogManager returned an instance of {} which does not implement {}. Unable to initialize Log4j.",
52                          factory.getClass().getName(), Log4jContextFactory.class.getName());
53              } else {
54                  LOGGER.fatal("LogManager did not return a LoggerContextFactory. This indicates something has gone terribly wrong!");
55              }
56              return null;
57          }
58      }
59  
60      /**
61       * Initializes the Logging Context.
62       * @param loader The ClassLoader for the Context (or null).
63       * @param source The InputSource for the configuration.
64       * @return The LoggerContext.
65       */
66      public static LoggerContext initialize(final ClassLoader loader,
67                                             final ConfigurationSource source) {
68          return initialize(loader, source, null);
69      }
70  
71      /**
72       * Initializes the Logging Context.
73       * @param loader The ClassLoader for the Context (or null).
74       * @param source The InputSource for the configuration.
75       * @param externalContext The external context to be attached to the LoggerContext.
76       * @return The LoggerContext.
77       */
78  
79      public static LoggerContext initialize(final ClassLoader loader,
80                                             final ConfigurationSource source,
81                                             final Object externalContext)
82      {
83  
84          try {
85              final Log4jContextFactory factory = getFactory();
86              return factory == null ? null :
87                      factory.getContext(FQCN, loader, externalContext, false, source);
88          } catch (final Exception ex) {
89              LOGGER.error("There was a problem obtaining a LoggerContext using the configuration source [{}]", source, ex);
90          }
91          return null;
92      }
93  
94      /**
95       * Initializes the Logging Context.
96       * @param name The Context name.
97       * @param loader The ClassLoader for the Context (or null).
98       * @param configLocation The configuration for the logging context.
99       * @return The LoggerContext or null if an error occurred (check the status logger).
100      */
101     public static LoggerContext initialize(final String name, final ClassLoader loader, final String configLocation) {
102         return initialize(name, loader, configLocation, null);
103 
104     }
105 
106     /**
107      * Initializes the Logging Context.
108      * @param name The Context name.
109      * @param loader The ClassLoader for the Context (or null).
110      * @param configLocation The configuration for the logging context (or null, or blank).
111      * @param externalContext The external context to be attached to the LoggerContext
112      * @return The LoggerContext or null if an error occurred (check the status logger).
113      */
114     public static LoggerContext initialize(final String name, final ClassLoader loader, final String configLocation,
115             final Object externalContext) {
116         if (Strings.isBlank(configLocation)) {
117             return initialize(name, loader, (URI) null, externalContext);
118         }
119         if (configLocation.contains(",")) {
120             final String[] parts = configLocation.split(",");
121             String scheme = null;
122             final List<URI> uris = new ArrayList<>(parts.length);
123             for (final String part : parts) {
124                 final URI uri = NetUtils.toURI(scheme != null ? scheme + ":" + part.trim() : part.trim());
125                 if (scheme == null && uri.getScheme() != null) {
126                     scheme = uri.getScheme();
127                 }
128                 uris.add(uri);
129             }
130             return initialize(name, loader, uris, externalContext);
131         }
132         return initialize(name, loader, NetUtils.toURI(configLocation), externalContext);
133     }
134 
135     /**
136      * Initializes the Logging Context.
137      * @param name The Context name.
138      * @param loader The ClassLoader for the Context (or null).
139      * @param configLocation The configuration for the logging context.
140      * @return The LoggerContext.
141      */
142     public static LoggerContext initialize(final String name, final ClassLoader loader, final URI configLocation) {
143         return initialize(name, loader, configLocation, null);
144     }
145 
146     /**
147      * Initializes the Logging Context.
148      * @param name The Context name.
149      * @param loader The ClassLoader for the Context (or null).
150      * @param configLocation The configuration for the logging context (or null).
151      * @param externalContext The external context to be attached to the LoggerContext
152      * @return The LoggerContext.
153      */
154     public static LoggerContext initialize(final String name, final ClassLoader loader, final URI configLocation,
155                                            final Object externalContext) {
156 
157         try {
158             final Log4jContextFactory factory = getFactory();
159             return factory == null ? null :
160                     factory.getContext(FQCN, loader, externalContext, false, configLocation, name);
161         } catch (final Exception ex) {
162             LOGGER.error("There was a problem initializing the LoggerContext [{}] using configuration at [{}].",
163                     name, configLocation, ex);
164         }
165         return null;
166     }
167 
168     /**
169      * Initializes the Logging Context.
170      * @param name The Context name.
171      * @param loader The ClassLoader for the Context (or null).
172      * @param configLocation The configuration for the logging context (or null).
173      * @param entry The external context entry to be attached to the LoggerContext
174      * @return The LoggerContext.
175      */
176     public static LoggerContext initialize(final String name, final ClassLoader loader, final URI configLocation,
177             final Map.Entry<String, Object> entry) {
178 
179         try {
180             final Log4jContextFactory factory = getFactory();
181             return factory == null ? null :
182                     factory.getContext(FQCN, loader, entry, false, configLocation, name);
183         } catch (final Exception ex) {
184             LOGGER.error("There was a problem initializing the LoggerContext [{}] using configuration at [{}].",
185                     name, configLocation, ex);
186         }
187         return null;
188     }
189 
190     public static LoggerContext initialize(final String name, final ClassLoader loader, final List<URI> configLocations,
191             final Object externalContext) {
192         try {
193             final Log4jContextFactory factory = getFactory();
194             return factory == null ?
195                     null :
196                     factory.getContext(FQCN, loader, externalContext, false, configLocations, name);
197         } catch (final Exception ex) {
198             LOGGER.error("There was a problem initializing the LoggerContext [{}] using configurations at [{}].", name,
199                     configLocations, ex);
200         }
201         return null;
202     }
203 
204     /**
205      * Initializes the Logging Context.
206      * @param name The Context name.
207      * @param configLocation The configuration for the logging context.
208      * @return The LoggerContext or null if an error occurred (check the status logger).
209      */
210     public static LoggerContext initialize(final String name, final String configLocation) {
211         return initialize(name, null, configLocation);
212     }
213 
214     /**
215      * Initializes the Logging Context.
216      * @param configuration The Configuration.
217      * @return The LoggerContext.
218      */
219     public static LoggerContext initialize(final Configuration configuration) {
220         return initialize(null, configuration, null);
221     }
222 
223     /**
224      * Initializes the Logging Context.
225      * @param loader The ClassLoader.
226      * @param configuration The Configuration.
227      * @return The LoggerContext.
228      */
229     public static LoggerContext initialize(final ClassLoader loader, final Configuration configuration) {
230         return initialize(loader, configuration, null);
231     }
232 
233     /**
234      * Initializes the Logging Context.
235      * @param loader The ClassLoader.
236      * @param configuration The Configuration.
237      * @param externalContext - The external context to be attached to the LoggerContext.
238      * @return The LoggerContext.
239      */
240     public static LoggerContext initialize(final ClassLoader loader, final Configuration configuration, final Object externalContext) {
241         try {
242             final Log4jContextFactory factory = getFactory();
243             return factory == null ? null :
244                     factory.getContext(FQCN, loader, externalContext, false, configuration);
245         } catch (final Exception ex) {
246             LOGGER.error("There was a problem initializing the LoggerContext using configuration {}",
247                     configuration.getName(), ex);
248         }
249         return null;
250     }
251 
252     /**
253      * Reconfigure using an already constructed Configuration.
254      * @param configuration The configuration.
255      * @since 2.13.0
256      */
257     public static void reconfigure(final Configuration configuration) {
258         try {
259             final Log4jContextFactory factory = getFactory();
260             if (factory != null) {
261                 factory.getContext(FQCN, null, null, false)
262                         .reconfigure(configuration);
263             }
264         } catch (final Exception ex) {
265             LOGGER.error("There was a problem initializing the LoggerContext using configuration {}",
266                     configuration.getName(), ex);
267         }
268     }
269 
270     /**
271      * Reload the existing reconfiguration.
272      * @since 2.12.0
273      */
274     public static void reconfigure() {
275         try {
276             Log4jContextFactory factory = getFactory();
277             if (factory != null) {
278                 factory.getSelector().getContext(FQCN, null, false).reconfigure();
279             } else {
280                 LOGGER.warn("Unable to reconfigure - Log4j has not been initialized.");
281             }
282         } catch (final Exception ex) {
283             LOGGER.error("Error encountered trying to reconfigure logging", ex);
284         }
285     }
286 
287     /**
288      * Reconfigure with a potentially new configuration.
289      * @param uri The location of the configuration.
290      * @since 2.12.0
291      */
292     public static void reconfigure(final URI uri) {
293         try {
294             Log4jContextFactory factory = getFactory();
295             if (factory != null) {
296                 factory.getSelector().getContext(FQCN, null, false).setConfigLocation(uri);
297             } else {
298                 LOGGER.warn("Unable to reconfigure - Log4j has not been initialized.");
299             }
300         } catch (final Exception ex) {
301             LOGGER.error("Error encountered trying to reconfigure logging", ex);
302         }
303     }
304 
305     /**
306      * Sets the levels of <code>parentLogger</code> and all 'child' loggers to the given <code>level</code>.
307      * @param parentLogger the parent logger
308      * @param level the new level
309      */
310     public static void setAllLevels(final String parentLogger, final Level level) {
311         // 1) get logger config
312         // 2) if exact match, use it, if not, create it.
313         // 3) set level on logger config
314         // 4) update child logger configs with level
315         // 5) update loggers
316         final LoggerContext loggerContext = LoggerContext.getContext(false);
317         final Configuration config = loggerContext.getConfiguration();
318         boolean set = setLevel(parentLogger, level, config);
319         for (final Map.Entry<String, LoggerConfig> entry : config.getLoggers().entrySet()) {
320             if (entry.getKey().startsWith(parentLogger)) {
321                 set |= setLevel(entry.getValue(), level);
322             }
323         }
324         if (set) {
325             loggerContext.updateLoggers();
326         }
327     }
328 
329     private static boolean setLevel(final LoggerConfig loggerConfig, final Level level) {
330         final boolean set = !loggerConfig.getLevel().equals(level);
331         if (set) {
332             loggerConfig.setLevel(level);
333         }
334         return set;
335     }
336 
337     /**
338      * Sets logger levels.
339      *
340      * @param levelMap
341      *            a levelMap where keys are level names and values are new
342      *            Levels.
343      */
344     public static void setLevel(final Map<String, Level> levelMap) {
345         final LoggerContext loggerContext = LoggerContext.getContext(false);
346         final Configuration config = loggerContext.getConfiguration();
347         boolean set = false;
348         for (final Map.Entry<String, Level> entry : levelMap.entrySet()) {
349             final String loggerName = entry.getKey();
350             final Level level = entry.getValue();
351             set |= setLevel(loggerName, level, config);
352         }
353         if (set) {
354             loggerContext.updateLoggers();
355         }
356     }
357 
358     /**
359      * Sets a logger's level.
360      *
361      * @param loggerName
362      *            the logger name
363      * @param level
364      *            the new level
365      */
366     public static void setLevel(final String loggerName, final Level level) {
367         final LoggerContext loggerContext = LoggerContext.getContext(false);
368         if (Strings.isEmpty(loggerName)) {
369             setRootLevel(level);
370         } else if (setLevel(loggerName, level, loggerContext.getConfiguration())) {
371             loggerContext.updateLoggers();
372         }
373     }
374 
375     private static boolean setLevel(final String loggerName, final Level level, final Configuration config) {
376         boolean set;
377         LoggerConfig loggerConfig = config.getLoggerConfig(loggerName);
378         if (!loggerName.equals(loggerConfig.getName())) {
379             // TODO Should additivity be inherited?
380             loggerConfig = new LoggerConfig(loggerName, level, true);
381             config.addLogger(loggerName, loggerConfig);
382             loggerConfig.setLevel(level);
383             set = true;
384         } else {
385             set = setLevel(loggerConfig, level);
386         }
387         return set;
388     }
389 
390     /**
391      * Sets the root logger's level.
392      *
393      * @param level
394      *            the new level
395      */
396     public static void setRootLevel(final Level level) {
397         final LoggerContext loggerContext = LoggerContext.getContext(false);
398         final LoggerConfig loggerConfig = loggerContext.getConfiguration().getRootLogger();
399         if (!loggerConfig.getLevel().equals(level)) {
400             loggerConfig.setLevel(level);
401             loggerContext.updateLoggers();
402         }
403     }
404 
405     /**
406      * Shuts down the given logger context. This request does not wait for Log4j tasks to complete.
407      * <p>
408      * Log4j starts threads to perform certain actions like file rollovers; calling this method will not wait until the
409      * rollover thread is done. When this method returns, these tasks' status are undefined, the tasks may be done or
410      * not.
411      * </p>
412      *
413      * @param ctx
414      *            the logger context to shut down, may be null.
415      */
416     public static void shutdown(final LoggerContext ctx) {
417         if (ctx != null) {
418             ctx.stop();
419         }
420     }
421 
422     /**
423      * Shuts down the given logger context.
424      * <p>
425      * Log4j can start threads to perform certain actions like file rollovers; calling this method with a positive
426      * timeout will block until the rollover thread is done.
427      * </p>
428      *
429      * @param ctx
430      *            the logger context to shut down, may be null.
431      * @param timeout
432      *            the maximum time to wait
433      * @param timeUnit
434      *            the time unit of the timeout argument
435      * @return {@code true} if the logger context terminated and {@code false} if the timeout elapsed before
436      *         termination.
437      *
438      * @see LoggerContext#stop(long, TimeUnit)
439      *
440      * @since 2.7
441      */
442     public static boolean shutdown(final LoggerContext ctx, final long timeout, final TimeUnit timeUnit) {
443         if (ctx != null) {
444             return ctx.stop(timeout, timeUnit);
445         }
446         return true;
447     }
448 
449     private Configurator() {
450         // empty
451     }
452 }