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