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