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 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 synchronized Configuration setConfiguration(final Configuration config) {
347 Assert.requireNonNull(config, "No Configuration was provided");
348 final Configuration prev = this.config;
349 config.addListener(this);
350 final ConcurrentMap<String, String> map = config.getComponent(Configuration.CONTEXT_PROPERTIES);
351
352 try {
353 map.putIfAbsent("hostName", NetUtils.getLocalHostname());
354 } catch (final Exception ex) {
355 LOGGER.debug("Ignoring {}, setting hostName to 'unknown'", ex.toString());
356 map.putIfAbsent("hostName", "unknown");
357 }
358 map.putIfAbsent("contextName", name);
359 config.start();
360 this.config = config;
361 updateLoggers();
362 if (prev != null) {
363 prev.removeListener(this);
364 prev.stop();
365 }
366
367 firePropertyChangeEvent(new PropertyChangeEvent(this, PROPERTY_CONFIG, prev, config));
368
369 try {
370 Server.reregisterMBeansAfterReconfigure();
371 } catch (final Throwable t) {
372
373 LOGGER.error("Could not reconfigure JMX", t);
374 }
375 return prev;
376 }
377
378 private void firePropertyChangeEvent(final PropertyChangeEvent event) {
379 for (final PropertyChangeListener listener : propertyChangeListeners) {
380 listener.propertyChange(event);
381 }
382 }
383
384 public void addPropertyChangeListener(final PropertyChangeListener listener) {
385 propertyChangeListeners.add(Assert.requireNonNull(listener, "listener"));
386 }
387
388 public void removePropertyChangeListener(final PropertyChangeListener listener) {
389 propertyChangeListeners.remove(listener);
390 }
391
392
393
394
395
396
397
398
399 public synchronized URI getConfigLocation() {
400 return configLocation;
401 }
402
403
404
405
406
407 public synchronized void setConfigLocation(final URI configLocation) {
408 this.configLocation = configLocation;
409 reconfigure();
410 }
411
412
413
414
415 public synchronized void reconfigure() {
416 final ClassLoader cl = ClassLoader.class.isInstance(externalContext) ? (ClassLoader) externalContext : null;
417 LOGGER.debug("Reconfiguration started for context[name={}] at {} ({}) with optional ClassLoader: {}", name,
418 configLocation, this, cl);
419 final Configuration instance = ConfigurationFactory.getInstance().getConfiguration(name, configLocation, cl);
420 setConfiguration(instance);
421
422
423
424
425
426 LOGGER.debug("Reconfiguration complete for context[name={}] at {} ({}) with optional ClassLoader: {}", name,
427 configLocation, this, cl);
428 }
429
430
431
432
433 public void updateLoggers() {
434 updateLoggers(this.config);
435 }
436
437
438
439
440
441 public void updateLoggers(final Configuration config) {
442 for (final Logger logger : loggers.values()) {
443 logger.updateConfiguration(config);
444 }
445 }
446
447
448
449
450
451
452
453 @Override
454 public synchronized void onChange(final Reconfigurable reconfigurable) {
455 LOGGER.debug("Reconfiguration started for context {} ({})", name, this);
456 final Configuration newConfig = reconfigurable.reconfigure();
457 if (newConfig != null) {
458 setConfiguration(newConfig);
459 LOGGER.debug("Reconfiguration completed for {} ({})", name, this);
460 } else {
461 LOGGER.debug("Reconfiguration failed for {} ({})", name, this);
462 }
463 }
464
465
466 protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) {
467 return new Logger(ctx, name, messageFactory);
468 }
469
470 }