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