1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.config;
18
19 import org.apache.logging.log4j.Level;
20 import org.apache.logging.log4j.Logger;
21 import org.apache.logging.log4j.core.LoggerContext;
22 import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
23 import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
24 import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
25 import org.apache.logging.log4j.core.config.plugins.util.PluginType;
26 import org.apache.logging.log4j.core.lookup.Interpolator;
27 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
28 import org.apache.logging.log4j.core.net.UrlConnectionFactory;
29 import org.apache.logging.log4j.core.util.AuthorizationProvider;
30 import org.apache.logging.log4j.core.util.BasicAuthorizationProvider;
31 import org.apache.logging.log4j.core.util.FileUtils;
32 import org.apache.logging.log4j.core.util.Loader;
33 import org.apache.logging.log4j.core.util.NetUtils;
34 import org.apache.logging.log4j.core.util.ReflectionUtil;
35 import org.apache.logging.log4j.status.StatusLogger;
36 import org.apache.logging.log4j.util.LoaderUtil;
37 import org.apache.logging.log4j.util.PropertiesUtil;
38 import org.apache.logging.log4j.util.Strings;
39
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.io.FileNotFoundException;
43 import java.io.UnsupportedEncodingException;
44 import java.net.URI;
45 import java.net.URISyntaxException;
46 import java.net.URL;
47 import java.net.URLConnection;
48 import java.net.URLDecoder;
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.Collections;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.concurrent.locks.Lock;
55 import java.util.concurrent.locks.ReentrantLock;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 public abstract class ConfigurationFactory extends ConfigurationBuilderFactory {
81
82 public ConfigurationFactory() {
83 super();
84
85 }
86
87
88
89
90 public static final String CONFIGURATION_FACTORY_PROPERTY = "log4j.configurationFactory";
91
92
93
94
95 public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile";
96
97 public static final String LOG4J1_CONFIGURATION_FILE_PROPERTY = "log4j.configuration";
98
99 public static final String LOG4J1_EXPERIMENTAL = "log4j1.compatibility";
100
101 public static final String AUTHORIZATION_PROVIDER = "log4j2.authorizationProvider";
102
103
104
105
106
107
108
109 public static final String CATEGORY = "ConfigurationFactory";
110
111
112
113
114 protected static final Logger LOGGER = StatusLogger.getLogger();
115
116
117
118
119 protected static final String TEST_PREFIX = "log4j2-test";
120
121
122
123
124 protected static final String DEFAULT_PREFIX = "log4j2";
125
126 protected static final String LOG4J1_VERSION = "1";
127 protected static final String LOG4J2_VERSION = "2";
128
129
130
131
132 private static final String CLASS_LOADER_SCHEME = "classloader";
133
134
135
136
137 private static final String CLASS_PATH_SCHEME = "classpath";
138
139 private static final String OVERRIDE_PARAM = "override";
140
141 private static volatile List<ConfigurationFactory> factories = null;
142
143 private static ConfigurationFactory configFactory = new Factory();
144
145 protected final StrSubstitutor substitutor = new StrSubstitutor(new Interpolator());
146
147 private static final Lock LOCK = new ReentrantLock();
148
149 private static final String HTTPS = "https";
150 private static final String HTTP = "http";
151
152 private static volatile AuthorizationProvider authorizationProvider = null;
153
154
155
156
157
158 public static ConfigurationFactory getInstance() {
159
160
161 if (factories == null) {
162 LOCK.lock();
163 try {
164 if (factories == null) {
165 final List<ConfigurationFactory> list = new ArrayList<>();
166 PropertiesUtil props = PropertiesUtil.getProperties();
167 final String factoryClass = props.getStringProperty(CONFIGURATION_FACTORY_PROPERTY);
168 if (factoryClass != null) {
169 addFactory(list, factoryClass);
170 }
171 final PluginManager manager = new PluginManager(CATEGORY);
172 manager.collectPlugins();
173 final Map<String, PluginType<?>> plugins = manager.getPlugins();
174 final List<Class<? extends ConfigurationFactory>> ordered = new ArrayList<>(plugins.size());
175 for (final PluginType<?> type : plugins.values()) {
176 try {
177 ordered.add(type.getPluginClass().asSubclass(ConfigurationFactory.class));
178 } catch (final Exception ex) {
179 LOGGER.warn("Unable to add class {}", type.getPluginClass(), ex);
180 }
181 }
182 Collections.sort(ordered, OrderComparator.getInstance());
183 for (final Class<? extends ConfigurationFactory> clazz : ordered) {
184 addFactory(list, clazz);
185 }
186
187
188 factories = Collections.unmodifiableList(list);
189 authorizationProvider = authorizationProvider(props);
190 }
191 } finally {
192 LOCK.unlock();
193 }
194 }
195
196 LOGGER.debug("Using configurationFactory {}", configFactory);
197 return configFactory;
198 }
199
200 public static AuthorizationProvider authorizationProvider(PropertiesUtil props) {
201 final String authClass = props.getStringProperty(AUTHORIZATION_PROVIDER);
202 AuthorizationProvider provider = null;
203 if (authClass != null) {
204 try {
205 Object obj = LoaderUtil.newInstanceOf(authClass);
206 if (obj instanceof AuthorizationProvider) {
207 provider = (AuthorizationProvider) obj;
208 } else {
209 LOGGER.warn("{} is not an AuthorizationProvider, using default", obj.getClass().getName());
210 }
211 } catch (Exception ex) {
212 LOGGER.warn("Unable to create {}, using default: {}", authClass, ex.getMessage());
213 }
214 }
215 if (provider == null) {
216 provider = new BasicAuthorizationProvider(props);
217 }
218 return provider;
219 }
220
221 public static AuthorizationProvider getAuthorizationProvider() {
222 return authorizationProvider;
223 }
224
225 private static void addFactory(final Collection<ConfigurationFactory> list, final String factoryClass) {
226 try {
227 addFactory(list, Loader.loadClass(factoryClass).asSubclass(ConfigurationFactory.class));
228 } catch (final Exception ex) {
229 LOGGER.error("Unable to load class {}", factoryClass, ex);
230 }
231 }
232
233 private static void addFactory(final Collection<ConfigurationFactory> list,
234 final Class<? extends ConfigurationFactory> factoryClass) {
235 try {
236 list.add(ReflectionUtil.instantiate(factoryClass));
237 } catch (final Exception ex) {
238 LOGGER.error("Unable to create instance of {}", factoryClass.getName(), ex);
239 }
240 }
241
242
243
244
245
246 public static void setConfigurationFactory(final ConfigurationFactory factory) {
247 configFactory = factory;
248 }
249
250
251
252
253
254 public static void resetConfigurationFactory() {
255 configFactory = new Factory();
256 }
257
258
259
260
261
262 public static void removeConfigurationFactory(final ConfigurationFactory factory) {
263 if (configFactory == factory) {
264 configFactory = new Factory();
265 }
266 }
267
268 protected abstract String[] getSupportedTypes();
269
270 protected String getTestPrefix() {
271 return TEST_PREFIX;
272 }
273
274 protected String getDefaultPrefix() {
275 return DEFAULT_PREFIX;
276 }
277
278 protected String getVersion() {
279 return LOG4J2_VERSION;
280 }
281
282 protected boolean isActive() {
283 return true;
284 }
285
286 public abstract Configuration getConfiguration(final LoggerContext loggerContext, ConfigurationSource source);
287
288
289
290
291
292
293
294
295 public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) {
296 if (!isActive()) {
297 return null;
298 }
299 if (configLocation != null) {
300 final ConfigurationSource source = ConfigurationSource.fromUri(configLocation);
301 if (source != null) {
302 return getConfiguration(loggerContext, source);
303 }
304 }
305 return null;
306 }
307
308
309
310
311
312
313
314
315
316
317
318 public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation, final ClassLoader loader) {
319 if (!isActive()) {
320 return null;
321 }
322 if (loader == null) {
323 return getConfiguration(loggerContext, name, configLocation);
324 }
325 if (isClassLoaderUri(configLocation)) {
326 final String path = extractClassLoaderUriPath(configLocation);
327 final ConfigurationSource source = ConfigurationSource.fromResource(path, loader);
328 if (source != null) {
329 final Configuration configuration = getConfiguration(loggerContext, source);
330 if (configuration != null) {
331 return configuration;
332 }
333 }
334 }
335 return getConfiguration(loggerContext, name, configLocation);
336 }
337
338 static boolean isClassLoaderUri(final URI uri) {
339 if (uri == null) {
340 return false;
341 }
342 final String scheme = uri.getScheme();
343 return scheme == null || scheme.equals(CLASS_LOADER_SCHEME) || scheme.equals(CLASS_PATH_SCHEME);
344 }
345
346 static String extractClassLoaderUriPath(final URI uri) {
347 return uri.getScheme() == null ? uri.getPath() : uri.getSchemeSpecificPart();
348 }
349
350
351
352
353
354
355
356 protected ConfigurationSource getInputFromString(final String config, final ClassLoader loader) {
357 try {
358 final URL url = new URL(config);
359 URLConnection urlConnection = UrlConnectionFactory.createConnection(url);
360 File file = FileUtils.fileFromUri(url.toURI());
361 if (file != null) {
362 return new ConfigurationSource(urlConnection.getInputStream(), FileUtils.fileFromUri(url.toURI()));
363 } else {
364 return new ConfigurationSource(urlConnection.getInputStream(), url, urlConnection.getLastModified());
365 }
366 } catch (final Exception ex) {
367 final ConfigurationSource source = ConfigurationSource.fromResource(config, loader);
368 if (source == null) {
369 try {
370 final File file = new File(config);
371 return new ConfigurationSource(new FileInputStream(file), file);
372 } catch (final FileNotFoundException fnfe) {
373
374 LOGGER.catching(Level.DEBUG, fnfe);
375 }
376 }
377 return source;
378 }
379 }
380
381
382
383
384 private static class Factory extends ConfigurationFactory {
385
386 private static final String ALL_TYPES = "*";
387
388
389
390
391
392
393
394 @Override
395 public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) {
396
397 if (configLocation == null) {
398 final String configLocationStr = this.substitutor.replace(PropertiesUtil.getProperties()
399 .getStringProperty(CONFIGURATION_FILE_PROPERTY));
400 if (configLocationStr != null) {
401 String[] sources = parseConfigLocations(configLocationStr);
402 if (sources.length > 1) {
403 final List<AbstractConfiguration> configs = new ArrayList<>();
404 for (final String sourceLocation : sources) {
405 final Configuration config = getConfiguration(loggerContext, sourceLocation.trim());
406 if (config != null && config instanceof AbstractConfiguration) {
407 configs.add((AbstractConfiguration) config);
408 } else {
409 LOGGER.error("Failed to created configuration at {}", sourceLocation);
410 return null;
411 }
412 }
413 return new CompositeConfiguration(configs);
414 }
415 return getConfiguration(loggerContext, configLocationStr);
416 } else {
417 final String log4j1ConfigStr = this.substitutor.replace(PropertiesUtil.getProperties()
418 .getStringProperty(LOG4J1_CONFIGURATION_FILE_PROPERTY));
419 if (log4j1ConfigStr != null) {
420 System.setProperty(LOG4J1_EXPERIMENTAL, "true");
421 return getConfiguration(LOG4J1_VERSION, loggerContext, log4j1ConfigStr);
422 }
423 }
424 for (final ConfigurationFactory factory : getFactories()) {
425 final String[] types = factory.getSupportedTypes();
426 if (types != null) {
427 for (final String type : types) {
428 if (type.equals(ALL_TYPES)) {
429 final Configuration config = factory.getConfiguration(loggerContext, name, configLocation);
430 if (config != null) {
431 return config;
432 }
433 }
434 }
435 }
436 }
437 } else {
438 String[] sources = parseConfigLocations(configLocation);
439 if (sources.length > 1) {
440 final List<AbstractConfiguration> configs = new ArrayList<>();
441 for (final String sourceLocation : sources) {
442 final Configuration config = getConfiguration(loggerContext, sourceLocation.trim());
443 if (config instanceof AbstractConfiguration) {
444 configs.add((AbstractConfiguration) config);
445 } else {
446 LOGGER.error("Failed to created configuration at {}", sourceLocation);
447 return null;
448 }
449 }
450 return new CompositeConfiguration(configs);
451 }
452
453 final String configLocationStr = configLocation.toString();
454 for (final ConfigurationFactory factory : getFactories()) {
455 final String[] types = factory.getSupportedTypes();
456 if (types != null) {
457 for (final String type : types) {
458 if (type.equals(ALL_TYPES) || configLocationStr.endsWith(type)) {
459 final Configuration config = factory.getConfiguration(loggerContext, name, configLocation);
460 if (config != null) {
461 return config;
462 }
463 }
464 }
465 }
466 }
467 }
468
469 Configuration config = getConfiguration(loggerContext, true, name);
470 if (config == null) {
471 config = getConfiguration(loggerContext, true, null);
472 if (config == null) {
473 config = getConfiguration(loggerContext, false, name);
474 if (config == null) {
475 config = getConfiguration(loggerContext, false, null);
476 }
477 }
478 }
479 if (config != null) {
480 return config;
481 }
482 LOGGER.warn("No Log4j 2 configuration file found. " +
483 "Using default configuration (logging only errors to the console), " +
484 "or user programmatically provided configurations. " +
485 "Set system property 'log4j2.debug' " +
486 "to show Log4j 2 internal initialization logging. " +
487 "See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2");
488 return new DefaultConfiguration();
489 }
490
491 private Configuration getConfiguration(final LoggerContext loggerContext, final String configLocationStr) {
492 return getConfiguration(null, loggerContext, configLocationStr);
493 }
494
495 private Configuration getConfiguration(String requiredVersion, final LoggerContext loggerContext,
496 final String configLocationStr) {
497 ConfigurationSource source = null;
498 try {
499 source = ConfigurationSource.fromUri(NetUtils.toURI(configLocationStr));
500 } catch (final Exception ex) {
501
502 LOGGER.catching(Level.DEBUG, ex);
503 }
504 if (source == null) {
505 final ClassLoader loader = LoaderUtil.getThreadContextClassLoader();
506 source = getInputFromString(configLocationStr, loader);
507 }
508 if (source != null) {
509 for (final ConfigurationFactory factory : getFactories()) {
510 if (requiredVersion != null && !factory.getVersion().equals(requiredVersion)) {
511 continue;
512 }
513 final String[] types = factory.getSupportedTypes();
514 if (types != null) {
515 for (final String type : types) {
516 if (type.equals(ALL_TYPES) || configLocationStr.endsWith(type)) {
517 final Configuration config = factory.getConfiguration(loggerContext, source);
518 if (config != null) {
519 return config;
520 }
521 }
522 }
523 }
524 }
525 }
526 return null;
527 }
528
529 private Configuration getConfiguration(final LoggerContext loggerContext, final boolean isTest, final String name) {
530 final boolean named = Strings.isNotEmpty(name);
531 final ClassLoader loader = LoaderUtil.getThreadContextClassLoader();
532 for (final ConfigurationFactory factory : getFactories()) {
533 String configName;
534 final String prefix = isTest ? factory.getTestPrefix() : factory.getDefaultPrefix();
535 final String [] types = factory.getSupportedTypes();
536 if (types == null) {
537 continue;
538 }
539
540 for (final String suffix : types) {
541 if (suffix.equals(ALL_TYPES)) {
542 continue;
543 }
544 configName = named ? prefix + name + suffix : prefix + suffix;
545
546 final ConfigurationSource source = ConfigurationSource.fromResource(configName, loader);
547 if (source != null) {
548 if (!factory.isActive()) {
549 LOGGER.warn("Found configuration file {} for inactive ConfigurationFactory {}", configName, factory.getClass().getName());
550 }
551 return factory.getConfiguration(loggerContext, source);
552 }
553 }
554 }
555 return null;
556 }
557
558 @Override
559 public String[] getSupportedTypes() {
560 return null;
561 }
562
563 @Override
564 public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
565 if (source != null) {
566 final String config = source.getLocation();
567 for (final ConfigurationFactory factory : getFactories()) {
568 final String[] types = factory.getSupportedTypes();
569 if (types != null) {
570 for (final String type : types) {
571 if (type.equals(ALL_TYPES) || config != null && config.endsWith(type)) {
572 final Configuration c = factory.getConfiguration(loggerContext, source);
573 if (c != null) {
574 LOGGER.debug("Loaded configuration from {}", source);
575 return c;
576 }
577 LOGGER.error("Cannot determine the ConfigurationFactory to use for {}", config);
578 return null;
579 }
580 }
581 }
582 }
583 }
584 LOGGER.error("Cannot process configuration, input source is null");
585 return null;
586 }
587
588 private String[] parseConfigLocations(URI configLocations) {
589 final String[] uris = configLocations.toString().split("\\?");
590 final List<String> locations = new ArrayList<>();
591 if (uris.length > 1) {
592 locations.add(uris[0]);
593 final String[] pairs = configLocations.getQuery().split("&");
594 for (String pair : pairs) {
595 final int idx = pair.indexOf("=");
596 try {
597 final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
598 if (key.equalsIgnoreCase(OVERRIDE_PARAM)) {
599 locations.add(URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
600 }
601 } catch (UnsupportedEncodingException ex) {
602 LOGGER.warn("Invalid query parameter in {}", configLocations);
603 }
604 }
605 return locations.toArray(new String[0]);
606 }
607 return new String[] {uris[0]};
608 }
609
610 private String[] parseConfigLocations(String configLocations) {
611 final String[] uris = configLocations.split(",");
612 if (uris.length > 1) {
613 return uris;
614 }
615 try {
616 return parseConfigLocations(new URI(configLocations));
617 } catch (URISyntaxException ex) {
618 LOGGER.warn("Error parsing URI {}", configLocations);
619 }
620 return new String[] {configLocations};
621 }
622 }
623
624 static List<ConfigurationFactory> getFactories() {
625 return factories;
626 }
627 }