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