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