1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
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.ConcurrentMap;
31  import java.util.concurrent.CopyOnWriteArrayList;
32  import java.util.concurrent.TimeUnit;
33  import java.util.concurrent.locks.Lock;
34  import java.util.concurrent.locks.ReentrantLock;
35  
36  import org.apache.logging.log4j.LogManager;
37  import org.apache.logging.log4j.core.config.Configuration;
38  import org.apache.logging.log4j.core.config.ConfigurationFactory;
39  import org.apache.logging.log4j.core.config.ConfigurationListener;
40  import org.apache.logging.log4j.core.config.ConfigurationSource;
41  import org.apache.logging.log4j.core.config.DefaultConfiguration;
42  import org.apache.logging.log4j.core.config.NullConfiguration;
43  import org.apache.logging.log4j.core.config.Reconfigurable;
44  import org.apache.logging.log4j.core.impl.Log4jLogEvent;
45  import org.apache.logging.log4j.core.jmx.Server;
46  import org.apache.logging.log4j.core.util.Cancellable;
47  import org.apache.logging.log4j.core.util.ExecutorServices;
48  import org.apache.logging.log4j.core.util.Loader;
49  import org.apache.logging.log4j.core.util.NetUtils;
50  import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
51  import org.apache.logging.log4j.message.MessageFactory;
52  import org.apache.logging.log4j.spi.AbstractLogger;
53  import org.apache.logging.log4j.spi.LoggerContextFactory;
54  import org.apache.logging.log4j.spi.LoggerContextShutdownAware;
55  import org.apache.logging.log4j.spi.LoggerContextShutdownEnabled;
56  import org.apache.logging.log4j.spi.LoggerRegistry;
57  import org.apache.logging.log4j.spi.Terminable;
58  import org.apache.logging.log4j.spi.ThreadContextMapFactory;
59  import org.apache.logging.log4j.util.PropertiesUtil;
60  
61  
62  
63  
64  
65  
66  
67  public class LoggerContext extends AbstractLifeCycle
68          implements org.apache.logging.log4j.spi.LoggerContext, AutoCloseable, Terminable, ConfigurationListener,
69          LoggerContextShutdownEnabled {
70  
71      static {
72          try {
73              
74              Loader.loadClass(ExecutorServices.class.getName());
75          } catch (final Exception e) {
76              LOGGER.error("Failed to preload ExecutorServices class.", e);
77          }
78      }
79  
80      
81  
82  
83      public static final String PROPERTY_CONFIG = "config";
84  
85      private static final Configuration NULL_CONFIGURATION = new NullConfiguration();
86  
87      private final LoggerRegistry<Logger> loggerRegistry = new LoggerRegistry<>();
88      private final CopyOnWriteArrayList<PropertyChangeListener> propertyChangeListeners = new CopyOnWriteArrayList<>();
89      private volatile List<LoggerContextShutdownAware> listeners = null;
90  
91      
92  
93  
94  
95      private volatile Configuration configuration = new DefaultConfiguration();
96      private Object externalContext;
97      private String contextName;
98      private volatile URI configLocation;
99      private Cancellable shutdownCallback;
100 
101     private final Lock configLock = new ReentrantLock();
102 
103     
104 
105 
106 
107 
108     public LoggerContext(final String name) {
109         this(name, null, (URI) null);
110     }
111 
112     
113 
114 
115 
116 
117 
118     public LoggerContext(final String name, final Object externalContext) {
119         this(name, externalContext, (URI) null);
120     }
121 
122     
123 
124 
125 
126 
127 
128 
129     public LoggerContext(final String name, final Object externalContext, final URI configLocn) {
130         this.contextName = name;
131         this.externalContext = externalContext;
132         this.configLocation = configLocn;
133     }
134 
135     
136 
137 
138 
139 
140 
141 
142 
143     public LoggerContext(final String name, final Object externalContext, final String configLocn) {
144         this.contextName = name;
145         this.externalContext = externalContext;
146         if (configLocn != null) {
147             URI uri;
148             try {
149                 uri = new File(configLocn).toURI();
150             } catch (final Exception ex) {
151                 uri = null;
152             }
153             configLocation = uri;
154         } else {
155             configLocation = null;
156         }
157     }
158 
159     public void addShutdownListener(LoggerContextShutdownAware listener) {
160         if (listeners == null) {
161             synchronized(this) {
162                 if (listeners == null) {
163                     listeners = Collections.synchronizedList(new ArrayList<LoggerContextShutdownAware>());
164                 }
165             }
166         }
167         listeners.add(listener);
168     }
169 
170     public List<LoggerContextShutdownAware> getListeners() {
171         return listeners;
172     }
173 
174     
175 
176 
177 
178 
179 
180 
181 
182 
183 
184 
185 
186 
187 
188 
189 
190 
191 
192     public static LoggerContext getContext() {
193         return (LoggerContext) LogManager.getContext();
194     }
195 
196     
197 
198 
199 
200 
201 
202 
203 
204 
205 
206 
207 
208 
209 
210 
211 
212 
213     public static LoggerContext getContext(final boolean currentContext) {
214         return (LoggerContext) LogManager.getContext(currentContext);
215     }
216 
217     
218 
219 
220 
221 
222 
223 
224 
225 
226 
227 
228 
229 
230 
231 
232 
233 
234 
235 
236 
237     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
238             final URI configLocation) {
239         return (LoggerContext) LogManager.getContext(loader, currentContext, configLocation);
240     }
241 
242     @Override
243     public void start() {
244         LOGGER.debug("Starting LoggerContext[name={}, {}]...", getName(), this);
245         if (PropertiesUtil.getProperties().getBooleanProperty("log4j.LoggerContext.stacktrace.on.start", false)) {
246             LOGGER.debug("Stack trace to locate invoker",
247                     new Exception("Not a real error, showing stack trace to locate invoker"));
248         }
249         if (configLock.tryLock()) {
250             try {
251                 if (this.isInitialized() || this.isStopped()) {
252                     this.setStarting();
253                     reconfigure();
254                     if (this.configuration.isShutdownHookEnabled()) {
255                         setUpShutdownHook();
256                     }
257                     this.setStarted();
258                 }
259             } finally {
260                 configLock.unlock();
261             }
262         }
263         LOGGER.debug("LoggerContext[name={}, {}] started OK.", getName(), this);
264     }
265 
266     
267 
268 
269 
270 
271     public void start(final Configuration config) {
272         LOGGER.debug("Starting LoggerContext[name={}, {}] with configuration {}...", getName(), this, config);
273         if (configLock.tryLock()) {
274             try {
275                 if (this.isInitialized() || this.isStopped()) {
276                     if (this.configuration.isShutdownHookEnabled()) {
277                         setUpShutdownHook();
278                     }
279                     this.setStarted();
280                 }
281             } finally {
282                 configLock.unlock();
283             }
284         }
285         setConfiguration(config);
286         LOGGER.debug("LoggerContext[name={}, {}] started OK with configuration {}.", getName(), this, config);
287     }
288 
289     private void setUpShutdownHook() {
290         if (shutdownCallback == null) {
291             final LoggerContextFactory factory = LogManager.getFactory();
292             if (factory instanceof ShutdownCallbackRegistry) {
293                 LOGGER.debug(SHUTDOWN_HOOK_MARKER, "Shutdown hook enabled. Registering a new one.");
294                 try {
295                     final long shutdownTimeoutMillis = this.configuration.getShutdownTimeoutMillis();
296                     this.shutdownCallback = ((ShutdownCallbackRegistry) factory).addShutdownCallback(new Runnable() {
297                         @Override
298                         public void run() {
299                             @SuppressWarnings("resource")
300                             final LoggerContext context = LoggerContext.this;
301                             LOGGER.debug(SHUTDOWN_HOOK_MARKER, "Stopping LoggerContext[name={}, {}]",
302                                     context.getName(), context);
303                             context.stop(shutdownTimeoutMillis, TimeUnit.MILLISECONDS);
304                         }
305 
306                         @Override
307                         public String toString() {
308                             return "Shutdown callback for LoggerContext[name=" + LoggerContext.this.getName() + ']';
309                         }
310                     });
311                 } catch (final IllegalStateException e) {
312                     throw new IllegalStateException(
313                             "Unable to register Log4j shutdown hook because JVM is shutting down.", e);
314                 } catch (final SecurityException e) {
315                     LOGGER.error(SHUTDOWN_HOOK_MARKER, "Unable to register shutdown hook due to security restrictions",
316                             e);
317                 }
318             }
319         }
320     }
321 
322     @Override
323     public void close() {
324         stop();
325     }
326 
327     @Override
328     public void terminate() {
329         stop();
330     }
331 
332     
333 
334 
335 
336 
337 
338 
339 
340 
341 
342 
343 
344 
345 
346 
347 
348 
349 
350 
351     @Override
352     public boolean stop(final long timeout, final TimeUnit timeUnit) {
353         LOGGER.debug("Stopping LoggerContext[name={}, {}]...", getName(), this);
354         configLock.lock();
355         try {
356             if (this.isStopped()) {
357                 return true;
358             }
359 
360             this.setStopping();
361             try {
362                 Server.unregisterLoggerContext(getName()); 
363             } catch (final LinkageError | Exception e) {
364                 
365                 LOGGER.error("Unable to unregister MBeans", e);
366             }
367             if (shutdownCallback != null) {
368                 shutdownCallback.cancel();
369                 shutdownCallback = null;
370             }
371             final Configuration prev = configuration;
372             configuration = NULL_CONFIGURATION;
373             updateLoggers();
374             if (prev instanceof LifeCycle2) {
375                 ((LifeCycle2) prev).stop(timeout, timeUnit);
376             } else {
377                 prev.stop();
378             }
379             externalContext = null;
380             LogManager.getFactory().removeContext(this);
381         } finally {
382             configLock.unlock();
383             this.setStopped();
384         }
385         if (listeners != null) {
386             for (LoggerContextShutdownAware listener : listeners) {
387                 try {
388                     listener.contextShutdown(this);
389                 } catch (Exception ex) {
390                     
391                 }
392             }
393         }
394         LOGGER.debug("Stopped LoggerContext[name={}, {}] with status {}", getName(), this, true);
395         return true;
396     }
397 
398     
399 
400 
401 
402 
403     public String getName() {
404         return contextName;
405     }
406 
407     
408 
409 
410 
411 
412     public Logger getRootLogger() {
413         return getLogger(LogManager.ROOT_LOGGER_NAME);
414     }
415 
416     
417 
418 
419 
420 
421 
422     public void setName(final String name) {
423     	contextName = Objects.requireNonNull(name);
424     }
425 
426     
427 
428 
429 
430 
431     public void setExternalContext(final Object context) {
432         this.externalContext = context;
433     }
434 
435     
436 
437 
438 
439 
440     @Override
441     public Object getExternalContext() {
442         return this.externalContext;
443     }
444 
445     
446 
447 
448 
449 
450 
451     @Override
452     public Logger getLogger(final String name) {
453         return getLogger(name, null);
454     }
455 
456     
457 
458 
459 
460 
461 
462 
463 
464 
465     public Collection<Logger> getLoggers() {
466         return loggerRegistry.getLoggers();
467     }
468 
469     
470 
471 
472 
473 
474 
475 
476 
477     @Override
478     public Logger getLogger(final String name, final MessageFactory messageFactory) {
479         
480         Logger logger = loggerRegistry.getLogger(name, messageFactory);
481         if (logger != null) {
482             AbstractLogger.checkMessageFactory(logger, messageFactory);
483             return logger;
484         }
485 
486         logger = newInstance(this, name, messageFactory);
487         loggerRegistry.putIfAbsent(name, messageFactory, logger);
488         return loggerRegistry.getLogger(name, messageFactory);
489     }
490 
491     
492 
493 
494 
495 
496 
497     @Override
498     public boolean hasLogger(final String name) {
499         return loggerRegistry.hasLogger(name);
500     }
501 
502     
503 
504 
505 
506 
507 
508     @Override
509     public boolean hasLogger(final String name, final MessageFactory messageFactory) {
510         return loggerRegistry.hasLogger(name, messageFactory);
511     }
512 
513     
514 
515 
516 
517 
518 
519     @Override
520     public boolean hasLogger(final String name, final Class<? extends MessageFactory> messageFactoryClass) {
521         return loggerRegistry.hasLogger(name, messageFactoryClass);
522     }
523 
524 	
525 
526 
527 
528 
529 
530 	public Configuration getConfiguration() {
531 		return configuration;
532 	}
533 
534     
535 
536 
537 
538 
539 
540     public void addFilter(final Filter filter) {
541         configuration.addFilter(filter);
542     }
543 
544     
545 
546 
547 
548 
549     public void removeFilter(final Filter filter) {
550         configuration.removeFilter(filter);
551     }
552 
553     
554 
555 
556 
557 
558 
559     public Configuration setConfiguration(final Configuration config) {
560         if (config == null) {
561             LOGGER.error("No configuration found for context '{}'.", contextName);
562             
563             return this.configuration;
564         }
565         configLock.lock();
566         try {
567             final Configuration prev = this.configuration;
568             config.addListener(this);
569 
570             final ConcurrentMap<String, String> map = config.getComponent(Configuration.CONTEXT_PROPERTIES);
571 
572             try { 
573                 map.putIfAbsent("hostName", NetUtils.getLocalHostname());
574             } catch (final Exception ex) {
575                 LOGGER.debug("Ignoring {}, setting hostName to 'unknown'", ex.toString());
576                 map.putIfAbsent("hostName", "unknown");
577             }
578             map.putIfAbsent("contextName", contextName);
579             config.start();
580             this.configuration = config;
581             updateLoggers();
582             if (prev != null) {
583                 prev.removeListener(this);
584                 prev.stop();
585             }
586 
587             firePropertyChangeEvent(new PropertyChangeEvent(this, PROPERTY_CONFIG, prev, config));
588 
589             try {
590                 Server.reregisterMBeansAfterReconfigure();
591             } catch (final LinkageError | Exception e) {
592                 
593                 LOGGER.error("Could not reconfigure JMX", e);
594             }
595             
596             Log4jLogEvent.setNanoClock(configuration.getNanoClock());
597 
598             return prev;
599         } finally {
600             configLock.unlock();
601         }
602     }
603 
604     private void firePropertyChangeEvent(final PropertyChangeEvent event) {
605         for (final PropertyChangeListener listener : propertyChangeListeners) {
606             listener.propertyChange(event);
607         }
608     }
609 
610     public void addPropertyChangeListener(final PropertyChangeListener listener) {
611         propertyChangeListeners.add(Objects.requireNonNull(listener, "listener"));
612     }
613 
614     public void removePropertyChangeListener(final PropertyChangeListener listener) {
615         propertyChangeListeners.remove(listener);
616     }
617 
618     
619 
620 
621 
622 
623 
624 
625 
626     public URI getConfigLocation() {
627         return configLocation;
628     }
629 
630     
631 
632 
633 
634 
635     public void setConfigLocation(final URI configLocation) {
636         this.configLocation = configLocation;
637         reconfigure(configLocation);
638     }
639 
640     
641 
642 
643     private void reconfigure(final URI configURI) {
644         final ClassLoader cl = ClassLoader.class.isInstance(externalContext) ? (ClassLoader) externalContext : null;
645         LOGGER.debug("Reconfiguration started for context[name={}] at URI {} ({}) with optional ClassLoader: {}",
646                 contextName, configURI, this, cl);
647         final Configuration instance = ConfigurationFactory.getInstance().getConfiguration(this, contextName, configURI, cl);
648         if (instance == null) {
649             LOGGER.error("Reconfiguration failed: No configuration found for '{}' at '{}' in '{}'", contextName, configURI, cl);
650         } else {
651             setConfiguration(instance);
652             
653 
654 
655 
656             final String location = configuration == null ? "?" : String.valueOf(configuration.getConfigurationSource());
657             LOGGER.debug("Reconfiguration complete for context[name={}] at URI {} ({}) with optional ClassLoader: {}",
658                     contextName, location, this, cl);
659         }
660     }
661 
662     
663 
664 
665 
666 
667     public void reconfigure() {
668         reconfigure(configLocation);
669     }
670 
671     
672 
673 
674     public void updateLoggers() {
675         updateLoggers(this.configuration);
676     }
677 
678     
679 
680 
681 
682 
683     public void updateLoggers(final Configuration config) {
684         final Configuration old = this.configuration;
685         for (final Logger logger : loggerRegistry.getLoggers()) {
686             logger.updateConfiguration(config);
687         }
688         firePropertyChangeEvent(new PropertyChangeEvent(this, PROPERTY_CONFIG, old, config));
689     }
690 
691     
692 
693 
694 
695 
696     @Override
697 	public synchronized void onChange(final Reconfigurable reconfigurable) {
698 		final long startMillis = System.currentTimeMillis();
699 		LOGGER.debug("Reconfiguration started for context {} ({})", contextName, this);
700 		initApiModule();
701 		final Configuration newConfig = reconfigurable.reconfigure();
702 		if (newConfig != null) {
703 			setConfiguration(newConfig);
704 			LOGGER.debug("Reconfiguration completed for {} ({}) in {} milliseconds.", contextName, this,
705 					System.currentTimeMillis() - startMillis);
706 		} else {
707 			LOGGER.debug("Reconfiguration failed for {} ({}) in {} milliseconds.", contextName, this,
708 					System.currentTimeMillis() - startMillis);
709 		}
710 	}
711 
712     private void initApiModule() {
713         ThreadContextMapFactory.init(); 
714     }
715 
716     
717     protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) {
718         return new Logger(ctx, name, messageFactory);
719     }
720 
721 }