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