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