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