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