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.lang.ref.Reference;
23 import java.lang.ref.SoftReference;
24 import java.net.URI;
25 import java.util.Collection;
26 import java.util.concurrent.ConcurrentHashMap;
27 import java.util.concurrent.ConcurrentMap;
28 import java.util.concurrent.CopyOnWriteArrayList;
29 import java.util.concurrent.locks.Lock;
30 import java.util.concurrent.locks.ReentrantLock;
31
32 import org.apache.logging.log4j.LogManager;
33 import org.apache.logging.log4j.Marker;
34 import org.apache.logging.log4j.MarkerManager;
35 import org.apache.logging.log4j.core.config.Configuration;
36 import org.apache.logging.log4j.core.config.ConfigurationFactory;
37 import org.apache.logging.log4j.core.config.ConfigurationListener;
38 import org.apache.logging.log4j.core.config.ConfigurationSource;
39 import org.apache.logging.log4j.core.config.DefaultConfiguration;
40 import org.apache.logging.log4j.core.config.NullConfiguration;
41 import org.apache.logging.log4j.core.config.Reconfigurable;
42 import org.apache.logging.log4j.core.jmx.Server;
43 import org.apache.logging.log4j.core.util.Assert;
44 import org.apache.logging.log4j.core.util.NetUtils;
45 import org.apache.logging.log4j.message.MessageFactory;
46 import org.apache.logging.log4j.spi.AbstractLogger;
47 import org.apache.logging.log4j.util.PropertiesUtil;
48
49
50
51
52
53
54
55
56 public class LoggerContext extends AbstractLifeCycle implements org.apache.logging.log4j.spi.LoggerContext, ConfigurationListener {
57
58 private static final boolean SHUTDOWN_HOOK_ENABLED =
59 PropertiesUtil.getProperties().getBooleanProperty("log4j.shutdownHookEnabled", true);
60
61 public static final String PROPERTY_CONFIG = "config";
62 private static final Marker SHUTDOWN_HOOK = MarkerManager.getMarker("SHUTDOWN HOOK");
63 private static final Configuration NULL_CONFIGURATION = new NullConfiguration();
64
65 private final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<String, Logger>();
66 private final CopyOnWriteArrayList<PropertyChangeListener> propertyChangeListeners = new CopyOnWriteArrayList<PropertyChangeListener>();
67
68
69
70
71
72 private volatile Configuration config = new DefaultConfiguration();
73 private Object externalContext;
74 private final String name;
75 private URI configLocation;
76
77
78
79
80
81
82 private Reference<Thread> shutdownThread;
83
84 private final Lock configLock = new ReentrantLock();
85
86
87
88
89
90 public LoggerContext(final String name) {
91 this(name, null, (URI) null);
92 }
93
94
95
96
97
98
99 public LoggerContext(final String name, final Object externalContext) {
100 this(name, externalContext, (URI) null);
101 }
102
103
104
105
106
107
108
109 public LoggerContext(final String name, final Object externalContext, final URI configLocn) {
110 this.name = 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.name = 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 @Override
140 public void start() {
141 LOGGER.debug("Starting LoggerContext[name={}, {}]...", getName(), this);
142 if (configLock.tryLock()) {
143 try {
144 if (this.isInitialized() || this.isStopped()) {
145 this.setStarting();
146 reconfigure();
147 setUpShutdownHook();
148 this.setStarted();
149 }
150 } finally {
151 configLock.unlock();
152 }
153 }
154 LOGGER.debug("LoggerContext[name={}, {}] started OK.", getName(), this);
155 }
156
157
158
159
160
161 public void start(final Configuration config) {
162 LOGGER.debug("Starting LoggerContext[name={}, {}] with configuration {}...", getName(), this, config);
163 if (configLock.tryLock()) {
164 try {
165 if (this.isInitialized() || this.isStopped()) {
166 setUpShutdownHook();
167 this.setStarted();
168 }
169 } finally {
170 configLock.unlock();
171 }
172 }
173 setConfiguration(config);
174 LOGGER.debug("LoggerContext[name={}, {}] started OK with configuration {}.", getName(), this, config);
175 }
176
177 private void setUpShutdownHook() {
178 if (config.isShutdownHookEnabled() && SHUTDOWN_HOOK_ENABLED) {
179 LOGGER.debug(SHUTDOWN_HOOK, "Shutdown hook enabled. Registering a new one.");
180 shutdownThread = new SoftReference<Thread>(
181 new Thread(new ShutdownThread(this), "log4j-shutdown")
182 );
183 addShutdownHook();
184 }
185 }
186
187 private void addShutdownHook() {
188 final Thread hook = getShutdownThread();
189 if (hook != null) {
190 try {
191 Runtime.getRuntime().addShutdownHook(hook);
192 } catch (final IllegalStateException ise) {
193 LOGGER.warn(SHUTDOWN_HOOK, "Unable to register shutdown hook due to JVM state");
194 } catch (final SecurityException se) {
195 LOGGER.warn(SHUTDOWN_HOOK, "Unable to register shutdown hook due to security restrictions");
196 }
197 }
198 }
199
200 private Thread getShutdownThread() {
201 return shutdownThread == null ? null : shutdownThread.get();
202 }
203
204 @Override
205 public void stop() {
206 LOGGER.debug("Stopping LoggerContext[name={}, {}]...", getName(), this);
207 configLock.lock();
208 try {
209 if (this.isStopped()) {
210 return;
211 }
212 this.setStopping();
213 tearDownShutdownHook();
214 final Configuration prev = config;
215 config = NULL_CONFIGURATION;
216 updateLoggers();
217 prev.stop();
218 externalContext = null;
219 LogManager.getFactory().removeContext(this);
220 this.setStopped();
221 } finally {
222 configLock.unlock();
223
224
225 Server.unregisterLoggerContext(getName());
226 }
227 LOGGER.debug("Stopped LoggerContext[name={}, {}]...", getName(), this);
228 }
229
230 private void tearDownShutdownHook() {
231 if (shutdownThread != null) {
232 LOGGER.debug(SHUTDOWN_HOOK, "Enqueue shutdown hook for garbage collection.");
233 shutdownThread.enqueue();
234 }
235 }
236
237
238
239
240
241
242 public String getName() {
243 return name;
244 }
245
246
247
248
249
250 public void setExternalContext(final Object context) {
251 this.externalContext = context;
252 }
253
254
255
256
257
258 @Override
259 public Object getExternalContext() {
260 return this.externalContext;
261 }
262
263
264
265
266
267
268 @Override
269 public Logger getLogger(final String name) {
270 return getLogger(name, null);
271 }
272
273
274
275
276
277
278
279
280
281
282 public Collection<Logger> getLoggers() {
283 return loggers.values();
284 }
285
286
287
288
289
290
291
292
293
294 @Override
295 public Logger getLogger(final String name, final MessageFactory messageFactory) {
296 Logger logger = loggers.get(name);
297 if (logger != null) {
298 AbstractLogger.checkMessageFactory(logger, messageFactory);
299 return logger;
300 }
301
302 logger = newInstance(this, name, messageFactory);
303 final Logger prev = loggers.putIfAbsent(name, logger);
304 return prev == null ? logger : prev;
305 }
306
307
308
309
310
311
312 @Override
313 public boolean hasLogger(final String name) {
314 return loggers.containsKey(name);
315 }
316
317
318
319
320
321
322
323 public Configuration getConfiguration() {
324 return config;
325 }
326
327
328
329
330
331
332 public void addFilter(final Filter filter) {
333 config.addFilter(filter);
334 }
335
336
337
338
339
340 public void removeFilter(final Filter filter) {
341 config.removeFilter(filter);
342 }
343
344
345
346
347
348
349 private synchronized Configuration setConfiguration(final Configuration config) {
350 if (config == null) {
351 throw new NullPointerException("No Configuration was provided");
352 }
353 final Configuration prev = this.config;
354 config.addListener(this);
355 final ConcurrentMap<String, String> map = config.getComponent(Configuration.CONTEXT_PROPERTIES);
356
357 try {
358 map.putIfAbsent("hostName", NetUtils.getLocalHostname());
359 } catch (final Exception ex) {
360 LOGGER.debug("Ignoring {}, setting hostName to 'unknown'", ex.toString());
361 map.putIfAbsent("hostName", "unknown");
362 }
363 map.putIfAbsent("contextName", name);
364 config.start();
365 this.config = config;
366 updateLoggers();
367 if (prev != null) {
368 prev.removeListener(this);
369 prev.stop();
370 }
371
372 firePropertyChangeEvent(new PropertyChangeEvent(this, PROPERTY_CONFIG, prev, config));
373
374 try {
375 Server.reregisterMBeansAfterReconfigure();
376 } catch (final Throwable t) {
377 LOGGER.error("Could not reconfigure JMX", t);
378 }
379 return prev;
380 }
381
382 private void firePropertyChangeEvent(final PropertyChangeEvent event) {
383 for (final PropertyChangeListener listener : propertyChangeListeners) {
384 listener.propertyChange(event);
385 }
386 }
387
388 public void addPropertyChangeListener(final PropertyChangeListener listener) {
389 propertyChangeListeners.add(Assert.requireNonNull(listener, "listener"));
390 }
391
392 public void removePropertyChangeListener(final PropertyChangeListener listener) {
393 propertyChangeListeners.remove(listener);
394 }
395
396
397
398
399
400
401
402
403 public synchronized URI getConfigLocation() {
404 return configLocation;
405 }
406
407
408
409
410
411 public synchronized void setConfigLocation(final URI configLocation) {
412 this.configLocation = configLocation;
413 reconfigure();
414 }
415
416
417
418
419 public synchronized void reconfigure() {
420 LOGGER.debug("Reconfiguration started for context[name={}] at {} ({})", name, configLocation, this);
421 final Configuration instance = ConfigurationFactory.getInstance().getConfiguration(name, configLocation);
422 setConfiguration(instance);
423
424
425
426
427
428 LOGGER.debug("Reconfiguration complete for context[name={}] at {} ({})", name, configLocation, this);
429 }
430
431
432
433
434 public void updateLoggers() {
435 updateLoggers(this.config);
436 }
437
438
439
440
441
442 public void updateLoggers(final Configuration config) {
443 for (final Logger logger : loggers.values()) {
444 logger.updateConfiguration(config);
445 }
446 }
447
448
449
450
451
452
453
454 @Override
455 public synchronized void onChange(final Reconfigurable reconfigurable) {
456 LOGGER.debug("Reconfiguration started for context {} ({})", name, this);
457 final Configuration config = reconfigurable.reconfigure();
458 if (config != null) {
459 setConfiguration(config);
460 LOGGER.debug("Reconfiguration completed for {} ({})", name, this);
461 } else {
462 LOGGER.debug("Reconfiguration failed for {} ({})", name, this);
463 }
464 }
465
466
467 protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) {
468 return new Logger(ctx, name, messageFactory);
469 }
470
471 private static class ShutdownThread implements Runnable {
472
473 private final LoggerContext context;
474
475 public ShutdownThread(final LoggerContext context) {
476 this.context = context;
477 }
478
479 @Override
480 public void run() {
481 LOGGER.debug("ShutdownThread stopping LoggerContext[name={}, {}]...", context.getName(), context);
482 context.stop();
483 LOGGER.debug("ShutdownThread stopped LoggerContext[name={}, {}].", context.getName(), context);
484 }
485 }
486
487 }