1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.logging.log4j.core.config.properties;
19
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.Properties;
23
24 import org.apache.logging.log4j.Level;
25 import org.apache.logging.log4j.core.config.ConfigurationException;
26 import org.apache.logging.log4j.core.config.ConfigurationSource;
27 import org.apache.logging.log4j.core.config.LoggerConfig;
28 import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
29 import org.apache.logging.log4j.core.config.builder.api.AppenderRefComponentBuilder;
30 import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder;
31 import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
32 import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
33 import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder;
34 import org.apache.logging.log4j.core.config.builder.api.FilterableComponentBuilder;
35 import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder;
36 import org.apache.logging.log4j.core.config.builder.api.LoggableComponentBuilder;
37 import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder;
38 import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder;
39 import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder;
40 import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder;
41 import org.apache.logging.log4j.core.util.Builder;
42 import org.apache.logging.log4j.util.PropertiesUtil;
43 import org.apache.logging.log4j.util.Strings;
44
45
46
47
48
49
50 public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory
51 implements Builder<PropertiesConfiguration> {
52
53 private static final String ADVERTISER_KEY = "advertiser";
54 private static final String STATUS_KEY = "status";
55 private static final String SHUTDOWN_HOOK = "shutdownHook";
56 private static final String VERBOSE = "verbose";
57 private static final String PACKAGES = "packages";
58 private static final String CONFIG_NAME = "name";
59 private static final String MONITOR_INTERVAL = "monitorInterval";
60 private static final String CONFIG_TYPE = "type";
61
62 private final ConfigurationBuilder<PropertiesConfiguration> builder;
63 private Properties rootProperties;
64
65 public PropertiesConfigurationBuilder() {
66 this.builder = newConfigurationBuilder(PropertiesConfiguration.class);
67 }
68
69 public PropertiesConfigurationBuilder setRootProperties(final Properties rootProperties) {
70 this.rootProperties = rootProperties;
71 return this;
72 }
73
74 public PropertiesConfigurationBuilder setConfigurationSource(ConfigurationSource source) {
75 builder.setConfigurationSource(source);
76 return this;
77 }
78
79 @Override
80 public PropertiesConfiguration build() {
81 Map<String, String> rootProps = new HashMap<>();
82 for (String key : rootProperties.stringPropertyNames()) {
83 if (!key.contains(".")) {
84 builder.addRootProperty(key, rootProperties.getProperty(key));
85 }
86 }
87 builder
88 .setStatusLevel(Level.toLevel(rootProperties.getProperty(STATUS_KEY), Level.ERROR))
89 .setShutdownHook(rootProperties.getProperty(SHUTDOWN_HOOK))
90 .setVerbosity(rootProperties.getProperty(VERBOSE))
91 .setPackages(rootProperties.getProperty(PACKAGES))
92 .setConfigurationName(rootProperties.getProperty(CONFIG_NAME))
93 .setMonitorInterval(rootProperties.getProperty(MONITOR_INTERVAL, "0"))
94 .setAdvertiser(rootProperties.getProperty(ADVERTISER_KEY));
95
96 final Properties propertyPlaceholders = PropertiesUtil.extractSubset(rootProperties, "property");
97 for (final String key : propertyPlaceholders.stringPropertyNames()) {
98 builder.addProperty(key, propertyPlaceholders.getProperty(key));
99 }
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 Map<String, Properties> filters = PropertiesUtil.partitionOnCommonPrefixes(
124 PropertiesUtil.extractSubset(rootProperties, "filter"));
125 for (final Map.Entry<String, Properties> entry : filters.entrySet()) {
126 builder.add(createFilter(entry.getKey().trim(), entry.getValue()));
127 }
128
129 final Map<String, Properties> appenders = PropertiesUtil.partitionOnCommonPrefixes(
130 PropertiesUtil.extractSubset(rootProperties, "appender"));
131 for (final Map.Entry<String, Properties> entry : appenders.entrySet()) {
132 builder.add(createAppender(entry.getKey().trim(), entry.getValue()));
133 }
134
135 final Map<String, Properties> loggers = PropertiesUtil.partitionOnCommonPrefixes(
136 PropertiesUtil.extractSubset(rootProperties, "logger"));
137 for (final Map.Entry<String, Properties> entry : loggers.entrySet()) {
138 final String name = entry.getKey().trim();
139 if (!name.equals(LoggerConfig.ROOT)) {
140 builder.add(createLogger(name, entry.getValue()));
141 }
142 }
143
144 final Properties props = PropertiesUtil.extractSubset(rootProperties, "rootLogger");
145 if (props.size() > 0) {
146 builder.add(createRootLogger(props));
147 }
148
149 return builder.build(false);
150 }
151
152 private ScriptComponentBuilder createScript(final Properties properties) {
153 final String name = (String) properties.remove("name");
154 final String language = (String) properties.remove("language");
155 final String text = (String) properties.remove("text");
156 final ScriptComponentBuilder scriptBuilder = builder.newScript(name, language, text);
157 return processRemainingProperties(scriptBuilder, properties);
158 }
159
160
161 private ScriptFileComponentBuilder createScriptFile(final Properties properties) {
162 final String name = (String) properties.remove("name");
163 final String path = (String) properties.remove("path");
164 final ScriptFileComponentBuilder scriptFileBuilder = builder.newScriptFile(name, path);
165 return processRemainingProperties(scriptFileBuilder, properties);
166 }
167
168 private AppenderComponentBuilder createAppender(final String key, final Properties properties) {
169 final String name = (String) properties.remove(CONFIG_NAME);
170 if (Strings.isEmpty(name)) {
171 throw new ConfigurationException("No name attribute provided for Appender " + key);
172 }
173 final String type = (String) properties.remove(CONFIG_TYPE);
174 if (Strings.isEmpty(type)) {
175 throw new ConfigurationException("No type attribute provided for Appender " + key);
176 }
177 final AppenderComponentBuilder appenderBuilder = builder.newAppender(name, type);
178 addFiltersToComponent(appenderBuilder, properties);
179 final Properties layoutProps = PropertiesUtil.extractSubset(properties, "layout");
180 if (layoutProps.size() > 0) {
181 appenderBuilder.add(createLayout(name, layoutProps));
182 }
183
184 return processRemainingProperties(appenderBuilder, properties);
185 }
186
187 private FilterComponentBuilder createFilter(final String key, final Properties properties) {
188 final String type = (String) properties.remove(CONFIG_TYPE);
189 if (Strings.isEmpty(type)) {
190 throw new ConfigurationException("No type attribute provided for Appender " + key);
191 }
192 final String onMatch = (String) properties.remove("onMatch");
193 final String onMisMatch = (String) properties.remove("onMisMatch");
194 final FilterComponentBuilder filterBuilder = builder.newFilter(type, onMatch, onMisMatch);
195 return processRemainingProperties(filterBuilder, properties);
196 }
197
198 private AppenderRefComponentBuilder createAppenderRef(final String key, final Properties properties) {
199 final String ref = (String) properties.remove("ref");
200 if (Strings.isEmpty(ref)) {
201 throw new ConfigurationException("No ref attribute provided for AppenderRef " + key);
202 }
203 final AppenderRefComponentBuilder appenderRefBuilder = builder.newAppenderRef(ref);
204 final String level = (String) properties.remove("level");
205 if (!Strings.isEmpty(level)) {
206 appenderRefBuilder.addAttribute("level", level);
207 }
208 return addFiltersToComponent(appenderRefBuilder, properties);
209 }
210
211 private LoggerComponentBuilder createLogger(final String key, final Properties properties) {
212 final String name = (String) properties.remove(CONFIG_NAME);
213 final String location = (String) properties.remove("includeLocation");
214 if (Strings.isEmpty(name)) {
215 throw new ConfigurationException("No name attribute provided for Logger " + key);
216 }
217 final String level = (String) properties.remove("level");
218 final String type = (String) properties.remove(CONFIG_TYPE);
219 final LoggerComponentBuilder loggerBuilder;
220 boolean includeLocation;
221 if (type != null) {
222 if (type.equalsIgnoreCase("asyncLogger")) {
223 if (location != null) {
224 includeLocation = Boolean.parseBoolean(location);
225 loggerBuilder = builder.newAsyncLogger(name, level, includeLocation);
226 } else {
227 loggerBuilder = builder.newAsyncLogger(name, level);
228 }
229 } else {
230 throw new ConfigurationException("Unknown Logger type " + type + " for Logger " + name);
231 }
232 } else {
233 if (location != null) {
234 includeLocation = Boolean.parseBoolean(location);
235 loggerBuilder = builder.newLogger(name, level, includeLocation);
236 } else {
237 loggerBuilder = builder.newLogger(name, level);
238 }
239 }
240 addLoggersToComponent(loggerBuilder, properties);
241 addFiltersToComponent(loggerBuilder, properties);
242 final String additivity = (String) properties.remove("additivity");
243 if (!Strings.isEmpty(additivity)) {
244 loggerBuilder.addAttribute("additivity", additivity);
245 }
246 return loggerBuilder;
247 }
248
249 private RootLoggerComponentBuilder createRootLogger(final Properties properties) {
250 final String level = (String) properties.remove("level");
251 final String type = (String) properties.remove(CONFIG_TYPE);
252 final String location = (String) properties.remove("includeLocation");
253 final boolean includeLocation;
254 final RootLoggerComponentBuilder loggerBuilder;
255 if (type != null) {
256 if (type.equalsIgnoreCase("asyncRoot")) {
257 if (location != null) {
258 includeLocation = Boolean.parseBoolean(location);
259 loggerBuilder = builder.newAsyncRootLogger(level, includeLocation);
260 } else {
261 loggerBuilder = builder.newAsyncRootLogger(level);
262 }
263 } else {
264 throw new ConfigurationException("Unknown Logger type for root logger" + type);
265 }
266 } else {
267 if (location != null) {
268 includeLocation = Boolean.parseBoolean(location);
269 loggerBuilder = builder.newRootLogger(level, includeLocation);
270 } else {
271 loggerBuilder = builder.newRootLogger(level);
272 }
273 }
274 addLoggersToComponent(loggerBuilder, properties);
275 return addFiltersToComponent(loggerBuilder, properties);
276 }
277
278 private LayoutComponentBuilder createLayout(final String appenderName, final Properties properties) {
279 final String type = (String) properties.remove(CONFIG_TYPE);
280 if (Strings.isEmpty(type)) {
281 throw new ConfigurationException("No type attribute provided for Layout on Appender " + appenderName);
282 }
283 final LayoutComponentBuilder layoutBuilder = builder.newLayout(type);
284 return processRemainingProperties(layoutBuilder, properties);
285 }
286
287 private static <B extends ComponentBuilder<B>> ComponentBuilder<B> createComponent(final ComponentBuilder<?> parent,
288 final String key,
289 final Properties properties) {
290 final String name = (String) properties.remove(CONFIG_NAME);
291 final String type = (String) properties.remove(CONFIG_TYPE);
292 if (Strings.isEmpty(type)) {
293 throw new ConfigurationException("No type attribute provided for component " + key);
294 }
295 final ComponentBuilder<B> componentBuilder = parent.getBuilder().newComponent(name, type);
296 return processRemainingProperties(componentBuilder, properties);
297 }
298
299 private static <B extends ComponentBuilder<?>> B processRemainingProperties(final B builder,
300 final Properties properties) {
301 while (properties.size() > 0) {
302 final String propertyName = properties.stringPropertyNames().iterator().next();
303 int index = propertyName.indexOf('.');
304 if (index > 0) {
305 final String prefix = propertyName.substring(0, index);
306 final Properties componentProperties = PropertiesUtil.extractSubset(properties, prefix);
307 builder.addComponent(createComponent(builder, prefix, componentProperties));
308 } else {
309 builder.addAttribute(propertyName, properties.getProperty(propertyName));
310 properties.remove(propertyName);
311 }
312 }
313 return builder;
314 }
315
316 private <B extends FilterableComponentBuilder<? extends ComponentBuilder<?>>> B addFiltersToComponent(
317 final B componentBuilder, final Properties properties) {
318 final Map<String, Properties> filters = PropertiesUtil.partitionOnCommonPrefixes(
319 PropertiesUtil.extractSubset(properties, "filter"));
320 for (final Map.Entry<String, Properties> entry : filters.entrySet()) {
321 componentBuilder.add(createFilter(entry.getKey().trim(), entry.getValue()));
322 }
323 return componentBuilder;
324 }
325
326 private <B extends LoggableComponentBuilder<? extends ComponentBuilder<?>>> B addLoggersToComponent(
327 final B loggerBuilder, final Properties properties) {
328 final Map<String, Properties> appenderRefs = PropertiesUtil.partitionOnCommonPrefixes(
329 PropertiesUtil.extractSubset(properties, "appenderRef"));
330 for (final Map.Entry<String, Properties> entry : appenderRefs.entrySet()) {
331 loggerBuilder.add(createAppenderRef(entry.getKey().trim(), entry.getValue()));
332 }
333 return loggerBuilder;
334 }
335 }