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