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 static org.apache.logging.log4j.core.util.ShutdownCallbackRegistry.SHUTDOWN_HOOK_MARKER;
20  
21  import java.beans.PropertyChangeEvent;
22  import java.beans.PropertyChangeListener;
23  import java.io.File;
24  import java.net.URI;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.List;
29  import java.util.Objects;
30  import java.util.concurrent.ConcurrentHashMap;
31  import java.util.concurrent.ConcurrentMap;
32  import java.util.concurrent.CopyOnWriteArrayList;
33  import java.util.concurrent.TimeUnit;
34  import java.util.concurrent.locks.Lock;
35  import java.util.concurrent.locks.ReentrantLock;
36  
37  import org.apache.logging.log4j.LogManager;
38  import org.apache.logging.log4j.core.config.Configuration;
39  import org.apache.logging.log4j.core.config.ConfigurationFactory;
40  import org.apache.logging.log4j.core.config.ConfigurationListener;
41  import org.apache.logging.log4j.core.config.ConfigurationSource;
42  import org.apache.logging.log4j.core.config.DefaultConfiguration;
43  import org.apache.logging.log4j.core.config.NullConfiguration;
44  import org.apache.logging.log4j.core.config.Reconfigurable;
45  import org.apache.logging.log4j.core.impl.Log4jLogEvent;
46  import org.apache.logging.log4j.core.jmx.Server;
47  import org.apache.logging.log4j.core.util.Cancellable;
48  import org.apache.logging.log4j.core.util.ExecutorServices;
49  import org.apache.logging.log4j.core.util.Loader;
50  import org.apache.logging.log4j.core.util.NetUtils;
51  import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
52  import org.apache.logging.log4j.message.MessageFactory;
53  import org.apache.logging.log4j.spi.AbstractLogger;
54  import org.apache.logging.log4j.spi.LoggerContextFactory;
55  import org.apache.logging.log4j.spi.LoggerContextShutdownAware;
56  import org.apache.logging.log4j.spi.LoggerContextShutdownEnabled;
57  import org.apache.logging.log4j.spi.LoggerRegistry;
58  import org.apache.logging.log4j.spi.Terminable;
59  import org.apache.logging.log4j.spi.ThreadContextMapFactory;
60  import org.apache.logging.log4j.util.PropertiesUtil;
61  
62  
63  /**
64   * The LoggerContext is the anchor for the logging system. It maintains a list of all the loggers requested by
65   * applications and a reference to the Configuration. The Configuration will contain the configured loggers, appenders,
66   * filters, etc and will be atomically updated whenever a reconfigure occurs.
67   */
68  public class LoggerContext extends AbstractLifeCycle
69          implements org.apache.logging.log4j.spi.LoggerContext, AutoCloseable, Terminable, ConfigurationListener,
70          LoggerContextShutdownEnabled {
71  
72      static {
73          try {
74              // LOG4J2-1642 preload ExecutorServices as it is used in shutdown hook
75              Loader.loadClass(ExecutorServices.class.getName());
76          } catch (final Exception e) {
77              LOGGER.error("Failed to preload ExecutorServices class.", e);
78          }
79      }
80  
81      /**
82       * Property name of the property change event fired if the configuration is changed.
83       */
84      public static final String PROPERTY_CONFIG = "config";
85  
86      private static final Configuration NULL_CONFIGURATION = new NullConfiguration();
87  
88      private final LoggerRegistry<Logger> loggerRegistry = new LoggerRegistry<>();
89      private final CopyOnWriteArrayList<PropertyChangeListener> propertyChangeListeners = new CopyOnWriteArrayList<>();
90      private volatile List<LoggerContextShutdownAware> listeners = null;
91  
92      /**
93       * The Configuration is volatile to guarantee that initialization of the Configuration has completed before the
94       * reference is updated.
95       */
96      private volatile Configuration configuration = new DefaultConfiguration();
97      private static final String EXTERNAL_CONTEXT_KEY = "__EXTERNAL_CONTEXT_KEY__";
98      private ConcurrentMap<String, Object> externalMap = new ConcurrentHashMap<>();
99      private String contextName;
100     private volatile URI configLocation;
101     private Cancellable shutdownCallback;
102 
103     private final Lock configLock = new ReentrantLock();
104 
105     /**
106      * Constructor taking only a name.
107      *
108      * @param name The context name.
109      */
110     public LoggerContext(final String name) {
111         this(name, null, (URI) null);
112     }
113 
114     /**
115      * Constructor taking a name and a reference to an external context.
116      *
117      * @param name The context name.
118      * @param externalContext The external context.
119      */
120     public LoggerContext(final String name, final Object externalContext) {
121         this(name, externalContext, (URI) null);
122     }
123 
124     /**
125      * Constructor taking a name, external context and a configuration URI.
126      *
127      * @param name The context name.
128      * @param externalContext The external context.
129      * @param configLocn The location of the configuration as a URI.
130      */
131     public LoggerContext(final String name, final Object externalContext, final URI configLocn) {
132         this.contextName = name;
133         if (externalContext == null) {
134             externalMap.remove(EXTERNAL_CONTEXT_KEY);
135         } else {
136             externalMap.put(EXTERNAL_CONTEXT_KEY, externalContext);
137         }
138         this.configLocation = configLocn;
139     }
140 
141     /**
142      * Constructor taking a name external context and a configuration location String. The location must be resolvable
143      * to a File.
144      *
145      * @param name The configuration location.
146      * @param externalContext The external context.
147      * @param configLocn The configuration location.
148      */
149     public LoggerContext(final String name, final Object externalContext, final String configLocn) {
150         this.contextName = name;
151         if (externalContext == null) {
152             externalMap.remove(EXTERNAL_CONTEXT_KEY);
153         } else {
154             externalMap.put(EXTERNAL_CONTEXT_KEY, externalContext);
155         }
156         if (configLocn != null) {
157             URI uri;
158             try {
159                 uri = new File(configLocn).toURI();
160             } catch (final Exception ex) {
161                 uri = null;
162             }
163             configLocation = uri;
164         } else {
165             configLocation = null;
166         }
167     }
168 
169     public void addShutdownListener(LoggerContextShutdownAware listener) {
170         if (listeners == null) {
171             synchronized(this) {
172                 if (listeners == null) {
173                     listeners = Collections.synchronizedList(new ArrayList<LoggerContextShutdownAware>());
174                 }
175             }
176         }
177         listeners.add(listener);
178     }
179 
180     public List<LoggerContextShutdownAware> getListeners() {
181         return listeners;
182     }
183 
184     /**
185      * Returns the current LoggerContext.
186      * <p>
187      * Avoids the type cast for:
188      * </p>
189      *
190      * <pre>
191      * (LoggerContext) LogManager.getContext();
192      * </pre>
193      *
194      * <p>
195      * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger for the
196      * calling class.
197      * </p>
198      *
199      * @return The current LoggerContext.
200      * @see LogManager#getContext()
201      */
202     public static LoggerContext getContext() {
203         return (LoggerContext) LogManager.getContext();
204     }
205 
206     /**
207      * Returns a LoggerContext.
208      * <p>
209      * Avoids the type cast for:
210      * </p>
211      *
212      * <pre>
213      * (LoggerContext) LogManager.getContext(currentContext);
214      * </pre>
215      *
216      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
217      *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
218      *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
219      *            be returned. If true then only a single LoggerContext will be returned.
220      * @return a LoggerContext.
221      * @see LogManager#getContext(boolean)
222      */
223     public static LoggerContext getContext(final boolean currentContext) {
224         return (LoggerContext) LogManager.getContext(currentContext);
225     }
226 
227     /**
228      * Returns a LoggerContext.
229      * <p>
230      * Avoids the type cast for:
231      * </p>
232      *
233      * <pre>
234      * (LoggerContext) LogManager.getContext(loader, currentContext, configLocation);
235      * </pre>
236      *
237      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
238      *            ClassLoader.
239      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
240      *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
241      *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
242      *            be returned. If true then only a single LoggerContext will be returned.
243      * @param configLocation The URI for the configuration to use.
244      * @return a LoggerContext.
245      * @see LogManager#getContext(ClassLoader, boolean, URI)
246      */
247     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
248             final URI configLocation) {
249         return (LoggerContext) LogManager.getContext(loader, currentContext, configLocation);
250     }
251 
252     @Override
253     public void start() {
254         LOGGER.debug("Starting LoggerContext[name={}, {}]...", getName(), this);
255         if (PropertiesUtil.getProperties().getBooleanProperty("log4j.LoggerContext.stacktrace.on.start", false)) {
256             LOGGER.debug("Stack trace to locate invoker",
257                     new Exception("Not a real error, showing stack trace to locate invoker"));
258         }
259         if (configLock.tryLock()) {
260             try {
261                 if (this.isInitialized() || this.isStopped()) {
262                     this.setStarting();
263                     reconfigure();
264                     if (this.configuration.isShutdownHookEnabled()) {
265                         setUpShutdownHook();
266                     }
267                     this.setStarted();
268                 }
269             } finally {
270                 configLock.unlock();
271             }
272         }
273         LOGGER.debug("LoggerContext[name={}, {}] started OK.", getName(), this);
274     }
275 
276     /**
277      * Starts with a specific configuration.
278      *
279      * @param config The new Configuration.
280      */
281     public void start(final Configuration config) {
282         LOGGER.debug("Starting LoggerContext[name={}, {}] with configuration {}...", getName(), this, config);
283         if (configLock.tryLock()) {
284             try {
285                 if (this.isInitialized() || this.isStopped()) {
286                     if (this.configuration.isShutdownHookEnabled()) {
287                         setUpShutdownHook();
288                     }
289                     this.setStarted();
290                 }
291             } finally {
292                 configLock.unlock();
293             }
294         }
295         setConfiguration(config);
296         LOGGER.debug("LoggerContext[name={}, {}] started OK with configuration {}.", getName(), this, config);
297     }
298 
299     private void setUpShutdownHook() {
300         if (shutdownCallback == null) {
301             final LoggerContextFactory factory = LogManager.getFactory();
302             if (factory instanceof ShutdownCallbackRegistry) {
303                 LOGGER.debug(SHUTDOWN_HOOK_MARKER, "Shutdown hook enabled. Registering a new one.");
304                 try {
305                     final long shutdownTimeoutMillis = this.configuration.getShutdownTimeoutMillis();
306                     this.shutdownCallback = ((ShutdownCallbackRegistry) factory).addShutdownCallback(new Runnable() {
307                         @Override
308                         public void run() {
309                             @SuppressWarnings("resource")
310                             final LoggerContext context = LoggerContext.this;
311                             LOGGER.debug(SHUTDOWN_HOOK_MARKER, "Stopping LoggerContext[name={}, {}]",
312                                     context.getName(), context);
313                             context.stop(shutdownTimeoutMillis, TimeUnit.MILLISECONDS);
314                         }
315 
316                         @Override
317                         public String toString() {
318                             return "Shutdown callback for LoggerContext[name=" + LoggerContext.this.getName() + ']';
319                         }
320                     });
321                 } catch (final IllegalStateException e) {
322                     throw new IllegalStateException(
323                             "Unable to register Log4j shutdown hook because JVM is shutting down.", e);
324                 } catch (final SecurityException e) {
325                     LOGGER.error(SHUTDOWN_HOOK_MARKER, "Unable to register shutdown hook due to security restrictions",
326                             e);
327                 }
328             }
329         }
330     }
331 
332     @Override
333     public void close() {
334         stop();
335     }
336 
337     @Override
338     public void terminate() {
339         stop();
340     }
341 
342     /**
343      * Blocks until all Log4j tasks have completed execution after a shutdown request and all appenders have shut down,
344      * or the timeout occurs, or the current thread is interrupted, whichever happens first.
345      * <p>
346      * Not all appenders will honor this, it is a hint and not an absolute guarantee that the this method not block longer.
347      * Setting timeout too low increase the risk of losing outstanding log events not yet written to the final
348      * destination.
349      * <p>
350      * Log4j can start threads to perform certain actions like file rollovers, calling this method with a positive timeout will
351      * block until the rollover thread is done.
352      *
353      * @param timeout the maximum time to wait, or 0 which mean that each apppender uses its default timeout, and don't wait for background
354     tasks
355      * @param timeUnit
356      *            the time unit of the timeout argument
357      * @return {@code true} if the logger context terminated and {@code false} if the timeout elapsed before
358      *         termination.
359      * @since 2.7
360      */
361     @Override
362     public boolean stop(final long timeout, final TimeUnit timeUnit) {
363         LOGGER.debug("Stopping LoggerContext[name={}, {}]...", getName(), this);
364         configLock.lock();
365         try {
366             if (this.isStopped()) {
367                 return true;
368             }
369 
370             this.setStopping();
371             try {
372                 Server.unregisterLoggerContext(getName()); // LOG4J2-406, LOG4J2-500
373             } catch (final LinkageError | Exception e) {
374                 // LOG4J2-1506 Hello Android, GAE
375                 LOGGER.error("Unable to unregister MBeans", e);
376             }
377             if (shutdownCallback != null) {
378                 shutdownCallback.cancel();
379                 shutdownCallback = null;
380             }
381             final Configuration prev = configuration;
382             configuration = NULL_CONFIGURATION;
383             updateLoggers();
384             if (prev instanceof LifeCycle2) {
385                 ((LifeCycle2) prev).stop(timeout, timeUnit);
386             } else {
387                 prev.stop();
388             }
389             externalMap.clear();
390             LogManager.getFactory().removeContext(this);
391         } finally {
392             configLock.unlock();
393             this.setStopped();
394         }
395         if (listeners != null) {
396             for (LoggerContextShutdownAware listener : listeners) {
397                 try {
398                     listener.contextShutdown(this);
399                 } catch (Exception ex) {
400                     // Ignore the exception.
401                 }
402             }
403         }
404         LOGGER.debug("Stopped LoggerContext[name={}, {}] with status {}", getName(), this, true);
405         return true;
406     }
407 
408     /**
409      * Gets the name.
410      *
411      * @return the name.
412      */
413     public String getName() {
414         return contextName;
415     }
416 
417     /**
418      * Gets the root logger.
419      *
420      * @return the root logger.
421      */
422     public Logger getRootLogger() {
423         return getLogger(LogManager.ROOT_LOGGER_NAME);
424     }
425 
426     /**
427      * Sets the name.
428      *
429      * @param name the new LoggerContext name
430      * @throws NullPointerException if the specified name is {@code null}
431      */
432     public void setName(final String name) {
433     	contextName = Objects.requireNonNull(name);
434     }
435 
436     @Override
437     public Object getObject(String key) {
438         return externalMap.get(key);
439     }
440 
441     @Override
442     public Object putObject(String key, Object value) {
443         return externalMap.put(key, value);
444     }
445 
446     @Override
447     public Object putObjectIfAbsent(String key, Object value) {
448         return externalMap.putIfAbsent(key, value);
449     }
450 
451     @Override
452     public Object removeObject(String key) {
453         return externalMap.remove(key);
454     }
455 
456     @Override
457     public boolean removeObject(String key, Object value) {
458         return externalMap.remove(key, value);
459     }
460 
461     /**
462      * Sets the external context.
463      *
464      * @param context The external context.
465      */
466     public void setExternalContext(final Object context) {
467         if (context != null) {
468             this.externalMap.put(EXTERNAL_CONTEXT_KEY, context);
469         } else {
470             this.externalMap.remove(EXTERNAL_CONTEXT_KEY);
471         }
472     }
473 
474     /**
475      * Returns the external context.
476      *
477      * @return The external context.
478      */
479     @Override
480     public Object getExternalContext() {
481         return this.externalMap.get(EXTERNAL_CONTEXT_KEY);
482     }
483 
484     /**
485      * Gets a Logger from the Context.
486      *
487      * @param name The name of the Logger to return.
488      * @return The Logger.
489      */
490     @Override
491     public Logger getLogger(final String name) {
492         return getLogger(name, null);
493     }
494 
495     /**
496      * Gets a collection of the current loggers.
497      * <p>
498      * Whether this collection is a copy of the underlying collection or not is undefined. Therefore, modify this
499      * collection at your own risk.
500      * </p>
501      *
502      * @return a collection of the current loggers.
503      */
504     public Collection<Logger> getLoggers() {
505         return loggerRegistry.getLoggers();
506     }
507 
508     /**
509      * Obtains a Logger from the Context.
510      *
511      * @param name The name of the Logger to return.
512      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the
513      *            logger but will log a warning if mismatched.
514      * @return The Logger.
515      */
516     @Override
517     public Logger getLogger(final String name, final MessageFactory messageFactory) {
518         // Note: This is the only method where we add entries to the 'loggerRegistry' ivar.
519         Logger logger = loggerRegistry.getLogger(name, messageFactory);
520         if (logger != null) {
521             AbstractLogger.checkMessageFactory(logger, messageFactory);
522             return logger;
523         }
524 
525         logger = newInstance(this, name, messageFactory);
526         loggerRegistry.putIfAbsent(name, messageFactory, logger);
527         return loggerRegistry.getLogger(name, messageFactory);
528     }
529 
530     /**
531      * Determines if the specified Logger exists.
532      *
533      * @param name The Logger name to search for.
534      * @return True if the Logger exists, false otherwise.
535      */
536     @Override
537     public boolean hasLogger(final String name) {
538         return loggerRegistry.hasLogger(name);
539     }
540 
541     /**
542      * Determines if the specified Logger exists.
543      *
544      * @param name The Logger name to search for.
545      * @return True if the Logger exists, false otherwise.
546      */
547     @Override
548     public boolean hasLogger(final String name, final MessageFactory messageFactory) {
549         return loggerRegistry.hasLogger(name, messageFactory);
550     }
551 
552     /**
553      * Determines if the specified Logger exists.
554      *
555      * @param name The Logger name to search for.
556      * @return True if the Logger exists, false otherwise.
557      */
558     @Override
559     public boolean hasLogger(final String name, final Class<? extends MessageFactory> messageFactoryClass) {
560         return loggerRegistry.hasLogger(name, messageFactoryClass);
561     }
562 
563 	/**
564 	 * Returns the current Configuration. The Configuration will be replaced when a reconfigure occurs.
565 	 *
566 	 * @return The current Configuration, never {@code null}, but may be
567 	 * {@link org.apache.logging.log4j.core.config.NullConfiguration}.
568 	 */
569 	public Configuration getConfiguration() {
570 		return configuration;
571 	}
572 
573     /**
574      * Adds a Filter to the Configuration. Filters that are added through the API will be lost when a reconfigure
575      * occurs.
576      *
577      * @param filter The Filter to add.
578      */
579     public void addFilter(final Filter filter) {
580         configuration.addFilter(filter);
581     }
582 
583     /**
584      * Removes a Filter from the current Configuration.
585      *
586      * @param filter The Filter to remove.
587      */
588     public void removeFilter(final Filter filter) {
589         configuration.removeFilter(filter);
590     }
591 
592     /**
593      * Sets the Configuration to be used.
594      *
595      * @param config The new Configuration.
596      * @return The previous Configuration.
597      */
598     public Configuration setConfiguration(final Configuration config) {
599         if (config == null) {
600             LOGGER.error("No configuration found for context '{}'.", contextName);
601             // No change, return the current configuration.
602             return this.configuration;
603         }
604         configLock.lock();
605         try {
606             final Configuration prev = this.configuration;
607             config.addListener(this);
608 
609             final ConcurrentMap<String, String> map = config.getComponent(Configuration.CONTEXT_PROPERTIES);
610 
611             try { // LOG4J2-719 network access may throw android.os.NetworkOnMainThreadException
612                 map.putIfAbsent("hostName", NetUtils.getLocalHostname());
613             } catch (final Exception ex) {
614                 LOGGER.debug("Ignoring {}, setting hostName to 'unknown'", ex.toString());
615                 map.putIfAbsent("hostName", "unknown");
616             }
617             map.putIfAbsent("contextName", contextName);
618             config.start();
619             this.configuration = config;
620             updateLoggers();
621             if (prev != null) {
622                 prev.removeListener(this);
623                 prev.stop();
624             }
625 
626             firePropertyChangeEvent(new PropertyChangeEvent(this, PROPERTY_CONFIG, prev, config));
627 
628             try {
629                 Server.reregisterMBeansAfterReconfigure();
630             } catch (final LinkageError | Exception e) {
631                 // LOG4J2-716: Android has no java.lang.management
632                 LOGGER.error("Could not reconfigure JMX", e);
633             }
634             // AsyncLoggers update their nanoClock when the configuration changes
635             Log4jLogEvent.setNanoClock(configuration.getNanoClock());
636 
637             return prev;
638         } finally {
639             configLock.unlock();
640         }
641     }
642 
643     private void firePropertyChangeEvent(final PropertyChangeEvent event) {
644         for (final PropertyChangeListener listener : propertyChangeListeners) {
645             listener.propertyChange(event);
646         }
647     }
648 
649     public void addPropertyChangeListener(final PropertyChangeListener listener) {
650         propertyChangeListeners.add(Objects.requireNonNull(listener, "listener"));
651     }
652 
653     public void removePropertyChangeListener(final PropertyChangeListener listener) {
654         propertyChangeListeners.remove(listener);
655     }
656 
657     /**
658      * Returns the initial configuration location or {@code null}. The returned value may not be the location of the
659      * current configuration. Use {@link #getConfiguration()}.{@link Configuration#getConfigurationSource()
660      * getConfigurationSource()}.{@link ConfigurationSource#getLocation() getLocation()} to get the actual source of the
661      * current configuration.
662      *
663      * @return the initial configuration location or {@code null}
664      */
665     public URI getConfigLocation() {
666         return configLocation;
667     }
668 
669     /**
670      * Sets the configLocation to the specified value and reconfigures this context.
671      *
672      * @param configLocation the location of the new configuration
673      */
674     public void setConfigLocation(final URI configLocation) {
675         this.configLocation = configLocation;
676         reconfigure(configLocation);
677     }
678 
679     /**
680      * Reconfigures the context.
681      */
682     private void reconfigure(final URI configURI) {
683         Object externalContext = externalMap.get(EXTERNAL_CONTEXT_KEY);
684         final ClassLoader cl = ClassLoader.class.isInstance(externalContext) ? (ClassLoader) externalContext : null;
685         LOGGER.debug("Reconfiguration started for context[name={}] at URI {} ({}) with optional ClassLoader: {}",
686                 contextName, configURI, this, cl);
687         final Configuration instance = ConfigurationFactory.getInstance().getConfiguration(this, contextName, configURI, cl);
688         if (instance == null) {
689             LOGGER.error("Reconfiguration failed: No configuration found for '{}' at '{}' in '{}'", contextName, configURI, cl);
690         } else {
691             setConfiguration(instance);
692             /*
693              * instance.start(); Configuration old = setConfiguration(instance); updateLoggers(); if (old != null) {
694              * old.stop(); }
695              */
696             final String location = configuration == null ? "?" : String.valueOf(configuration.getConfigurationSource());
697             LOGGER.debug("Reconfiguration complete for context[name={}] at URI {} ({}) with optional ClassLoader: {}",
698                     contextName, location, this, cl);
699         }
700     }
701 
702     /**
703      * Reconfigures the context. Log4j does not remove Loggers during a reconfiguration. Log4j will create new
704      * LoggerConfig objects and Log4j will point the Loggers at the new LoggerConfigs. Log4j will free the old
705      * LoggerConfig, along with old Appenders and Filters.
706      */
707     public void reconfigure() {
708         reconfigure(configLocation);
709     }
710 
711     public void reconfigure(Configuration configuration) {
712         setConfiguration(configuration);
713         ConfigurationSource source = configuration.getConfigurationSource();
714         if (source != null) {
715             URI uri = source.getURI();
716             if (uri != null) {
717                 configLocation = uri;
718             }
719         }
720     }
721 
722     /**
723      * Causes all Loggers to be updated against the current Configuration.
724      */
725     public void updateLoggers() {
726         updateLoggers(this.configuration);
727     }
728 
729     /**
730      * Causes all Logger to be updated against the specified Configuration.
731      *
732      * @param config The Configuration.
733      */
734     public void updateLoggers(final Configuration config) {
735         final Configuration old = this.configuration;
736         for (final Logger logger : loggerRegistry.getLoggers()) {
737             logger.updateConfiguration(config);
738         }
739         firePropertyChangeEvent(new PropertyChangeEvent(this, PROPERTY_CONFIG, old, config));
740     }
741 
742     /**
743      * Causes a reconfiguration to take place when the underlying configuration file changes.
744      *
745      * @param reconfigurable The Configuration that can be reconfigured.
746      */
747     @Override
748 	public synchronized void onChange(final Reconfigurable reconfigurable) {
749 		final long startMillis = System.currentTimeMillis();
750 		LOGGER.debug("Reconfiguration started for context {} ({})", contextName, this);
751 		initApiModule();
752 		final Configuration newConfig = reconfigurable.reconfigure();
753 		if (newConfig != null) {
754 			setConfiguration(newConfig);
755 			LOGGER.debug("Reconfiguration completed for {} ({}) in {} milliseconds.", contextName, this,
756 					System.currentTimeMillis() - startMillis);
757 		} else {
758 			LOGGER.debug("Reconfiguration failed for {} ({}) in {} milliseconds.", contextName, this,
759 					System.currentTimeMillis() - startMillis);
760 		}
761 	}
762 
763     private void initApiModule() {
764         ThreadContextMapFactory.init(); // Or make public and call ThreadContext.init() which calls ThreadContextMapFactory.init().
765     }
766 
767     // LOG4J2-151: changed visibility from private to protected
768     protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) {
769         return new Logger(ctx, name, messageFactory);
770     }
771 
772 }