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