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.NetUtils;
46 import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
47 import org.apache.logging.log4j.message.MessageFactory;
48 import org.apache.logging.log4j.spi.AbstractLogger;
49 import org.apache.logging.log4j.spi.LoggerContextFactory;
50 import org.apache.logging.log4j.spi.LoggerRegistry;
51 import org.apache.logging.log4j.spi.Terminable;
52 import org.apache.logging.log4j.spi.ThreadContextMapFactory;
53 import org.apache.logging.log4j.util.LoaderUtil;
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 LoaderUtil.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 public Configuration getConfiguration() {
499 return configuration;
500 }
501
502
503
504
505
506
507
508 public void addFilter(final Filter filter) {
509 configuration.addFilter(filter);
510 }
511
512
513
514
515
516
517 public void removeFilter(final Filter filter) {
518 configuration.removeFilter(filter);
519 }
520
521
522
523
524
525
526
527 private Configuration setConfiguration(final Configuration config) {
528 if (config == null) {
529 LOGGER.error("No configuration found for context '{}'.", contextName);
530
531 return this.configuration;
532 }
533 configLock.lock();
534 try {
535 final Configuration prev = this.configuration;
536 config.addListener(this);
537
538 final ConcurrentMap<String, String> map = config.getComponent(Configuration.CONTEXT_PROPERTIES);
539
540 try {
541 map.putIfAbsent("hostName", NetUtils.getLocalHostname());
542 } catch (final Exception ex) {
543 LOGGER.debug("Ignoring {}, setting hostName to 'unknown'", ex.toString());
544 map.putIfAbsent("hostName", "unknown");
545 }
546 map.putIfAbsent("contextName", contextName);
547 config.start();
548 this.configuration = config;
549 updateLoggers();
550 if (prev != null) {
551 prev.removeListener(this);
552 prev.stop();
553 }
554
555 firePropertyChangeEvent(new PropertyChangeEvent(this, PROPERTY_CONFIG, prev, config));
556
557 try {
558 Server.reregisterMBeansAfterReconfigure();
559 } catch (final LinkageError | Exception e) {
560
561 LOGGER.error("Could not reconfigure JMX", e);
562 }
563
564 Log4jLogEvent.setNanoClock(configuration.getNanoClock());
565
566 return prev;
567 } finally {
568 configLock.unlock();
569 }
570 }
571
572 private void firePropertyChangeEvent(final PropertyChangeEvent event) {
573 for (final PropertyChangeListener listener : propertyChangeListeners) {
574 listener.propertyChange(event);
575 }
576 }
577
578 public void addPropertyChangeListener(final PropertyChangeListener listener) {
579 propertyChangeListeners.add(Objects.requireNonNull(listener, "listener"));
580 }
581
582 public void removePropertyChangeListener(final PropertyChangeListener listener) {
583 propertyChangeListeners.remove(listener);
584 }
585
586
587
588
589
590
591
592
593
594 public URI getConfigLocation() {
595 return configLocation;
596 }
597
598
599
600
601
602
603 public void setConfigLocation(final URI configLocation) {
604 this.configLocation = configLocation;
605 reconfigure(configLocation);
606 }
607
608
609
610
611 private void reconfigure(final URI configURI) {
612 final ClassLoader cl = ClassLoader.class.isInstance(externalContext) ? (ClassLoader) externalContext : null;
613 LOGGER.debug("Reconfiguration started for context[name={}] at URI {} ({}) with optional ClassLoader: {}",
614 contextName, configURI, this, cl);
615 final Configuration instance = ConfigurationFactory.getInstance().getConfiguration(this, contextName, configURI, cl);
616 if (instance == null) {
617 LOGGER.error("Reconfiguration failed: No configuration found for '{}' at '{}' in '{}'", contextName, configURI, cl);
618 } else {
619 setConfiguration(instance);
620
621
622
623
624 final String location = configuration == null ? "?" : String.valueOf(configuration.getConfigurationSource());
625 LOGGER.debug("Reconfiguration complete for context[name={}] at URI {} ({}) with optional ClassLoader: {}",
626 contextName, location, this, cl);
627 }
628 }
629
630
631
632
633
634
635 public void reconfigure() {
636 reconfigure(configLocation);
637 }
638
639
640
641
642 public void updateLoggers() {
643 updateLoggers(this.configuration);
644 }
645
646
647
648
649
650
651 public void updateLoggers(final Configuration config) {
652 final Configuration old = this.configuration;
653 for (final Logger logger : loggerRegistry.getLoggers()) {
654 logger.updateConfiguration(config);
655 }
656 firePropertyChangeEvent(new PropertyChangeEvent(this, PROPERTY_CONFIG, old, config));
657 }
658
659
660
661
662
663
664 @Override
665 public synchronized void onChange(final Reconfigurable reconfigurable) {
666 LOGGER.debug("Reconfiguration started for context {} ({})", contextName, this);
667 initApiModule();
668 final Configuration newConfig = reconfigurable.reconfigure();
669 if (newConfig != null) {
670 setConfiguration(newConfig);
671 LOGGER.debug("Reconfiguration completed for {} ({})", contextName, this);
672 } else {
673 LOGGER.debug("Reconfiguration failed for {} ({})", contextName, this);
674 }
675 }
676
677 private void initApiModule() {
678 ThreadContextMapFactory.init();
679 }
680
681
682 protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) {
683 return new Logger(ctx, name, messageFactory);
684 }
685
686 }