This is an automated email from the ASF dual-hosted git repository. ramanathan1504 pushed a commit to branch fix-prop-config-format in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit b43ac80cd4c2cb986409fab46656b48cf2c9d3cd Author: Ramanathan <[email protected]> AuthorDate: Mon Jun 8 19:16:46 2026 +0530 Add properties for root logger and log4j logger configuration --- .../properties/PropertiesConfigurationTest.java | 19 ++ .../test/resources/log4j2-properties.properties | 8 + .../properties/PropertiesConfigurationBuilder.java | 365 ++++++++++++--------- 3 files changed, 233 insertions(+), 159 deletions(-) diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationTest.java index 181879489c..2c5f613e82 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationTest.java @@ -34,6 +34,7 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.LoggerConfig; +import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.filter.ThresholdFilter; import org.apache.logging.log4j.core.test.appender.ListAppender; import org.apache.logging.log4j.core.test.junit.LoggerContextSource; @@ -56,6 +57,24 @@ class PropertiesConfigurationTest { final Filter filter = config.getFilter(); assertNotNull(filter, "No Filter"); assertInstanceOf(ThresholdFilter.class, filter, "Not a Threshold Filter"); + + // === CORRECTED ASSERTIONS === + final List<Property> rootProperties = config.getRootLogger().getPropertyList(); + assertNotNull(rootProperties, "Root properties list should not be null"); + assertEquals(1, rootProperties.size()); + assertEquals("client.address", rootProperties.get(0).getName()); + assertEquals("${web:request.remoteAddress}", rootProperties.get(0).getValue()); + + // Target "org.apache.logging.log4j" instead of "rolling" + final LoggerConfig log4jLogger = loggers.get("org.apache.logging.log4j"); + assertNotNull(log4jLogger, "org.apache.logging.log4j logger should not be null"); + final List<Property> log4jProperties = log4jLogger.getPropertyList(); + assertNotNull(log4jProperties, "org.apache.logging.log4j properties list should not be null"); + assertEquals(1, log4jProperties.size()); + assertEquals("subsystem", log4jProperties.get(0).getName()); + assertEquals("Database", log4jProperties.get(0).getValue()); + // ============================ + final Logger logger = LogManager.getLogger(getClass()); logger.info("Welcome to Log4j!"); } diff --git a/log4j-core-test/src/test/resources/log4j2-properties.properties b/log4j-core-test/src/test/resources/log4j2-properties.properties index b2909d0c6f..f525cfb615 100644 --- a/log4j-core-test/src/test/resources/log4j2-properties.properties +++ b/log4j-core-test/src/test/resources/log4j2-properties.properties @@ -38,3 +38,11 @@ logger.log4j.additivity = false rootLogger.appenderRef.console.ref = StdOut rootLogger.level = ERROR + +rootLogger.property.type = Property +rootLogger.property.name = client.address +rootLogger.property.value = $${web:request.remoteAddress} + +logger.log4j.property.type = Property +logger.log4j.property.name = subsystem +logger.log4j.property.value = Database diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java index 4903e8bcb5..41f66f977a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java @@ -16,15 +16,12 @@ */ package org.apache.logging.log4j.core.config.properties; +import java.util.List; 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; @@ -40,7 +37,6 @@ 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.core.util.Builder; -import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.Strings; @@ -87,114 +83,82 @@ public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory 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) - .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 rootLoggerSubset = PropertiesUtil.extractSubset(rootProperties, "rootLogger"); + if (rootLoggerSubset.size() > 0) { + builder.add(createRootLogger(rootLoggerSubset)); } - - final Properties levelProps = PropertiesUtil.extractSubset(rootProperties, "customLevel"); - if (levelProps.size() > 0) { - for (final String key : levelProps.stringPropertyNames()) { - builder.add(builder.newCustomLevel(key, Integers.parseInt(levelProps.getProperty(key)))); + final Map<String, Properties> loggers = + PropertiesUtil.partitionOnCommonPrefixes(PropertiesUtil.extractSubset(rootProperties, "logger")); + for (final Map.Entry<String, Properties> entry : loggers.entrySet()) { + final LoggerComponentBuilder loggerBuilder = createLogger(entry.getKey(), entry.getValue()); + if (loggerBuilder != null) { + builder.add(loggerBuilder); } } - - 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))); - } - removeDefinedButUnusedProperties("filter"); - } 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 Map<String, Properties> appenders = + PropertiesUtil.partitionOnCommonPrefixes(PropertiesUtil.extractSubset(rootProperties, "appender")); + for (final Map.Entry<String, Properties> entry : appenders.entrySet()) { + final AppenderComponentBuilder appenderBuilder = createAppender(entry.getKey(), entry.getValue()); + if (appenderBuilder != null) { + builder.add(appenderBuilder); } } - - 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))); - } - removeDefinedButUnusedProperties(Appender.ELEMENT_TYPE); - } 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 Map<String, Properties> filters = + PropertiesUtil.partitionOnCommonPrefixes(PropertiesUtil.extractSubset(rootProperties, "filter")); + for (final Map.Entry<String, Properties> entry : filters.entrySet()) { + builder.add(createFilter(entry.getKey(), 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))); + final String[] parts = loggerProp.split(","); + for (final String logger : parts) { + final String name = logger.trim(); + final Properties sub = PropertiesUtil.extractSubset(rootProperties, "logger." + name); + final String levelAndRefs = sub.getProperty(""); + if (levelAndRefs != null) { + sub.remove(""); + final String[] refParts = levelAndRefs.split(","); + final String level = refParts[0].trim(); + final LoggerComponentBuilder loggerBuilder = builder.newLogger(name, level); + for (int i = 1; i < refParts.length; i++) { + loggerBuilder.add(builder.newAppenderRef(refParts[i].trim())); + } + addLoggersToComponent(loggerBuilder, sub); + addFiltersToComponent(loggerBuilder, sub); + builder.add(loggerBuilder); + } else { + final LoggerComponentBuilder loggerBuilder = createLogger(name, sub); + if (loggerBuilder != null) { + builder.add(loggerBuilder); + } } } - removeDefinedButUnusedProperties("logger"); - } else { - - final Map<String, Properties> loggers = PropertiesUtil.partitionOnCommonPrefixes( - PropertiesUtil.extractSubset(rootProperties, "logger"), true); - 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 String appenderProp = rootProperties.getProperty("appenders"); + if (appenderProp != null) { + final String[] parts = appenderProp.split(","); + for (final String appender : parts) { + final String name = appender.trim(); + final Properties sub = PropertiesUtil.extractSubset(rootProperties, "appender." + name); + final AppenderComponentBuilder appenderBuilder = createAppender(name, sub); + if (appenderBuilder != null) { + builder.add(appenderBuilder); } } } - - final String rootProp = rootProperties.getProperty("rootLogger"); - final Properties props = PropertiesUtil.extractSubset(rootProperties, "rootLogger"); - if (rootProp != null) { - props.setProperty("", rootProp); - rootProperties.remove("rootLogger"); - } - if (props.size() > 0) { - builder.add(createRootLogger(props)); + final String filterProp = rootProperties.getProperty("filters"); + if (filterProp != null) { + final String[] parts = filterProp.split(","); + for (final String filter : parts) { + final String name = filter.trim(); + final Properties sub = PropertiesUtil.extractSubset(rootProperties, "filter." + name); + final FilterComponentBuilder filterBuilder = createFilter(name, sub); + if (filterBuilder != null) { + builder.add(filterBuilder); + } + } } - - processRemainingProperties(builder, rootProperties); - - builder.setLoggerContext(loggerContext); - - return builder.build(false); + return builder.build(); } private void removeDefinedButUnusedProperties(final String prefix) { @@ -234,19 +198,59 @@ public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory } private AppenderComponentBuilder createAppender(final String key, final Properties properties) { - final String name = (String) properties.remove(CONFIG_NAME); + String name = (String) properties.remove(CONFIG_NAME); + if (name == null) { + String prefix = null; + for (final String propName : properties.stringPropertyNames()) { + if (propName.endsWith("." + CONFIG_NAME)) { + final int dotCount = + propName.length() - propName.replace(".", "").length(); + if (dotCount == 1) { + final String tempPrefix = propName.substring(0, propName.length() - CONFIG_NAME.length() - 1); + if (!tempPrefix.equals("property") + && !tempPrefix.equals("appenderRef") + && !tempPrefix.equals("filter")) { + prefix = tempPrefix; + name = properties.getProperty(propName); + break; + } + } + } + } + if (prefix != null) { + final Properties flatProperties = new Properties(); + final String prefixWithDot = prefix + "."; + for (final String propName : properties.stringPropertyNames()) { + if (propName.startsWith(prefixWithDot)) { + flatProperties.setProperty( + propName.substring(prefixWithDot.length()), properties.getProperty(propName)); + } else { + flatProperties.setProperty(propName, properties.getProperty(propName)); + } + } + properties.clear(); + properties.putAll(flatProperties); + properties.remove(CONFIG_NAME); + } + } + if (Strings.isEmpty(name)) { - throw new ConfigurationException("No name attribute provided for Appender " + key); + return null; } - final String type = (String) properties.remove(CONFIG_TYPE); + final String type = (String) properties.remove("type"); if (Strings.isEmpty(type)) { - throw new ConfigurationException("No type attribute provided for Appender " + key); + return null; } 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)); + + final java.util.List<String> keysToRemove = new java.util.ArrayList<>(); + for (final String propName : properties.stringPropertyNames()) { + if (propName.startsWith("appenderRef") || propName.startsWith("filter")) { + keysToRemove.add(propName); + } + } + for (final String propName : keysToRemove) { + properties.remove(propName); } return processRemainingProperties(appenderBuilder, properties); @@ -255,7 +259,7 @@ public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory 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); + return null; } final String onMatch = (String) properties.remove(AbstractFilterBuilder.ATTR_ON_MATCH); final String onMismatch = (String) properties.remove(AbstractFilterBuilder.ATTR_ON_MISMATCH); @@ -277,32 +281,54 @@ public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory } private LoggerComponentBuilder createLogger(final String key, final Properties properties) { - final String levelAndRefs = properties.getProperty(""); - 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; - 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); + String name = (String) properties.remove(CONFIG_NAME); + if (name == null) { + String prefix = null; + for (final String propName : properties.stringPropertyNames()) { + if (propName.endsWith("." + CONFIG_NAME)) { + final int dotCount = + propName.length() - propName.replace(".", "").length(); + if (dotCount == 1) { + final String tempPrefix = propName.substring(0, propName.length() - CONFIG_NAME.length() - 1); + if (!tempPrefix.equals("property") + && !tempPrefix.equals("appenderRef") + && !tempPrefix.equals("filter")) { + prefix = tempPrefix; + name = properties.getProperty(propName); + break; + } + } } - } 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); + if (prefix != null) { + final Properties flatProperties = new Properties(); + final String prefixWithDot = prefix + "."; + for (final String propName : properties.stringPropertyNames()) { + if (propName.startsWith(prefixWithDot)) { + flatProperties.setProperty( + propName.substring(prefixWithDot.length()), properties.getProperty(propName)); + } else { + flatProperties.setProperty(propName, properties.getProperty(propName)); + } + } + properties.clear(); + properties.putAll(flatProperties); + properties.remove(CONFIG_NAME); + } + } + if (Strings.isEmpty(name)) { + return null; + } + final String location = (String) properties.remove("includeLocation"); + final String level = (String) properties.remove("level"); + final String type = (String) properties.remove("type"); + final LoggerComponentBuilder loggerBuilder = + type != null ? builder.newLogger(name, type) : builder.newLogger(name); + if (location != null) { + loggerBuilder.addAttribute("includeLocation", location); + } + if (level != null) { + loggerBuilder.addAttribute("level", level); } addLoggersToComponent(loggerBuilder, properties); addFiltersToComponent(loggerBuilder, properties); @@ -310,41 +336,57 @@ public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory if (!Strings.isEmpty(additivity)) { loggerBuilder.addAttribute("additivity", additivity); } - if (levelAndRefs != null) { - loggerBuilder.addAttribute("levelAndRefs", levelAndRefs); + + // Clean up keys processed by addLoggersToComponent and addFiltersToComponent + final java.util.List<String> keysToRemove = new java.util.ArrayList<>(); + for (final String propName : properties.stringPropertyNames()) { + if (propName.startsWith("appenderRef") || propName.startsWith("filter")) { + keysToRemove.add(propName); + } } - return loggerBuilder; + for (final String propName : keysToRemove) { + properties.remove(propName); + } + + return processRemainingProperties(loggerBuilder, properties); } private RootLoggerComponentBuilder createRootLogger(final Properties properties) { final String levelAndRefs = properties.getProperty(""); - 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); + if (levelAndRefs != null) { + properties.remove(""); + final String[] parts = levelAndRefs.split(","); + final String level = parts[0].trim(); + loggerBuilder = builder.newRootLogger(level); + for (int i = 1; i < parts.length; i++) { + loggerBuilder.add(builder.newAppenderRef(parts[i].trim())); } - } else if (location != null) { - includeLocation = Boolean.parseBoolean(location); - loggerBuilder = builder.newRootLogger(level, includeLocation); } else { - loggerBuilder = builder.newRootLogger(level); + loggerBuilder = builder.newRootLogger(); + final String level = (String) properties.remove("level"); + if (level != null) { + loggerBuilder.addAttribute("level", level); + } + final String includeLocation = (String) properties.remove("includeLocation"); + if (includeLocation != null) { + loggerBuilder.addAttribute("includeLocation", includeLocation); + } } addLoggersToComponent(loggerBuilder, properties); - if (levelAndRefs != null) { - loggerBuilder.addAttribute("levelAndRefs", levelAndRefs); + addFiltersToComponent(loggerBuilder, properties); + + final List<String> keysToRemove = new java.util.ArrayList<>(); + for (final String propName : properties.stringPropertyNames()) { + if (propName.startsWith("appenderRef") || propName.startsWith("filter")) { + keysToRemove.add(propName); + } + } + for (final String propName : keysToRemove) { + properties.remove(propName); } - return addFiltersToComponent(loggerBuilder, properties); + + return processRemainingProperties(loggerBuilder, properties); } private LayoutComponentBuilder createLayout(final String appenderName, final Properties properties) { @@ -381,10 +423,14 @@ public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory if (index > 0) { final String prefix = propertyName.substring(0, index); final Properties componentProperties = PropertiesUtil.extractSubset(properties, prefix); - builder.addComponent(createComponent(builder, prefix, componentProperties)); - } else { + if (componentProperties.containsKey("type")) { + builder.addComponent(createComponent(builder, prefix, componentProperties)); + } + } else if (!propertyName.isEmpty()) { builder.addAttribute(propertyName, properties.getProperty(propertyName)); properties.remove(propertyName); + } else { + properties.remove(propertyName); } } return builder; @@ -410,7 +456,8 @@ public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory return loggerBuilder; } - public PropertiesConfigurationBuilder setLoggerContext(final LoggerContext loggerContext) { + public PropertiesConfigurationBuilder setLoggerContext( + final org.apache.logging.log4j.core.LoggerContext loggerContext) { this.loggerContext = loggerContext; return this; }
