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