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