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