blob: 34e816c4abd731fbf96b8aecb1afb660e2ca76a1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.core.config.properties;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.ConfigurationException;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.AppenderRefComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.FilterableComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.LoggableComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder;
import org.apache.logging.log4j.core.filter.AbstractFilter.AbstractFilterBuilder;
import org.apache.logging.log4j.plugins.util.Builder;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.apache.logging.log4j.util.Strings;
/**
* Helper builder for parsing properties files into a PropertiesConfiguration.
*
* @since 2.6
*/
public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory
implements Builder<PropertiesConfiguration> {
private static final String ADVERTISER_KEY = "advertiser";
private static final String STATUS_KEY = "status";
private static final String SHUTDOWN_HOOK = "shutdownHook";
private static final String SHUTDOWN_TIMEOUT = "shutdownTimeout";
private static final String VERBOSE = "verbose";
private static final String DEST = "dest";
private static final String PACKAGES = "packages";
private static final String CONFIG_NAME = "name";
private static final String MONITOR_INTERVAL = "monitorInterval";
private static final String CONFIG_TYPE = "type";
private final ConfigurationBuilder<PropertiesConfiguration> builder;
private LoggerContext loggerContext;
private Properties rootProperties;
public PropertiesConfigurationBuilder() {
this.builder = newConfigurationBuilder(PropertiesConfiguration.class);
}
public PropertiesConfigurationBuilder setRootProperties(final Properties rootProperties) {
this.rootProperties = rootProperties;
return this;
}
public PropertiesConfigurationBuilder setConfigurationSource(final ConfigurationSource source) {
builder.setConfigurationSource(source);
return this;
}
@Override
public PropertiesConfiguration build() {
for (final String key : rootProperties.stringPropertyNames()) {
if (!key.contains(".")) {
builder.addRootProperty(key, rootProperties.getProperty(key));
}
}
builder
.setStatusLevel(Level.toLevel(rootProperties.getProperty(STATUS_KEY), Level.ERROR))
.setShutdownHook(rootProperties.getProperty(SHUTDOWN_HOOK))
.setShutdownTimeout(Long.parseLong(rootProperties.getProperty(SHUTDOWN_TIMEOUT, "0")), TimeUnit.MILLISECONDS)
.setVerbosity(rootProperties.getProperty(VERBOSE))
.setDestination(rootProperties.getProperty(DEST))
.setPackages(rootProperties.getProperty(PACKAGES))
.setConfigurationName(rootProperties.getProperty(CONFIG_NAME))
.setMonitorInterval(rootProperties.getProperty(MONITOR_INTERVAL, "0"))
.setAdvertiser(rootProperties.getProperty(ADVERTISER_KEY));
final Properties propertyPlaceholders = PropertiesUtil.extractSubset(rootProperties, "property");
for (final String key : propertyPlaceholders.stringPropertyNames()) {
builder.addProperty(key, propertyPlaceholders.getProperty(key));
}
final Map<String, Properties> scripts = PropertiesUtil.partitionOnCommonPrefixes(
PropertiesUtil.extractSubset(rootProperties, "script"));
for (final Map.Entry<String, Properties> entry : scripts.entrySet()) {
final Properties scriptProps = entry.getValue();
final String type = (String) scriptProps.remove("type");
if (type == null) {
throw new ConfigurationException("No type provided for script - must be Script or ScriptFile");
}
if (type.equalsIgnoreCase("script")) {
builder.add(createScript(scriptProps));
} else {
builder.add(createScriptFile(scriptProps));
}
}
final Properties levelProps = PropertiesUtil.extractSubset(rootProperties, "customLevel");
if (levelProps.size() > 0) {
for (final String key : levelProps.stringPropertyNames()) {
builder.add(builder.newCustomLevel(key, Integer.parseInt(levelProps.getProperty(key))));
}
}
final String filterProp = rootProperties.getProperty("filters");
if (filterProp != null) {
final String[] filterNames = filterProp.split(",");
for (final String filterName : filterNames) {
final String name = filterName.trim();
builder.add(createFilter(name, PropertiesUtil.extractSubset(rootProperties, "filter." + name)));
}
} else {
final Map<String, Properties> filters = PropertiesUtil
.partitionOnCommonPrefixes(PropertiesUtil.extractSubset(rootProperties, "filter"));
for (final Map.Entry<String, Properties> entry : filters.entrySet()) {
builder.add(createFilter(entry.getKey().trim(), entry.getValue()));
}
}
final String appenderProp = rootProperties.getProperty("appenders");
if (appenderProp != null) {
final String[] appenderNames = appenderProp.split(",");
for (final String appenderName : appenderNames) {
final String name = appenderName.trim();
builder.add(createAppender(appenderName.trim(),
PropertiesUtil.extractSubset(rootProperties, "appender." + name)));
}
} else {
final Map<String, Properties> appenders = PropertiesUtil
.partitionOnCommonPrefixes(PropertiesUtil.extractSubset(rootProperties, Appender.ELEMENT_TYPE));
for (final Map.Entry<String, Properties> entry : appenders.entrySet()) {
builder.add(createAppender(entry.getKey().trim(), entry.getValue()));
}
}
final String loggerProp = rootProperties.getProperty("loggers");
if (loggerProp != null) {
final String[] loggerNames = loggerProp.split(",");
for (final String loggerName : loggerNames) {
final String name = loggerName.trim();
if (!name.equals(LoggerConfig.ROOT)) {
builder.add(createLogger(name, PropertiesUtil.extractSubset(rootProperties, "logger." +
name)));
}
}
} else {
final Map<String, Properties> loggers = PropertiesUtil
.partitionOnCommonPrefixes(PropertiesUtil.extractSubset(rootProperties, "logger"));
for (final Map.Entry<String, Properties> entry : loggers.entrySet()) {
final String name = entry.getKey().trim();
if (!name.equals(LoggerConfig.ROOT)) {
builder.add(createLogger(name, entry.getValue()));
}
}
}
final Properties props = PropertiesUtil.extractSubset(rootProperties, "rootLogger");
if (props.size() > 0) {
builder.add(createRootLogger(props));
}
builder.setLoggerContext(loggerContext);
return builder.build(false);
}
private ScriptComponentBuilder createScript(final Properties properties) {
final String name = (String) properties.remove("name");
final String language = (String) properties.remove("language");
final String text = (String) properties.remove("text");
final ScriptComponentBuilder scriptBuilder = builder.newScript(name, language, text);
return processRemainingProperties(scriptBuilder, properties);
}
private ScriptFileComponentBuilder createScriptFile(final Properties properties) {
final String name = (String) properties.remove("name");
final String path = (String) properties.remove("path");
final ScriptFileComponentBuilder scriptFileBuilder = builder.newScriptFile(name, path);
return processRemainingProperties(scriptFileBuilder, properties);
}
private AppenderComponentBuilder createAppender(final String key, final Properties properties) {
final String name = (String) properties.remove(CONFIG_NAME);
if (Strings.isEmpty(name)) {
throw new ConfigurationException("No name attribute provided for Appender " + key);
}
final String type = (String) properties.remove(CONFIG_TYPE);
if (Strings.isEmpty(type)) {
throw new ConfigurationException("No type attribute provided for Appender " + key);
}
final AppenderComponentBuilder appenderBuilder = builder.newAppender(name, type);
addFiltersToComponent(appenderBuilder, properties);
final Properties layoutProps = PropertiesUtil.extractSubset(properties, "layout");
if (layoutProps.size() > 0) {
appenderBuilder.add(createLayout(name, layoutProps));
}
return processRemainingProperties(appenderBuilder, properties);
}
private FilterComponentBuilder createFilter(final String key, final Properties properties) {
final String type = (String) properties.remove(CONFIG_TYPE);
if (Strings.isEmpty(type)) {
throw new ConfigurationException("No type attribute provided for Filter " + key);
}
final String onMatch = (String) properties.remove(AbstractFilterBuilder.ATTR_ON_MATCH);
final String onMismatch = (String) properties.remove(AbstractFilterBuilder.ATTR_ON_MISMATCH);
final FilterComponentBuilder filterBuilder = builder.newFilter(type, onMatch, onMismatch);
return processRemainingProperties(filterBuilder, properties);
}
private AppenderRefComponentBuilder createAppenderRef(final String key, final Properties properties) {
final String ref = (String) properties.remove("ref");
if (Strings.isEmpty(ref)) {
throw new ConfigurationException("No ref attribute provided for AppenderRef " + key);
}
final AppenderRefComponentBuilder appenderRefBuilder = builder.newAppenderRef(ref);
final String level = Strings.trimToNull((String) properties.remove("level"));
if (!Strings.isEmpty(level)) {
appenderRefBuilder.addAttribute("level", level);
}
return addFiltersToComponent(appenderRefBuilder, properties);
}
private LoggerComponentBuilder createLogger(final String key, final Properties properties) {
final String name = (String) properties.remove(CONFIG_NAME);
final String location = (String) properties.remove("includeLocation");
if (Strings.isEmpty(name)) {
throw new ConfigurationException("No name attribute provided for Logger " + key);
}
final String level = Strings.trimToNull((String) properties.remove("level"));
final String type = (String) properties.remove(CONFIG_TYPE);
final LoggerComponentBuilder loggerBuilder;
final boolean includeLocation;
if (type != null) {
if (type.equalsIgnoreCase("asyncLogger")) {
if (location != null) {
includeLocation = Boolean.parseBoolean(location);
loggerBuilder = builder.newAsyncLogger(name, level, includeLocation);
} else {
loggerBuilder = builder.newAsyncLogger(name, level);
}
} else {
throw new ConfigurationException("Unknown Logger type " + type + " for Logger " + name);
}
} else {
if (location != null) {
includeLocation = Boolean.parseBoolean(location);
loggerBuilder = builder.newLogger(name, level, includeLocation);
} else {
loggerBuilder = builder.newLogger(name, level);
}
}
addLoggersToComponent(loggerBuilder, properties);
addFiltersToComponent(loggerBuilder, properties);
final String additivity = (String) properties.remove("additivity");
if (!Strings.isEmpty(additivity)) {
loggerBuilder.addAttribute("additivity", additivity);
}
return loggerBuilder;
}
private RootLoggerComponentBuilder createRootLogger(final Properties properties) {
final String level = Strings.trimToNull((String) properties.remove("level"));
final String type = (String) properties.remove(CONFIG_TYPE);
final String location = (String) properties.remove("includeLocation");
final boolean includeLocation;
final RootLoggerComponentBuilder loggerBuilder;
if (type != null) {
if (type.equalsIgnoreCase("asyncRoot")) {
if (location != null) {
includeLocation = Boolean.parseBoolean(location);
loggerBuilder = builder.newAsyncRootLogger(level, includeLocation);
} else {
loggerBuilder = builder.newAsyncRootLogger(level);
}
} else {
throw new ConfigurationException("Unknown Logger type for root logger" + type);
}
} else {
if (location != null) {
includeLocation = Boolean.parseBoolean(location);
loggerBuilder = builder.newRootLogger(level, includeLocation);
} else {
loggerBuilder = builder.newRootLogger(level);
}
}
addLoggersToComponent(loggerBuilder, properties);
return addFiltersToComponent(loggerBuilder, properties);
}
private LayoutComponentBuilder createLayout(final String appenderName, final Properties properties) {
final String type = (String) properties.remove(CONFIG_TYPE);
if (Strings.isEmpty(type)) {
throw new ConfigurationException("No type attribute provided for Layout on Appender " + appenderName);
}
final LayoutComponentBuilder layoutBuilder = builder.newLayout(type);
return processRemainingProperties(layoutBuilder, properties);
}
private static <B extends ComponentBuilder<B>> ComponentBuilder<B> createComponent(final ComponentBuilder<?> parent,
final String key,
final Properties properties) {
final String name = (String) properties.remove(CONFIG_NAME);
final String type = (String) properties.remove(CONFIG_TYPE);
if (Strings.isEmpty(type)) {
throw new ConfigurationException("No type attribute provided for component " + key);
}
final ComponentBuilder<B> componentBuilder = parent.getBuilder().newComponent(name, type);
return processRemainingProperties(componentBuilder, properties);
}
private static <B extends ComponentBuilder<?>> B processRemainingProperties(final B builder,
final Properties properties) {
while (properties.size() > 0) {
final String propertyName = properties.stringPropertyNames().iterator().next();
final int index = propertyName.indexOf('.');
if (index > 0) {
final String prefix = propertyName.substring(0, index);
final Properties componentProperties = PropertiesUtil.extractSubset(properties, prefix);
builder.addComponent(createComponent(builder, prefix, componentProperties));
} else {
builder.addAttribute(propertyName, properties.getProperty(propertyName));
properties.remove(propertyName);
}
}
return builder;
}
private <B extends FilterableComponentBuilder<? extends ComponentBuilder<?>>> B addFiltersToComponent(
final B componentBuilder, final Properties properties) {
final Map<String, Properties> filters = PropertiesUtil.partitionOnCommonPrefixes(
PropertiesUtil.extractSubset(properties, "filter"));
for (final Map.Entry<String, Properties> entry : filters.entrySet()) {
componentBuilder.add(createFilter(entry.getKey().trim(), entry.getValue()));
}
return componentBuilder;
}
private <B extends LoggableComponentBuilder<? extends ComponentBuilder<?>>> B addLoggersToComponent(
final B loggerBuilder, final Properties properties) {
final Map<String, Properties> appenderRefs = PropertiesUtil.partitionOnCommonPrefixes(
PropertiesUtil.extractSubset(properties, "appenderRef"));
for (final Map.Entry<String, Properties> entry : appenderRefs.entrySet()) {
loggerBuilder.add(createAppenderRef(entry.getKey().trim(), entry.getValue()));
}
return loggerBuilder;
}
public PropertiesConfigurationBuilder setLoggerContext(final LoggerContext loggerContext) {
this.loggerContext = loggerContext;
return this;
}
public LoggerContext getLoggerContext() {
return loggerContext;
}
}