001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.config.properties;
018
019import org.apache.logging.log4j.Level;
020import org.apache.logging.log4j.core.config.ConfigurationException;
021import org.apache.logging.log4j.core.config.ConfigurationFactory;
022import org.apache.logging.log4j.core.config.ConfigurationSource;
023import org.apache.logging.log4j.core.config.Order;
024import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
025import org.apache.logging.log4j.core.config.builder.api.AppenderRefComponentBuilder;
026import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder;
027import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
028import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder;
029import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder;
030import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder;
031import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder;
032import org.apache.logging.log4j.core.config.plugins.Plugin;
033import org.apache.logging.log4j.util.PropertiesUtil;
034import org.apache.logging.log4j.util.Strings;
035
036import java.io.IOException;
037import java.io.InputStream;
038import java.util.Properties;
039
040/**
041 * Creates a PropertiesConfiguration from a properties file.
042 * @since 2.4
043 */
044@Plugin(name = "PropertiesConfigurationFactory", category = ConfigurationFactory.CATEGORY)
045@Order(8)
046public class PropertiesConfigurationFactory extends ConfigurationFactory {
047    private static final String ADVERTISER_KEY = "advertiser";
048    private static final String STATUS_KEY = "status";
049    private static final String SHUTDOWN_HOOK = "shutdownHook";
050    private static final String VERBOSE = "verbose";
051    private static final String PACKAGES = "packages";
052    private static final String CONFIG_NAME = "name";
053    private static final String MONITOR_INTERVAL = "monitorInterval";
054    private static final String CONFIG_TYPE = "type";
055
056    @Override
057    protected String[] getSupportedTypes() {
058        return new String[] {".properties"};
059    }
060
061    @Override
062    public PropertiesConfiguration getConfiguration(ConfigurationSource source) {
063        final InputStream configStream = source.getInputStream();
064        Properties properties = new Properties();
065        try {
066            properties.load(configStream);
067        } catch (IOException ioe) {
068            throw new ConfigurationException("Unable to load " + source.toString(), ioe);
069        }
070        ConfigurationBuilder<PropertiesConfiguration> builder = newConfigurationBuilder(PropertiesConfiguration.class);
071        String value = properties.getProperty(STATUS_KEY);
072        if (value != null) {
073            builder.setStatusLevel(Level.toLevel(value, Level.ERROR));
074        } else {
075            builder.setStatusLevel(Level.ERROR);
076        }
077        value = properties.getProperty(SHUTDOWN_HOOK);
078        if (value != null) {
079            builder.setShutdownHook(value);
080        }
081        value = properties.getProperty(VERBOSE);
082        if (value != null) {
083            builder.setVerbosity(value);
084        }
085        value = properties.getProperty(PACKAGES);
086        if (value != null) {
087            builder.setPackages(value);
088        }
089        value = properties.getProperty(CONFIG_NAME);
090        if (value != null) {
091            builder.setConfigurationName(value);
092        }
093        value = properties.getProperty(MONITOR_INTERVAL);
094        if (value != null) {
095            builder.setMonitorInterval(value);
096        }
097        value = properties.getProperty(ADVERTISER_KEY);
098        if (value != null) {
099            builder.setAdvertiser(value);
100        }
101        Properties props = PropertiesUtil.extractSubset(properties, "property");
102        for (String key : props.stringPropertyNames()) {
103            builder.addProperty(key, props.getProperty(key));
104        }
105
106        Properties levelProps = PropertiesUtil.extractSubset(properties, "customLevel");
107        if (levelProps.size() > 0) {
108            for (String key : levelProps.stringPropertyNames()) {
109                builder.add(builder.newCustomLevel(key, Integer.parseInt(props.getProperty(key))));
110            }
111        }
112
113        String filterProp = properties.getProperty("filters");
114        if (filterProp != null) {
115            String[] filterNames = filterProp.split(",");
116            for (String filterName : filterNames) {
117                String name = filterName.trim();
118                builder.add(createFilter(builder, name, PropertiesUtil.extractSubset(properties, "filter." + name)));
119            }
120        }
121        String appenderProp = properties.getProperty("appenders");
122        if (appenderProp != null) {
123            String[] appenderNames = appenderProp.split(",");
124            for (String appenderName : appenderNames) {
125                String name = appenderName.trim();
126                builder.add(
127                        createAppender(builder, name, PropertiesUtil.extractSubset(properties, "appender." + name)));
128            }
129        }
130        String loggerProp = properties.getProperty("loggers");
131        if (appenderProp != null) {
132            String[] loggerNames = loggerProp.split(",");
133            for (String loggerName : loggerNames) {
134                String name = loggerName.trim();
135                if (!name.equals("root")) {
136                    builder.add(
137                            createLogger(builder, name, PropertiesUtil.extractSubset(properties, "logger." + name)));
138                }
139            }
140        }
141
142        props = PropertiesUtil.extractSubset(properties, "rootLogger");
143        if (props.size() > 0) {
144            builder.add(createRootLogger(builder, props));
145        }
146
147        return builder.build();
148    }
149
150    private AppenderComponentBuilder createAppender(ConfigurationBuilder<PropertiesConfiguration> builder, String key, Properties properties) {
151        String name = properties.getProperty(CONFIG_NAME);
152        if (Strings.isEmpty(name)) {
153            throw new ConfigurationException("No name attribute provided for Appender " + key);
154        }
155        properties.remove(CONFIG_NAME);
156        String type = properties.getProperty(CONFIG_TYPE);
157        if (Strings.isEmpty(type)) {
158            throw new ConfigurationException("No type attribute provided for Appender " + key);
159        }
160        properties.remove(CONFIG_TYPE);
161        AppenderComponentBuilder appenderBuilder = builder.newAppender(name, type);
162        String filters = properties.getProperty("filters");
163        if (filters != null) {
164            properties.remove("filters");
165            String[] filterNames = filters.split(",");
166            for (String filterName : filterNames) {
167                filterName = filterName.trim();
168                Properties filterProps = PropertiesUtil.extractSubset(properties, "filter." + filterName);
169                appenderBuilder.add(createFilter(builder, filterName, filterProps));
170            }
171        }
172        Properties layoutProps = PropertiesUtil.extractSubset(properties, "layout");
173        if (layoutProps.size() > 0) {
174            appenderBuilder.add(createLayout(builder, name, layoutProps));
175        }
176
177        processRemainingProperties(appenderBuilder, name, properties);
178        return appenderBuilder;
179    }
180
181    private FilterComponentBuilder createFilter(ConfigurationBuilder<PropertiesConfiguration> builder, String key, Properties properties) {
182        String type = properties.getProperty(CONFIG_TYPE);
183        if (Strings.isEmpty(type)) {
184            throw new ConfigurationException("No type attribute provided for Appender " + key);
185        }
186        properties.remove(CONFIG_TYPE);
187        String onMatch = properties.getProperty("onMatch");
188        if (onMatch != null) {
189            properties.remove("onMatch");
190        }
191        String onMisMatch = properties.getProperty("onMisMatch");
192        if (onMisMatch != null) {
193            properties.remove("onMisMatch");
194        }
195        FilterComponentBuilder filterBuilder = builder.newFilter(type, onMatch, onMisMatch);
196        processRemainingProperties(filterBuilder, key, properties);
197        return filterBuilder;
198    }
199
200    private AppenderRefComponentBuilder createAppenderRef(ConfigurationBuilder<PropertiesConfiguration> builder, String key, Properties properties) {
201        String ref = properties.getProperty("ref");
202        if (Strings.isEmpty(ref)) {
203            throw new ConfigurationException("No ref attribute provided for AppenderRef " + key);
204        }
205        properties.remove("ref");
206        AppenderRefComponentBuilder appenderRefBuilder = builder.newAppenderRef(ref);
207        String level = properties.getProperty("level");
208        if (!Strings.isEmpty(level)) {
209            appenderRefBuilder.addAttribute("level", level);
210        }
211        String filters = properties.getProperty("filters");
212        if (filters != null) {
213            properties.remove("filters");
214            String[] filterNames = filters.split(",");
215            for (String filterName : filterNames) {
216                filterName = filterName.trim();
217                Properties filterProps = PropertiesUtil.extractSubset(properties, "filter." + filterName);
218                appenderRefBuilder.add(createFilter(builder, filterName, filterProps));
219            }
220        }
221        return appenderRefBuilder;
222    }
223
224    private LoggerComponentBuilder createLogger(ConfigurationBuilder<PropertiesConfiguration> builder, String key, Properties properties) {
225        String name = properties.getProperty(CONFIG_NAME);
226        if (Strings.isEmpty(name)) {
227            throw new ConfigurationException("No name attribute provided for Logger " + key);
228        }
229        properties.remove(CONFIG_NAME);
230        String level = properties.getProperty("level");
231        if (level != null) {
232            properties.remove("level");
233        }
234        LoggerComponentBuilder loggerBuilder;
235        String type = properties.getProperty(CONFIG_TYPE);
236        if (type != null) {
237            if (type.equalsIgnoreCase("asyncLogger")) {
238                loggerBuilder = builder.newAsyncLogger(name, level);
239            } else {
240                throw new ConfigurationException("Unknown Logger type " + type + " for Logger " + name);
241            }
242        } else {
243            loggerBuilder = builder.newLogger(name, level);
244        }
245        String appenderRefs = properties.getProperty("appenderRefs");
246        if (appenderRefs != null) {
247            properties.remove("appenderRefs");
248            String[] refNames = appenderRefs.split(",");
249            for (String appenderRef : refNames) {
250                appenderRef = appenderRef.trim();
251                Properties refProps = PropertiesUtil.extractSubset(properties, "appenderRef." + appenderRef);
252                loggerBuilder.add(createAppenderRef(builder, appenderRef, refProps));
253            }
254        }
255        String filters = properties.getProperty("filters");
256        if (filters != null) {
257            properties.remove("filters");
258            String[] filterNames = filters.split(",");
259            for (String filterName : filterNames) {
260                filterName = filterName.trim();
261                Properties filterProps = PropertiesUtil.extractSubset(properties, "filter." + filterName);
262                loggerBuilder.add(createFilter(builder, filterName, filterProps));
263            }
264        }
265        String additivity = properties.getProperty("additivity");
266        if (!Strings.isEmpty(additivity)) {
267            loggerBuilder.addAttribute("additivity", additivity);
268        }
269        return loggerBuilder;
270    }
271
272    private RootLoggerComponentBuilder createRootLogger(ConfigurationBuilder<PropertiesConfiguration> builder, Properties properties) {
273        String level = properties.getProperty("level");
274        if (level != null) {
275            properties.remove("level");
276        }
277        RootLoggerComponentBuilder loggerBuilder;
278        String type = properties.getProperty(CONFIG_TYPE);
279        if (type != null) {
280            if (type.equalsIgnoreCase("asyncRoot")) {
281                loggerBuilder = builder.newAsyncRootLogger(level);
282            } else {
283                throw new ConfigurationException("Unknown Logger type for root logger" + type);
284            }
285        } else {
286            loggerBuilder = builder.newRootLogger(level);
287        }
288        String appenderRefs = properties.getProperty("appenderRefs");
289        if (appenderRefs != null) {
290            properties.remove("appenderRefs");
291            String[] refNames = appenderRefs.split(",");
292            for (String appenderRef : refNames) {
293                appenderRef = appenderRef.trim();
294                Properties refProps = PropertiesUtil.extractSubset(properties, "appenderRef." + appenderRef);
295                loggerBuilder.add(createAppenderRef(builder, appenderRef, refProps));
296            }
297        }
298        String filters = properties.getProperty("filters");
299        if (filters != null) {
300            properties.remove("filters");
301            String[] filterNames = filters.split(",");
302            for (String filterName : filterNames) {
303                filterName = filterName.trim();
304                Properties filterProps = PropertiesUtil.extractSubset(properties, "filter." + filterName);
305                loggerBuilder.add(createFilter(builder, filterName, filterProps));
306            }
307        }
308        return loggerBuilder;
309    }
310
311    private LayoutComponentBuilder createLayout(ConfigurationBuilder<PropertiesConfiguration> builder, String appenderName, Properties properties) {
312        String type = properties.getProperty(CONFIG_TYPE);
313        if (Strings.isEmpty(type)) {
314            throw new ConfigurationException("No type attribute provided for Layout on Appender " + appenderName);
315        }
316        properties.remove(CONFIG_TYPE);
317        LayoutComponentBuilder layoutBuilder = builder.newLayout(type);
318        processRemainingProperties(layoutBuilder, appenderName, properties);
319        return layoutBuilder;
320    }
321
322    private <B extends ComponentBuilder<B>> ComponentBuilder<B> createComponent(ComponentBuilder<?> parent, String key, Properties properties) {
323        String name = properties.getProperty(CONFIG_NAME);
324        if (name != null) {
325            properties.remove(CONFIG_NAME);
326        }
327        String type = properties.getProperty(CONFIG_TYPE);
328        if (Strings.isEmpty(type)) {
329            throw new ConfigurationException("No type attribute provided for component " + key);
330        }
331        properties.remove(CONFIG_TYPE);
332        ComponentBuilder<B> componentBuilder = parent.getBuilder().newComponent(name, type);
333        processRemainingProperties(componentBuilder, name, properties);
334        return componentBuilder;
335    }
336
337    @SuppressWarnings({"unchecked", "rawtypes"})
338    private void processRemainingProperties(ComponentBuilder<?> builder, String name, Properties properties) {
339        while (properties.size() > 0) {
340            String propertyName = properties.stringPropertyNames().iterator().next();
341
342            int index = propertyName.indexOf('.');
343            if (index > 0) {
344                String prefix = propertyName.substring(0, index);
345                Properties componentProperties = PropertiesUtil.extractSubset(properties, prefix);
346                builder.addComponent(createComponent(builder, prefix, componentProperties));
347            } else  {
348                builder.addAttribute(propertyName, properties.getProperty(propertyName));
349                properties.remove(propertyName);
350            }
351        }
352    }
353}