1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.impl;
18
19 import java.net.URI;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.Objects;
23
24 import org.apache.logging.log4j.core.LifeCycle;
25 import org.apache.logging.log4j.core.LoggerContext;
26 import org.apache.logging.log4j.core.config.AbstractConfiguration;
27 import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
28 import org.apache.logging.log4j.core.config.Configuration;
29 import org.apache.logging.log4j.core.config.ConfigurationFactory;
30 import org.apache.logging.log4j.core.config.ConfigurationSource;
31 import org.apache.logging.log4j.core.selector.ClassLoaderContextSelector;
32 import org.apache.logging.log4j.core.selector.ContextSelector;
33 import org.apache.logging.log4j.core.util.Cancellable;
34 import org.apache.logging.log4j.core.util.Constants;
35 import org.apache.logging.log4j.core.util.DefaultShutdownCallbackRegistry;
36 import org.apache.logging.log4j.core.util.Loader;
37 import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
38 import org.apache.logging.log4j.spi.LoggerContextFactory;
39 import org.apache.logging.log4j.status.StatusLogger;
40 import org.apache.logging.log4j.util.PropertiesUtil;
41
42
43
44
45 public class Log4jContextFactory implements LoggerContextFactory, ShutdownCallbackRegistry {
46
47 private static final StatusLogger LOGGER = StatusLogger.getLogger();
48 private static final boolean SHUTDOWN_HOOK_ENABLED =
49 PropertiesUtil.getProperties().getBooleanProperty(ShutdownCallbackRegistry.SHUTDOWN_HOOK_ENABLED, true) &&
50 !Constants.IS_WEB_APP;
51
52 private final ContextSelector selector;
53 private final ShutdownCallbackRegistry shutdownCallbackRegistry;
54
55
56
57
58 public Log4jContextFactory() {
59 this(createContextSelector(), createShutdownCallbackRegistry());
60 }
61
62
63
64
65
66 public Log4jContextFactory(final ContextSelector selector) {
67 this(selector, createShutdownCallbackRegistry());
68 }
69
70
71
72
73
74
75
76
77 public Log4jContextFactory(final ShutdownCallbackRegistry shutdownCallbackRegistry) {
78 this(createContextSelector(), shutdownCallbackRegistry);
79 }
80
81
82
83
84
85
86
87
88 public Log4jContextFactory(final ContextSelector selector,
89 final ShutdownCallbackRegistry shutdownCallbackRegistry) {
90 this.selector = Objects.requireNonNull(selector, "No ContextSelector provided");
91 this.shutdownCallbackRegistry = Objects.requireNonNull(shutdownCallbackRegistry, "No ShutdownCallbackRegistry provided");
92 LOGGER.debug("Using ShutdownCallbackRegistry {}", shutdownCallbackRegistry.getClass());
93 initializeShutdownCallbackRegistry();
94 }
95
96 private static ContextSelector createContextSelector() {
97 try {
98 final ContextSelector selector = Loader.newCheckedInstanceOfProperty(Constants.LOG4J_CONTEXT_SELECTOR,
99 ContextSelector.class);
100 if (selector != null) {
101 return selector;
102 }
103 } catch (final Exception e) {
104 LOGGER.error("Unable to create custom ContextSelector. Falling back to default.", e);
105 }
106 return new ClassLoaderContextSelector();
107 }
108
109 private static ShutdownCallbackRegistry createShutdownCallbackRegistry() {
110 try {
111 final ShutdownCallbackRegistry registry = Loader.newCheckedInstanceOfProperty(
112 ShutdownCallbackRegistry.SHUTDOWN_CALLBACK_REGISTRY, ShutdownCallbackRegistry.class
113 );
114 if (registry != null) {
115 return registry;
116 }
117 } catch (final Exception e) {
118 LOGGER.error("Unable to create custom ShutdownCallbackRegistry. Falling back to default.", e);
119 }
120 return new DefaultShutdownCallbackRegistry();
121 }
122
123 private void initializeShutdownCallbackRegistry() {
124 if (isShutdownHookEnabled() && this.shutdownCallbackRegistry instanceof LifeCycle) {
125 try {
126 ((LifeCycle) this.shutdownCallbackRegistry).start();
127 } catch (final IllegalStateException e) {
128 LOGGER.error("Cannot start ShutdownCallbackRegistry, already shutting down.");
129 throw e;
130 } catch (final RuntimeException e) {
131 LOGGER.error("There was an error starting the ShutdownCallbackRegistry.", e);
132 }
133 }
134 }
135
136
137
138
139
140
141
142
143
144
145 @Override
146 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
147 final boolean currentContext) {
148 final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext);
149 if (externalContext != null && ctx.getExternalContext() == null) {
150 ctx.setExternalContext(externalContext);
151 }
152 if (ctx.getState() == LifeCycle.State.INITIALIZED) {
153 ctx.start();
154 }
155 return ctx;
156 }
157
158
159
160
161
162
163
164
165
166
167
168 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
169 final boolean currentContext, final ConfigurationSource source) {
170 final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext, null);
171 if (externalContext != null && ctx.getExternalContext() == null) {
172 ctx.setExternalContext(externalContext);
173 }
174 if (ctx.getState() == LifeCycle.State.INITIALIZED) {
175 if (source != null) {
176 ContextAnchor.THREAD_CONTEXT.set(ctx);
177 final Configuration config = ConfigurationFactory.getInstance().getConfiguration(ctx, source);
178 LOGGER.debug("Starting LoggerContext[name={}] from configuration {}", ctx.getName(), source);
179 ctx.start(config);
180 ContextAnchor.THREAD_CONTEXT.remove();
181 } else {
182 ctx.start();
183 }
184 }
185 return ctx;
186 }
187
188
189
190
191
192
193
194
195
196
197
198 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
199 final boolean currentContext, final Configuration configuration) {
200 final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext, null);
201 if (externalContext != null && ctx.getExternalContext() == null) {
202 ctx.setExternalContext(externalContext);
203 }
204 if (ctx.getState() == LifeCycle.State.INITIALIZED) {
205 ContextAnchor.THREAD_CONTEXT.set(ctx);
206 try {
207 ctx.start(configuration);
208 } finally {
209 ContextAnchor.THREAD_CONTEXT.remove();
210 }
211 }
212 return ctx;
213 }
214
215
216
217
218
219
220
221
222
223
224
225 @Override
226 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
227 final boolean currentContext, final URI configLocation, final String name) {
228 final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext, configLocation);
229 if (externalContext != null && ctx.getExternalContext() == null) {
230 ctx.setExternalContext(externalContext);
231 }
232 if (name != null) {
233 ctx.setName(name);
234 }
235 if (ctx.getState() == LifeCycle.State.INITIALIZED) {
236 if (configLocation != null || name != null) {
237 ContextAnchor.THREAD_CONTEXT.set(ctx);
238 final Configuration config = ConfigurationFactory.getInstance().getConfiguration(ctx, name, configLocation);
239 LOGGER.debug("Starting LoggerContext[name={}] from configuration at {}", ctx.getName(), configLocation);
240 ctx.start(config);
241 ContextAnchor.THREAD_CONTEXT.remove();
242 } else {
243 ctx.start();
244 }
245 }
246 return ctx;
247 }
248
249 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
250 final boolean currentContext, final List<URI> configLocations, final String name) {
251 final LoggerContext ctx = selector
252 .getContext(fqcn, loader, currentContext, null);
253 if (externalContext != null && ctx.getExternalContext() == null) {
254 ctx.setExternalContext(externalContext);
255 }
256 if (name != null) {
257 ctx.setName(name);
258 }
259 if (ctx.getState() == LifeCycle.State.INITIALIZED) {
260 if ((configLocations != null && !configLocations.isEmpty())) {
261 ContextAnchor.THREAD_CONTEXT.set(ctx);
262 final List<AbstractConfiguration> configurations = new ArrayList<>(configLocations.size());
263 for (final URI configLocation : configLocations) {
264 final Configuration currentReadConfiguration = ConfigurationFactory.getInstance()
265 .getConfiguration(ctx, name, configLocation);
266 if (currentReadConfiguration instanceof AbstractConfiguration) {
267 configurations.add((AbstractConfiguration) currentReadConfiguration);
268 } else {
269 LOGGER.error(
270 "Found configuration {}, which is not an AbstractConfiguration and can't be handled by CompositeConfiguration",
271 configLocation);
272 }
273 }
274 final CompositeConfiguration compositeConfiguration = new CompositeConfiguration(configurations);
275 LOGGER.debug("Starting LoggerContext[name={}] from configurations at {}", ctx.getName(),
276 configLocations);
277 ctx.start(compositeConfiguration);
278 ContextAnchor.THREAD_CONTEXT.remove();
279 } else {
280 ctx.start();
281 }
282 }
283 return ctx;
284 }
285
286
287
288
289
290 public ContextSelector getSelector() {
291 return selector;
292 }
293
294
295
296
297
298
299
300 public ShutdownCallbackRegistry getShutdownCallbackRegistry() {
301 return shutdownCallbackRegistry;
302 }
303
304
305
306
307
308
309 @Override
310 public void removeContext(final org.apache.logging.log4j.spi.LoggerContext context) {
311 if (context instanceof LoggerContext) {
312 selector.removeContext((LoggerContext) context);
313 }
314 }
315
316 @Override
317 public Cancellable addShutdownCallback(final Runnable callback) {
318 return isShutdownHookEnabled() ? shutdownCallbackRegistry.addShutdownCallback(callback) : null;
319 }
320
321 public boolean isShutdownHookEnabled() {
322 return SHUTDOWN_HOOK_ENABLED;
323 }
324 }