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