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.async.AsyncLogger;
33 import org.apache.logging.log4j.core.config.Configuration;
34 import org.apache.logging.log4j.core.config.ConfigurationFactory;
35 import org.apache.logging.log4j.core.config.ConfigurationListener;
36 import org.apache.logging.log4j.core.config.ConfigurationSource;
37 import org.apache.logging.log4j.core.config.DefaultConfiguration;
38 import org.apache.logging.log4j.core.config.NullConfiguration;
39 import org.apache.logging.log4j.core.config.Reconfigurable;
40 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
41 import org.apache.logging.log4j.core.jmx.Server;
42 import org.apache.logging.log4j.core.util.Cancellable;
43 import org.apache.logging.log4j.core.util.NanoClockFactory;
44 import org.apache.logging.log4j.core.util.NetUtils;
45 import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
46 import org.apache.logging.log4j.message.MessageFactory;
47 import org.apache.logging.log4j.spi.AbstractLogger;
48 import org.apache.logging.log4j.spi.LoggerContextFactory;
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 final 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.fatal(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 void setExternalContext(final Object context) {
328 this.externalContext = context;
329 }
330
331
332
333
334
335
336 @Override
337 public Object getExternalContext() {
338 return this.externalContext;
339 }
340
341
342
343
344
345
346
347 @Override
348 public Logger getLogger(final String name) {
349 return getLogger(name, null);
350 }
351
352
353
354
355
356
357
358
359
360
361 public Collection<Logger> getLoggers() {
362 return loggers.values();
363 }
364
365
366
367
368
369
370
371
372
373 @Override
374 public Logger getLogger(final String name, final MessageFactory messageFactory) {
375 Logger logger = loggers.get(name);
376 if (logger != null) {
377 AbstractLogger.checkMessageFactory(logger, messageFactory);
378 return logger;
379 }
380
381 logger = newInstance(this, name, messageFactory);
382 final Logger prev = loggers.putIfAbsent(name, logger);
383 return prev == null ? logger : prev;
384 }
385
386
387
388
389
390
391
392 @Override
393 public boolean hasLogger(final String name) {
394 return loggers.containsKey(name);
395 }
396
397
398
399
400
401
402 public Configuration getConfiguration() {
403 return configuration;
404 }
405
406
407
408
409
410
411
412 public void addFilter(final Filter filter) {
413 configuration.addFilter(filter);
414 }
415
416
417
418
419
420
421 public void removeFilter(final Filter filter) {
422 configuration.removeFilter(filter);
423 }
424
425
426
427
428
429
430
431 private Configuration setConfiguration(final Configuration config) {
432 Objects.requireNonNull(config, "No Configuration was provided");
433 configLock.lock();
434 try {
435 final Configuration prev = this.configuration;
436 config.addListener(this);
437 final ConcurrentMap<String, String> map = config.getComponent(Configuration.CONTEXT_PROPERTIES);
438
439 try {
440 map.putIfAbsent("hostName", NetUtils.getLocalHostname());
441 } catch (final Exception ex) {
442 LOGGER.debug("Ignoring {}, setting hostName to 'unknown'", ex.toString());
443 map.putIfAbsent("hostName", "unknown");
444 }
445 map.putIfAbsent("contextName", contextName);
446 config.start();
447 this.configuration = config;
448 updateLoggers();
449 if (prev != null) {
450 prev.removeListener(this);
451 prev.stop();
452 }
453
454 firePropertyChangeEvent(new PropertyChangeEvent(this, PROPERTY_CONFIG, prev, config));
455
456 try {
457 Server.reregisterMBeansAfterReconfigure();
458 } catch (final Throwable t) {
459
460 LOGGER.error("Could not reconfigure JMX", t);
461 }
462 Log4jLogEvent.setNanoClock(NanoClockFactory.createNanoClock());
463 try {
464 AsyncLogger.setNanoClock(NanoClockFactory.createNanoClock());
465 } catch (Throwable ignored) {
466
467 LOGGER.debug("Could not set AsyncLogger NanoClock. Ignoring: " + ignored.toString());
468 }
469 return prev;
470 } finally {
471 configLock.unlock();
472 }
473 }
474
475 private void firePropertyChangeEvent(final PropertyChangeEvent event) {
476 for (final PropertyChangeListener listener : propertyChangeListeners) {
477 listener.propertyChange(event);
478 }
479 }
480
481 public void addPropertyChangeListener(final PropertyChangeListener listener) {
482 propertyChangeListeners.add(Objects.requireNonNull(listener, "listener"));
483 }
484
485 public void removePropertyChangeListener(final PropertyChangeListener listener) {
486 propertyChangeListeners.remove(listener);
487 }
488
489
490
491
492
493
494
495
496
497 public URI getConfigLocation() {
498 return configLocation;
499 }
500
501
502
503
504
505
506 public void setConfigLocation(final URI configLocation) {
507 this.configLocation = configLocation;
508
509 reconfigure(configLocation);
510 }
511
512
513
514
515 private void reconfigure(final URI configURI) {
516 final ClassLoader cl = ClassLoader.class.isInstance(externalContext) ? (ClassLoader) externalContext : null;
517 LOGGER.debug("Reconfiguration started for context[name={}] at URI {} ({}) with optional ClassLoader: {}",
518 contextName, configURI, this, cl);
519 final Configuration instance = ConfigurationFactory.getInstance().getConfiguration(contextName, configURI, cl);
520 setConfiguration(instance);
521
522
523
524
525
526 LOGGER.debug("Reconfiguration complete for context[name={}] at URI {} ({}) with optional ClassLoader: {}",
527 contextName, configURI, this, cl);
528 }
529
530
531
532
533
534
535 public void reconfigure() {
536 reconfigure(configLocation);
537 }
538
539
540
541
542 public void updateLoggers() {
543 updateLoggers(this.configuration);
544 }
545
546
547
548
549
550
551 public void updateLoggers(final Configuration config) {
552 for (final Logger logger : loggers.values()) {
553 logger.updateConfiguration(config);
554 }
555 }
556
557
558
559
560
561
562 @Override
563 public synchronized void onChange(final Reconfigurable reconfigurable) {
564 LOGGER.debug("Reconfiguration started for context {} ({})", contextName, this);
565 final Configuration newConfig = reconfigurable.reconfigure();
566 if (newConfig != null) {
567 setConfiguration(newConfig);
568 LOGGER.debug("Reconfiguration completed for {} ({})", contextName, this);
569 } else {
570 LOGGER.debug("Reconfiguration failed for {} ({})", contextName, this);
571 }
572 }
573
574
575 protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) {
576 return new Logger(ctx, name, messageFactory);
577 }
578
579 }