| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache license, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the license for the specific language governing permissions and |
| * limitations under the license. |
| */ |
| package org.apache.logging.log4j.core; |
| |
| import static org.apache.logging.log4j.core.util.ShutdownCallbackRegistry.SHUTDOWN_HOOK_MARKER; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.io.File; |
| import java.net.URI; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| import java.util.concurrent.CopyOnWriteArrayList; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.locks.Lock; |
| import java.util.concurrent.locks.ReentrantLock; |
| |
| import org.apache.logging.log4j.LogManager; |
| import org.apache.logging.log4j.core.config.Configuration; |
| import org.apache.logging.log4j.core.config.ConfigurationFactory; |
| import org.apache.logging.log4j.core.config.ConfigurationListener; |
| import org.apache.logging.log4j.core.config.ConfigurationSource; |
| import org.apache.logging.log4j.core.config.DefaultConfiguration; |
| import org.apache.logging.log4j.core.config.NullConfiguration; |
| import org.apache.logging.log4j.core.config.Reconfigurable; |
| import org.apache.logging.log4j.core.impl.Log4jLogEvent; |
| import org.apache.logging.log4j.core.impl.ThreadContextDataInjector; |
| import org.apache.logging.log4j.core.jmx.Server; |
| import org.apache.logging.log4j.core.util.Cancellable; |
| import org.apache.logging.log4j.core.util.ExecutorServices; |
| import org.apache.logging.log4j.core.util.Loader; |
| import org.apache.logging.log4j.core.util.NetUtils; |
| import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry; |
| import org.apache.logging.log4j.message.MessageFactory; |
| import org.apache.logging.log4j.spi.AbstractLogger; |
| import org.apache.logging.log4j.spi.LoggerContextFactory; |
| import org.apache.logging.log4j.spi.LoggerContextShutdownAware; |
| import org.apache.logging.log4j.spi.LoggerContextShutdownEnabled; |
| import org.apache.logging.log4j.spi.LoggerRegistry; |
| import org.apache.logging.log4j.spi.Terminable; |
| import org.apache.logging.log4j.spi.ThreadContextMapFactory; |
| import org.apache.logging.log4j.util.PropertiesUtil; |
| |
| /** |
| * The LoggerContext is the anchor for the logging system. It maintains a list of all the loggers requested by |
| * applications and a reference to the Configuration. The Configuration will contain the configured loggers, appenders, |
| * filters, etc and will be atomically updated whenever a reconfigure occurs. |
| */ |
| public class LoggerContext extends AbstractLifeCycle |
| implements org.apache.logging.log4j.spi.LoggerContext, AutoCloseable, Terminable, ConfigurationListener, |
| LoggerContextShutdownEnabled { |
| |
| static { |
| try { |
| // LOG4J2-1642 preload ExecutorServices as it is used in shutdown hook |
| Loader.loadClass(ExecutorServices.class.getName()); |
| } catch (final Exception e) { |
| LOGGER.error("Failed to preload ExecutorServices class.", e); |
| } |
| } |
| |
| /** |
| * Property name of the property change event fired if the configuration is changed. |
| */ |
| public static final String PROPERTY_CONFIG = "config"; |
| |
| private static final Configuration NULL_CONFIGURATION = new NullConfiguration(); |
| |
| private final LoggerRegistry<Logger> loggerRegistry = new LoggerRegistry<>(); |
| private final CopyOnWriteArrayList<PropertyChangeListener> propertyChangeListeners = new CopyOnWriteArrayList<>(); |
| private volatile List<LoggerContextShutdownAware> listeners; |
| |
| /** |
| * The Configuration is volatile to guarantee that initialization of the Configuration has completed before the |
| * reference is updated. |
| */ |
| private volatile Configuration configuration = new DefaultConfiguration(); |
| private static final String EXTERNAL_CONTEXT_KEY = "__EXTERNAL_CONTEXT_KEY__"; |
| private final ConcurrentMap<String, Object> externalMap = new ConcurrentHashMap<>(); |
| private String contextName; |
| private volatile URI configLocation; |
| private Cancellable shutdownCallback; |
| |
| private final Lock configLock = new ReentrantLock(); |
| |
| /** |
| * Constructor taking only a name. |
| * |
| * @param name The context name. |
| */ |
| public LoggerContext(final String name) { |
| this(name, null, (URI) null); |
| } |
| |
| /** |
| * Constructor taking a name and a reference to an external context. |
| * |
| * @param name The context name. |
| * @param externalContext The external context. |
| */ |
| public LoggerContext(final String name, final Object externalContext) { |
| this(name, externalContext, (URI) null); |
| } |
| |
| /** |
| * Constructor taking a name, external context and a configuration URI. |
| * |
| * @param name The context name. |
| * @param externalContext The external context. |
| * @param configLocn The location of the configuration as a URI. |
| */ |
| public LoggerContext(final String name, final Object externalContext, final URI configLocn) { |
| this.contextName = name; |
| if (externalContext == null) { |
| externalMap.remove(EXTERNAL_CONTEXT_KEY); |
| } else { |
| externalMap.put(EXTERNAL_CONTEXT_KEY, externalContext); |
| } |
| this.configLocation = configLocn; |
| Thread runner = new Thread(new ThreadContextDataTask(), "Thread Context Data Task"); |
| runner.setDaemon(true); |
| runner.start(); |
| } |
| |
| /** |
| * Constructor taking a name external context and a configuration location String. The location must be resolvable |
| * to a File. |
| * |
| * @param name The configuration location. |
| * @param externalContext The external context. |
| * @param configLocn The configuration location. |
| */ |
| public LoggerContext(final String name, final Object externalContext, final String configLocn) { |
| this.contextName = name; |
| if (externalContext == null) { |
| externalMap.remove(EXTERNAL_CONTEXT_KEY); |
| } else { |
| externalMap.put(EXTERNAL_CONTEXT_KEY, externalContext); |
| } |
| if (configLocn != null) { |
| URI uri; |
| try { |
| uri = new File(configLocn).toURI(); |
| } catch (final Exception ex) { |
| uri = null; |
| } |
| configLocation = uri; |
| } else { |
| configLocation = null; |
| } |
| Thread runner = new Thread(new ThreadContextDataTask(), "Thread Context Data Task"); |
| runner.setDaemon(true); |
| runner.start(); |
| } |
| |
| public void addShutdownListener(LoggerContextShutdownAware listener) { |
| if (listeners == null) { |
| synchronized(this) { |
| if (listeners == null) { |
| listeners = new CopyOnWriteArrayList<>(); |
| } |
| } |
| } |
| listeners.add(listener); |
| } |
| |
| public List<LoggerContextShutdownAware> getListeners() { |
| return listeners; |
| } |
| |
| /** |
| * Returns the current LoggerContext. |
| * <p> |
| * Avoids the type cast for: |
| * </p> |
| * |
| * <pre> |
| * (LoggerContext) LogManager.getContext(); |
| * </pre> |
| * |
| * <p> |
| * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger for the |
| * calling class. |
| * </p> |
| * |
| * @return The current LoggerContext. |
| * @see LogManager#getContext() |
| */ |
| public static LoggerContext getContext() { |
| return (LoggerContext) LogManager.getContext(); |
| } |
| |
| /** |
| * Returns a LoggerContext. |
| * <p> |
| * Avoids the type cast for: |
| * </p> |
| * |
| * <pre> |
| * (LoggerContext) LogManager.getContext(currentContext); |
| * </pre> |
| * |
| * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For |
| * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be |
| * returned and if the caller is a class in the container's classpath then a different LoggerContext may |
| * be returned. If true then only a single LoggerContext will be returned. |
| * @return a LoggerContext. |
| * @see LogManager#getContext(boolean) |
| */ |
| public static LoggerContext getContext(final boolean currentContext) { |
| return (LoggerContext) LogManager.getContext(currentContext); |
| } |
| |
| /** |
| * Returns a LoggerContext. |
| * <p> |
| * Avoids the type cast for: |
| * </p> |
| * |
| * <pre> |
| * (LoggerContext) LogManager.getContext(loader, currentContext, configLocation); |
| * </pre> |
| * |
| * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate |
| * ClassLoader. |
| * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For |
| * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be |
| * returned and if the caller is a class in the container's classpath then a different LoggerContext may |
| * be returned. If true then only a single LoggerContext will be returned. |
| * @param configLocation The URI for the configuration to use. |
| * @return a LoggerContext. |
| * @see LogManager#getContext(ClassLoader, boolean, URI) |
| */ |
| public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, |
| final URI configLocation) { |
| return (LoggerContext) LogManager.getContext(loader, currentContext, configLocation); |
| } |
| |
| @Override |
| public void start() { |
| LOGGER.debug("Starting LoggerContext[name={}, {}]...", getName(), this); |
| if (PropertiesUtil.getProperties().getBooleanProperty("log4j.LoggerContext.stacktrace.on.start", false)) { |
| LOGGER.debug("Stack trace to locate invoker", |
| new Exception("Not a real error, showing stack trace to locate invoker")); |
| } |
| if (configLock.tryLock()) { |
| try { |
| if (this.isInitialized() || this.isStopped()) { |
| this.setStarting(); |
| reconfigure(); |
| if (this.configuration.isShutdownHookEnabled()) { |
| setUpShutdownHook(); |
| } |
| this.setStarted(); |
| } |
| } finally { |
| configLock.unlock(); |
| } |
| } |
| LOGGER.debug("LoggerContext[name={}, {}] started OK.", getName(), this); |
| } |
| |
| /** |
| * Starts with a specific configuration. |
| * |
| * @param config The new Configuration. |
| */ |
| public void start(final Configuration config) { |
| LOGGER.debug("Starting LoggerContext[name={}, {}] with configuration {}...", getName(), this, config); |
| if (configLock.tryLock()) { |
| try { |
| if (this.isInitialized() || this.isStopped()) { |
| if (this.configuration.isShutdownHookEnabled()) { |
| setUpShutdownHook(); |
| } |
| this.setStarted(); |
| } |
| } finally { |
| configLock.unlock(); |
| } |
| } |
| setConfiguration(config); |
| LOGGER.debug("LoggerContext[name={}, {}] started OK with configuration {}.", getName(), this, config); |
| } |
| |
| private void setUpShutdownHook() { |
| if (shutdownCallback == null) { |
| final LoggerContextFactory factory = LogManager.getFactory(); |
| if (factory instanceof ShutdownCallbackRegistry) { |
| LOGGER.debug(SHUTDOWN_HOOK_MARKER, "Shutdown hook enabled. Registering a new one."); |
| try { |
| final long shutdownTimeoutMillis = this.configuration.getShutdownTimeoutMillis(); |
| this.shutdownCallback = ((ShutdownCallbackRegistry) factory).addShutdownCallback(new Runnable() { |
| @Override |
| public void run() { |
| @SuppressWarnings("resource") |
| final LoggerContext context = LoggerContext.this; |
| LOGGER.debug(SHUTDOWN_HOOK_MARKER, "Stopping LoggerContext[name={}, {}]", |
| context.getName(), context); |
| context.stop(shutdownTimeoutMillis, TimeUnit.MILLISECONDS); |
| } |
| |
| @Override |
| public String toString() { |
| return "Shutdown callback for LoggerContext[name=" + LoggerContext.this.getName() + ']'; |
| } |
| }); |
| } catch (final IllegalStateException e) { |
| throw new IllegalStateException( |
| "Unable to register Log4j shutdown hook because JVM is shutting down.", e); |
| } catch (final SecurityException e) { |
| LOGGER.error(SHUTDOWN_HOOK_MARKER, "Unable to register shutdown hook due to security restrictions", |
| e); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void close() { |
| stop(); |
| } |
| |
| @Override |
| public void terminate() { |
| stop(); |
| } |
| |
| /** |
| * Blocks until all Log4j tasks have completed execution after a shutdown request and all appenders have shut down, |
| * or the timeout occurs, or the current thread is interrupted, whichever happens first. |
| * <p> |
| * Not all appenders will honor this, it is a hint and not an absolute guarantee that the this method not block longer. |
| * Setting timeout too low increase the risk of losing outstanding log events not yet written to the final |
| * destination. |
| * <p> |
| * Log4j can start threads to perform certain actions like file rollovers, calling this method with a positive timeout will |
| * block until the rollover thread is done. |
| * |
| * @param timeout the maximum time to wait, or 0 which mean that each apppender uses its default timeout, and don't wait for background |
| tasks |
| * @param timeUnit |
| * the time unit of the timeout argument |
| * @return {@code true} if the logger context terminated and {@code false} if the timeout elapsed before |
| * termination. |
| * @since 2.7 |
| */ |
| @Override |
| public boolean stop(final long timeout, final TimeUnit timeUnit) { |
| LOGGER.debug("Stopping LoggerContext[name={}, {}]...", getName(), this); |
| configLock.lock(); |
| try { |
| if (this.isStopped()) { |
| return true; |
| } |
| |
| this.setStopping(); |
| try { |
| Server.unregisterLoggerContext(getName()); // LOG4J2-406, LOG4J2-500 |
| } catch (final LinkageError | Exception e) { |
| // LOG4J2-1506 Hello Android, GAE |
| LOGGER.error("Unable to unregister MBeans", e); |
| } |
| if (shutdownCallback != null) { |
| shutdownCallback.cancel(); |
| shutdownCallback = null; |
| } |
| final Configuration prev = configuration; |
| configuration = NULL_CONFIGURATION; |
| updateLoggers(); |
| prev.stop(timeout, timeUnit); |
| externalMap.clear(); |
| LogManager.getFactory().removeContext(this); |
| } finally { |
| configLock.unlock(); |
| this.setStopped(); |
| } |
| if (listeners != null) { |
| for (LoggerContextShutdownAware listener : listeners) { |
| try { |
| listener.contextShutdown(this); |
| } catch (Exception ex) { |
| // Ignore the exception. |
| } |
| } |
| } |
| LOGGER.debug("Stopped LoggerContext[name={}, {}] with status {}", getName(), this, true); |
| return true; |
| } |
| |
| /** |
| * Gets the name. |
| * |
| * @return the name. |
| */ |
| public String getName() { |
| return contextName; |
| } |
| |
| /** |
| * Gets the root logger. |
| * |
| * @return the root logger. |
| */ |
| public Logger getRootLogger() { |
| return getLogger(LogManager.ROOT_LOGGER_NAME); |
| } |
| |
| /** |
| * Sets the name. |
| * |
| * @param name the new LoggerContext name |
| * @throws NullPointerException if the specified name is {@code null} |
| */ |
| public void setName(final String name) { |
| contextName = Objects.requireNonNull(name); |
| } |
| |
| @Override |
| public Object getObject(String key) { |
| return externalMap.get(key); |
| } |
| |
| @Override |
| public Object putObject(String key, Object value) { |
| return externalMap.put(key, value); |
| } |
| |
| @Override |
| public Object putObjectIfAbsent(String key, Object value) { |
| return externalMap.putIfAbsent(key, value); |
| } |
| |
| @Override |
| public Object removeObject(String key) { |
| return externalMap.remove(key); |
| } |
| |
| @Override |
| public boolean removeObject(String key, Object value) { |
| return externalMap.remove(key, value); |
| } |
| |
| /** |
| * Sets the external context. |
| * |
| * @param context The external context. |
| */ |
| public void setExternalContext(final Object context) { |
| if (context != null) { |
| this.externalMap.put(EXTERNAL_CONTEXT_KEY, context); |
| } else { |
| this.externalMap.remove(EXTERNAL_CONTEXT_KEY); |
| } |
| } |
| |
| /** |
| * Returns the external context. |
| * |
| * @return The external context. |
| */ |
| @Override |
| public Object getExternalContext() { |
| return this.externalMap.get(EXTERNAL_CONTEXT_KEY); |
| } |
| |
| /** |
| * Gets a Logger from the Context. |
| * |
| * @param name The name of the Logger to return. |
| * @return The Logger. |
| */ |
| @Override |
| public Logger getLogger(final String name) { |
| return getLogger(name, null); |
| } |
| |
| /** |
| * Gets a collection of the current loggers. |
| * <p> |
| * Whether this collection is a copy of the underlying collection or not is undefined. Therefore, modify this |
| * collection at your own risk. |
| * </p> |
| * |
| * @return a collection of the current loggers. |
| */ |
| public Collection<Logger> getLoggers() { |
| return loggerRegistry.getLoggers(); |
| } |
| |
| /** |
| * Obtains a Logger from the Context. |
| * |
| * @param name The name of the Logger to return. |
| * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the |
| * logger but will log a warning if mismatched. |
| * @return The Logger. |
| */ |
| @Override |
| public Logger getLogger(final String name, final MessageFactory messageFactory) { |
| // Note: This is the only method where we add entries to the 'loggerRegistry' ivar. |
| Logger logger = loggerRegistry.getLogger(name, messageFactory); |
| if (logger != null) { |
| AbstractLogger.checkMessageFactory(logger, messageFactory); |
| return logger; |
| } |
| |
| logger = newInstance(this, name, messageFactory); |
| loggerRegistry.putIfAbsent(name, messageFactory, logger); |
| return loggerRegistry.getLogger(name, messageFactory); |
| } |
| |
| /** |
| * Determines if the specified Logger exists. |
| * |
| * @param name The Logger name to search for. |
| * @return True if the Logger exists, false otherwise. |
| */ |
| @Override |
| public boolean hasLogger(final String name) { |
| return loggerRegistry.hasLogger(name); |
| } |
| |
| /** |
| * Determines if the specified Logger exists. |
| * |
| * @param name The Logger name to search for. |
| * @return True if the Logger exists, false otherwise. |
| */ |
| @Override |
| public boolean hasLogger(final String name, final MessageFactory messageFactory) { |
| return loggerRegistry.hasLogger(name, messageFactory); |
| } |
| |
| /** |
| * Determines if the specified Logger exists. |
| * |
| * @param name The Logger name to search for. |
| * @return True if the Logger exists, false otherwise. |
| */ |
| @Override |
| public boolean hasLogger(final String name, final Class<? extends MessageFactory> messageFactoryClass) { |
| return loggerRegistry.hasLogger(name, messageFactoryClass); |
| } |
| |
| /** |
| * Returns the current Configuration. The Configuration will be replaced when a reconfigure occurs. |
| * |
| * @return The current Configuration, never {@code null}, but may be |
| * {@link org.apache.logging.log4j.core.config.NullConfiguration}. |
| */ |
| public Configuration getConfiguration() { |
| return configuration; |
| } |
| |
| /** |
| * Adds a Filter to the Configuration. Filters that are added through the API will be lost when a reconfigure |
| * occurs. |
| * |
| * @param filter The Filter to add. |
| */ |
| public void addFilter(final Filter filter) { |
| configuration.addFilter(filter); |
| } |
| |
| /** |
| * Removes a Filter from the current Configuration. |
| * |
| * @param filter The Filter to remove. |
| */ |
| public void removeFilter(final Filter filter) { |
| configuration.removeFilter(filter); |
| } |
| |
| /** |
| * Sets the Configuration to be used. |
| * |
| * @param config The new Configuration. |
| * @return The previous Configuration. |
| */ |
| public Configuration setConfiguration(final Configuration config) { |
| if (config == null) { |
| LOGGER.error("No configuration found for context '{}'.", contextName); |
| // No change, return the current configuration. |
| return this.configuration; |
| } |
| configLock.lock(); |
| try { |
| final Configuration prev = this.configuration; |
| config.addListener(this); |
| |
| final ConcurrentMap<String, String> map = config.getComponent(Configuration.CONTEXT_PROPERTIES); |
| |
| try { // LOG4J2-719 network access may throw android.os.NetworkOnMainThreadException |
| map.putIfAbsent("hostName", NetUtils.getLocalHostname()); |
| } catch (final Exception ex) { |
| LOGGER.debug("Ignoring {}, setting hostName to 'unknown'", ex.toString()); |
| map.putIfAbsent("hostName", "unknown"); |
| } |
| map.putIfAbsent("contextName", contextName); |
| config.start(); |
| this.configuration = config; |
| updateLoggers(); |
| if (prev != null) { |
| prev.removeListener(this); |
| prev.stop(); |
| } |
| |
| firePropertyChangeEvent(new PropertyChangeEvent(this, PROPERTY_CONFIG, prev, config)); |
| |
| try { |
| Server.reregisterMBeansAfterReconfigure(); |
| } catch (final LinkageError | Exception e) { |
| // LOG4J2-716: Android has no java.lang.management |
| LOGGER.error("Could not reconfigure JMX", e); |
| } |
| // AsyncLoggers update their nanoClock when the configuration changes |
| Log4jLogEvent.setNanoClock(configuration.getNanoClock()); |
| |
| return prev; |
| } finally { |
| configLock.unlock(); |
| } |
| } |
| |
| private void firePropertyChangeEvent(final PropertyChangeEvent event) { |
| for (final PropertyChangeListener listener : propertyChangeListeners) { |
| listener.propertyChange(event); |
| } |
| } |
| |
| public void addPropertyChangeListener(final PropertyChangeListener listener) { |
| propertyChangeListeners.add(Objects.requireNonNull(listener, "listener")); |
| } |
| |
| public void removePropertyChangeListener(final PropertyChangeListener listener) { |
| propertyChangeListeners.remove(listener); |
| } |
| |
| /** |
| * Returns the initial configuration location or {@code null}. The returned value may not be the location of the |
| * current configuration. Use {@link #getConfiguration()}.{@link Configuration#getConfigurationSource() |
| * getConfigurationSource()}.{@link ConfigurationSource#getLocation() getLocation()} to get the actual source of the |
| * current configuration. |
| * |
| * @return the initial configuration location or {@code null} |
| */ |
| public URI getConfigLocation() { |
| return configLocation; |
| } |
| |
| /** |
| * Sets the configLocation to the specified value and reconfigures this context. |
| * |
| * @param configLocation the location of the new configuration |
| */ |
| public void setConfigLocation(final URI configLocation) { |
| this.configLocation = configLocation; |
| reconfigure(configLocation); |
| } |
| |
| /** |
| * Reconfigures the context. |
| */ |
| private void reconfigure(final URI configURI) { |
| Object externalContext = externalMap.get(EXTERNAL_CONTEXT_KEY); |
| final ClassLoader cl = ClassLoader.class.isInstance(externalContext) ? (ClassLoader) externalContext : null; |
| LOGGER.debug("Reconfiguration started for context[name={}] at URI {} ({}) with optional ClassLoader: {}", |
| contextName, configURI, this, cl); |
| final Configuration instance = ConfigurationFactory.getInstance().getConfiguration(this, contextName, configURI, cl); |
| if (instance == null) { |
| LOGGER.error("Reconfiguration failed: No configuration found for '{}' at '{}' in '{}'", contextName, configURI, cl); |
| } else { |
| setConfiguration(instance); |
| /* |
| * instance.start(); Configuration old = setConfiguration(instance); updateLoggers(); if (old != null) { |
| * old.stop(); } |
| */ |
| final String location = configuration == null ? "?" : String.valueOf(configuration.getConfigurationSource()); |
| LOGGER.debug("Reconfiguration complete for context[name={}] at URI {} ({}) with optional ClassLoader: {}", |
| contextName, location, this, cl); |
| } |
| } |
| |
| /** |
| * Reconfigures the context. Log4j does not remove Loggers during a reconfiguration. Log4j will create new |
| * LoggerConfig objects and Log4j will point the Loggers at the new LoggerConfigs. Log4j will free the old |
| * LoggerConfig, along with old Appenders and Filters. |
| */ |
| public void reconfigure() { |
| reconfigure(configLocation); |
| } |
| |
| public void reconfigure(Configuration configuration) { |
| setConfiguration(configuration); |
| ConfigurationSource source = configuration.getConfigurationSource(); |
| if (source != null) { |
| URI uri = source.getURI(); |
| if (uri != null) { |
| configLocation = uri; |
| } |
| } |
| } |
| |
| /** |
| * Causes all Loggers to be updated against the current Configuration. |
| */ |
| public void updateLoggers() { |
| updateLoggers(this.configuration); |
| } |
| |
| /** |
| * Causes all Logger to be updated against the specified Configuration. |
| * |
| * @param config The Configuration. |
| */ |
| public void updateLoggers(final Configuration config) { |
| final Configuration old = this.configuration; |
| for (final Logger logger : loggerRegistry.getLoggers()) { |
| logger.updateConfiguration(config); |
| } |
| firePropertyChangeEvent(new PropertyChangeEvent(this, PROPERTY_CONFIG, old, config)); |
| } |
| |
| /** |
| * Causes a reconfiguration to take place when the underlying configuration file changes. |
| * |
| * @param reconfigurable The Configuration that can be reconfigured. |
| */ |
| @Override |
| public synchronized void onChange(final Reconfigurable reconfigurable) { |
| final long startMillis = System.currentTimeMillis(); |
| LOGGER.debug("Reconfiguration started for context {} ({})", contextName, this); |
| initApiModule(); |
| final Configuration newConfig = reconfigurable.reconfigure(); |
| if (newConfig != null) { |
| setConfiguration(newConfig); |
| LOGGER.debug("Reconfiguration completed for {} ({}) in {} milliseconds.", contextName, this, |
| System.currentTimeMillis() - startMillis); |
| } else { |
| LOGGER.debug("Reconfiguration failed for {} ({}) in {} milliseconds.", contextName, this, |
| System.currentTimeMillis() - startMillis); |
| } |
| } |
| |
| private void initApiModule() { |
| ThreadContextMapFactory.init(); // Or make public and call ThreadContext.init() which calls ThreadContextMapFactory.init(). |
| } |
| |
| // LOG4J2-151: changed visibility from private to protected |
| protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) { |
| return new Logger(ctx, name, messageFactory); |
| } |
| |
| private static class ThreadContextDataTask implements Runnable { |
| |
| @Override |
| public void run() { |
| LOGGER.debug("Initializing Thread Context Data Service Providers"); |
| ThreadContextDataInjector.initServiceProviders(); |
| LOGGER.debug("Thread Context Data Service Provider initialization complete"); |
| } |
| } |
| } |