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