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.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.CopyOnWriteArrayList;
26 import java.util.concurrent.locks.Lock;
27 import java.util.concurrent.locks.ReentrantLock;
28
29 import org.apache.logging.log4j.LogManager;
30 import org.apache.logging.log4j.core.config.Configuration;
31 import org.apache.logging.log4j.core.config.ConfigurationFactory;
32 import org.apache.logging.log4j.core.config.ConfigurationListener;
33 import org.apache.logging.log4j.core.config.DefaultConfiguration;
34 import org.apache.logging.log4j.core.config.NullConfiguration;
35 import org.apache.logging.log4j.core.config.Reconfigurable;
36 import org.apache.logging.log4j.core.helpers.Assert;
37 import org.apache.logging.log4j.core.helpers.NetUtils;
38 import org.apache.logging.log4j.core.jmx.Server;
39 import org.apache.logging.log4j.message.MessageFactory;
40 import org.apache.logging.log4j.spi.AbstractLogger;
41 import org.apache.logging.log4j.status.StatusLogger;
42
43
44
45
46
47
48
49
50 public class LoggerContext implements org.apache.logging.log4j.spi.LoggerContext, ConfigurationListener, LifeCycle {
51
52 public static final String PROPERTY_CONFIG = "config";
53 private static final StatusLogger LOGGER = StatusLogger.getLogger();
54 private static final Configuration NULL_CONFIGURATION = new NullConfiguration();
55
56 private final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<String, Logger>();
57 private final CopyOnWriteArrayList<PropertyChangeListener> propertyChangeListeners = new CopyOnWriteArrayList<PropertyChangeListener>();
58
59
60
61
62
63 private volatile Configuration config = new DefaultConfiguration();
64 private Object externalContext;
65 private final String name;
66 private URI configLocation;
67
68 private ShutdownThread shutdownThread = null;
69
70
71
72
73 public enum Status {
74
75 INITIALIZED,
76
77 STARTING,
78
79 STARTED,
80
81 STOPPING,
82
83 STOPPED
84 }
85
86 private volatile Status status = Status.INITIALIZED;
87
88 private final Lock configLock = new ReentrantLock();
89
90
91
92
93
94 public LoggerContext(final String name) {
95 this(name, null, (URI) null);
96 }
97
98
99
100
101
102
103 public LoggerContext(final String name, final Object externalContext) {
104 this(name, externalContext, (URI) null);
105 }
106
107
108
109
110
111
112
113 public LoggerContext(final String name, final Object externalContext, final URI configLocn) {
114 this.name = name;
115 this.externalContext = externalContext;
116 this.configLocation = configLocn;
117 }
118
119
120
121
122
123
124
125
126
127 public LoggerContext(final String name, final Object externalContext, final String configLocn) {
128 this.name = name;
129 this.externalContext = externalContext;
130 if (configLocn != null) {
131 URI uri;
132 try {
133 uri = new File(configLocn).toURI();
134 } catch (final Exception ex) {
135 uri = null;
136 }
137 configLocation = uri;
138 } else {
139 configLocation = null;
140 }
141 }
142
143 @Override
144 public void start() {
145 if (configLock.tryLock()) {
146 try {
147 if (status == Status.INITIALIZED || status == Status.STOPPED) {
148 status = Status.STARTING;
149 reconfigure();
150 if (config.isShutdownHookEnabled()) {
151 shutdownThread = new ShutdownThread(this);
152 try {
153 Runtime.getRuntime().addShutdownHook(shutdownThread);
154 } catch (final IllegalStateException ise) {
155 LOGGER.warn("Unable to register shutdown hook due to JVM state");
156 shutdownThread = null;
157 } catch (final SecurityException se) {
158 LOGGER.warn("Unable to register shutdown hook due to security restrictions");
159 shutdownThread = null;
160 }
161 }
162 status = Status.STARTED;
163 }
164 } finally {
165 configLock.unlock();
166 }
167 }
168 }
169
170
171
172
173
174 public void start(final Configuration config) {
175 if (configLock.tryLock()) {
176 try {
177 if ((status == Status.INITIALIZED || status == Status.STOPPED) && config.isShutdownHookEnabled() ) {
178 shutdownThread = new ShutdownThread(this);
179 try {
180 Runtime.getRuntime().addShutdownHook(shutdownThread);
181 } catch (final IllegalStateException ise) {
182 LOGGER.warn("Unable to register shutdown hook due to JVM state");
183 shutdownThread = null;
184 } catch (final SecurityException se) {
185 LOGGER.warn("Unable to register shutdown hook due to security restrictions");
186 shutdownThread = null;
187 }
188 status = Status.STARTED;
189 }
190 } finally {
191 configLock.unlock();
192 }
193 }
194 setConfiguration(config);
195 }
196
197 @Override
198 public void stop() {
199 configLock.lock();
200 try {
201 if (status == Status.STOPPED) {
202 return;
203 }
204 status = Status.STOPPING;
205 if (shutdownThread != null) {
206 Runtime.getRuntime().removeShutdownHook(shutdownThread);
207 shutdownThread = null;
208 }
209 final Configuration prev = config;
210 config = NULL_CONFIGURATION;
211 updateLoggers();
212 prev.stop();
213 externalContext = null;
214 LogManager.getFactory().removeContext(this);
215 status = Status.STOPPED;
216 } finally {
217 configLock.unlock();
218
219
220 Server.unregisterLoggerContext(getName());
221 }
222 }
223
224
225
226
227
228
229 public String getName() {
230 return name;
231 }
232
233 public Status getStatus() {
234 return status;
235 }
236
237 @Override
238 public boolean isStarted() {
239 return status == Status.STARTED;
240 }
241
242
243
244
245
246 public void setExternalContext(final Object context) {
247 this.externalContext = context;
248 }
249
250
251
252
253
254 @Override
255 public Object getExternalContext() {
256 return this.externalContext;
257 }
258
259
260
261
262
263
264 @Override
265 public Logger getLogger(final String name) {
266 return getLogger(name, null);
267 }
268
269
270
271
272
273
274
275
276
277 @Override
278 public Logger getLogger(final String name, final MessageFactory messageFactory) {
279 Logger logger = loggers.get(name);
280 if (logger != null) {
281 AbstractLogger.checkMessageFactory(logger, messageFactory);
282 return logger;
283 }
284
285 logger = newInstance(this, name, messageFactory);
286 final Logger prev = loggers.putIfAbsent(name, logger);
287 return prev == null ? logger : prev;
288 }
289
290
291
292
293
294
295 @Override
296 public boolean hasLogger(final String name) {
297 return loggers.containsKey(name);
298 }
299
300
301
302
303
304
305
306 public Configuration getConfiguration() {
307 return config;
308 }
309
310
311
312
313
314
315 public void addFilter(final Filter filter) {
316 config.addFilter(filter);
317 }
318
319
320
321
322
323 public void removeFilter(final Filter filter) {
324 config.removeFilter(filter);
325 }
326
327
328
329
330
331
332 private synchronized Configuration setConfiguration(final Configuration config) {
333 if (config == null) {
334 throw new NullPointerException("No Configuration was provided");
335 }
336 final Configuration prev = this.config;
337 config.addListener(this);
338 final ConcurrentMap<String, String> map = config.getComponent(Configuration.CONTEXT_PROPERTIES);
339 map.putIfAbsent("hostName", NetUtils.getLocalHostname());
340 map.putIfAbsent("contextName", name);
341 config.start();
342 this.config = config;
343 updateLoggers();
344 if (prev != null) {
345 prev.removeListener(this);
346 prev.stop();
347 }
348
349
350 final PropertyChangeEvent evt = new PropertyChangeEvent(this, PROPERTY_CONFIG, prev, config);
351 for (final PropertyChangeListener listener : propertyChangeListeners) {
352 listener.propertyChange(evt);
353 }
354
355 try {
356 Server.reregisterMBeansAfterReconfigure();
357 } catch (final Exception ex) {
358 LOGGER.error("Could not reconfigure JMX", ex);
359 }
360 return prev;
361 }
362
363 public void addPropertyChangeListener(final PropertyChangeListener listener) {
364 propertyChangeListeners.add(Assert.isNotNull(listener, "listener"));
365 }
366
367 public void removePropertyChangeListener(final PropertyChangeListener listener) {
368 propertyChangeListeners.remove(listener);
369 }
370
371 public synchronized URI getConfigLocation() {
372 return configLocation;
373 }
374
375 public synchronized void setConfigLocation(final URI configLocation) {
376 this.configLocation = configLocation;
377 reconfigure();
378 }
379
380
381
382
383 public synchronized void reconfigure() {
384 LOGGER.debug("Reconfiguration started for context " + name);
385 final Configuration instance = ConfigurationFactory.getInstance().getConfiguration(name, configLocation);
386 setConfiguration(instance);
387
388
389
390
391
392 LOGGER.debug("Reconfiguration completed");
393 }
394
395
396
397
398 public void updateLoggers() {
399 updateLoggers(this.config);
400 }
401
402
403
404
405
406 public void updateLoggers(final Configuration config) {
407 for (final Logger logger : loggers.values()) {
408 logger.updateConfiguration(config);
409 }
410 }
411
412
413
414
415
416
417
418 @Override
419 public synchronized void onChange(final Reconfigurable reconfigurable) {
420 LOGGER.debug("Reconfiguration started for context " + name);
421 final Configuration config = reconfigurable.reconfigure();
422 if (config != null) {
423 setConfiguration(config);
424 LOGGER.debug("Reconfiguration completed");
425 } else {
426 LOGGER.debug("Reconfiguration failed");
427 }
428 }
429
430
431 protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) {
432 return new Logger(ctx, name, messageFactory);
433 }
434
435 private class ShutdownThread extends Thread {
436
437 private final LoggerContext context;
438
439 public ShutdownThread(final LoggerContext context) {
440 this.context = context;
441 }
442
443 @Override
444 public void run() {
445 context.shutdownThread = null;
446 context.stop();
447 }
448 }
449
450 }