This is an automated email from the ASF dual-hosted git repository.
rgoers pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/release-2.x by this push:
new 5a459dd LOG4J2-3341 - Support specifying the level and appender
references on the Logger as on property
5a459dd is described below
commit 5a459dda1e731dfa93198db63493c1cb1cd1e941
Author: Ralph Goers <[email protected]>
AuthorDate: Fri Feb 11 23:47:50 2022 -0700
LOG4J2-3341 - Support specifying the level and appender references on the
Logger as on property
---
.../apache/logging/log4j/util/PropertiesUtil.java | 24 +-
log4j-core/revapi.json | 42 +++
.../log4j/core/async/AsyncLoggerConfig.java | 41 ++-
.../logging/log4j/core/config/LoggerConfig.java | 315 ++++++++++++++++++++-
.../properties/PropertiesConfigurationBuilder.java | 55 ++--
.../properties/PropertiesConfigurationTest.java | 17 ++
.../resources/LoggerLevelAppenderTest.properties | 2 +
... => LoggerLevelSysPropsAppenderTest.properties} | 8 +-
.../RootLoggerLevelAppenderTest.properties | 3 +-
src/changes/changes.xml | 4 +-
src/site/xdoc/manual/configuration.xml.vm | 22 +-
11 files changed, 461 insertions(+), 72 deletions(-)
diff --git
a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
index 0fe8502..250333b 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
@@ -549,10 +549,32 @@ public final class PropertiesUtil {
* @since 2.6
*/
public static Map<String, Properties> partitionOnCommonPrefixes(final
Properties properties) {
+ return partitionOnCommonPrefixes(properties, false);
+ }
+
+ /**
+ * Partitions a properties map based on common key prefixes up to the
first period.
+ *
+ * @param properties properties to partition
+ * @param includeBaseKey when true if a key exists with no '.' the key
will be included.
+ * @return the partitioned properties where each key is the common prefix
(minus the period) and the values are
+ * new property maps without the prefix and period in the key
+ * @since 2.17.2
+ */
+ public static Map<String, Properties> partitionOnCommonPrefixes(final
Properties properties,
+ final boolean includeBaseKey) {
final Map<String, Properties> parts = new ConcurrentHashMap<>();
for (final String key : properties.stringPropertyNames()) {
final int idx = key.indexOf('.');
- if (idx < 0) continue;
+ if (idx < 0) {
+ if (includeBaseKey) {
+ if (!parts.containsKey(key)) {
+ parts.put(key, new Properties());
+ }
+ parts.get(key).setProperty("",
properties.getProperty(key));
+ }
+ continue;
+ }
final String prefix = key.substring(0, idx);
if (!parts.containsKey(prefix)) {
parts.put(prefix, new Properties());
diff --git a/log4j-core/revapi.json b/log4j-core/revapi.json
index 10e586d..9c7f874 100644
--- a/log4j-core/revapi.json
+++ b/log4j-core/revapi.json
@@ -51,6 +51,48 @@
"justification": "LOG4J2-2486 - Require enabled script languages to be
specified in a system property"
},
{
+ "code": "java.annotation.removed",
+ "old": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.config.LoggerConfig::createLogger(boolean,
org.apache.logging.log4j.Level, java.lang.String, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter) @
org.apache.logging.log4j.core.async.AsyncLoggerConfig.RootLogger",
+ "new": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.config.LoggerConfig::createLogger(boolean,
org.apache.logging.log4j.Level, java.lang.String, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter) @
org.apache.logging.log4j.core.async.AsyncLoggerConfig.RootLogger",
+ "annotation":
"@org.apache.logging.log4j.core.config.plugins.PluginFactory",
+ "justification": "LoggerConfig now uses a Builder"
+ },
+ {
+ "code": "java.annotation.removed",
+ "old": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.async.AsyncLoggerConfig.RootLogger::createLogger(java.lang.String,
org.apache.logging.log4j.Level, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter)",
+ "new": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.async.AsyncLoggerConfig.RootLogger::createLogger(java.lang.String,
org.apache.logging.log4j.Level, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter)",
+ "annotation":
"@org.apache.logging.log4j.core.config.plugins.PluginFactory",
+ "justification": "AsyncLoggerConfig.RootLogger now uses a Builder"
+ },
+ {
+ "code": "java.annotation.removed",
+ "old": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.async.AsyncLoggerConfig::createLogger(boolean,
org.apache.logging.log4j.Level, java.lang.String, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter)",
+ "new": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.async.AsyncLoggerConfig::createLogger(boolean,
org.apache.logging.log4j.Level, java.lang.String, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter)",
+ "annotation":
"@org.apache.logging.log4j.core.config.plugins.PluginFactory",
+ "justification": "AsyncLoggerConfig now uses a Builder"
+ },
+ {
+ "code": "java.annotation.removed",
+ "old": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.config.LoggerConfig::createLogger(boolean,
org.apache.logging.log4j.Level, java.lang.String, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter) @
org.apache.logging.log4j.core.config.LoggerConfig.RootLogger",
+ "new": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.config.LoggerConfig::createLogger(boolean,
org.apache.logging.log4j.Level, java.lang.String, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter) @
org.apache.logging.log4j.core.config.LoggerConfig.RootLogger",
+ "annotation":
"@org.apache.logging.log4j.core.config.plugins.PluginFactory",
+ "justification": "LoggerConfig now uses a Builder"
+ },
+ {
+ "code": "java.annotation.removed",
+ "old": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.config.LoggerConfig.RootLogger::createLogger(java.lang.String,
org.apache.logging.log4j.Level, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter)",
+ "new": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.config.LoggerConfig.RootLogger::createLogger(java.lang.String,
org.apache.logging.log4j.Level, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter)",
+ "annotation":
"@org.apache.logging.log4j.core.config.plugins.PluginFactory",
+ "justification": "LoggerConfig.RootLogger now uses a Builder"
+ },
+ {
+ "code": "java.annotation.removed",
+ "old": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.config.LoggerConfig::createLogger(boolean,
org.apache.logging.log4j.Level, java.lang.String, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter)",
+ "new": "method org.apache.logging.log4j.core.config.LoggerConfig
org.apache.logging.log4j.core.config.LoggerConfig::createLogger(boolean,
org.apache.logging.log4j.Level, java.lang.String, java.lang.String,
org.apache.logging.log4j.core.config.AppenderRef[],
org.apache.logging.log4j.core.config.Property[],
org.apache.logging.log4j.core.config.Configuration,
org.apache.logging.log4j.core.Filter)",
+ "annotation":
"@org.apache.logging.log4j.core.config.plugins.PluginFactory",
+ "justification": "LoggerConfig now uses a Builder"
+ },
+ {
"code": "java.method.returnTypeChanged",
"old": "method java.lang.String
org.apache.logging.log4j.core.lookup.StrSubstitutor::resolveVariable(org.apache.logging.log4j.core.LogEvent,
java.lang.String, java.lang.StringBuilder, int, int)",
"new": "method org.apache.logging.log4j.core.lookup.LookupResult
org.apache.logging.log4j.core.lookup.StrSubstitutor::resolveVariable(org.apache.logging.log4j.core.LogEvent,
java.lang.String, java.lang.StringBuilder, int, int)",
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java
index f8d4102..2cd731b 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java
@@ -32,6 +32,7 @@ import org.apache.logging.log4j.core.config.Node;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
@@ -71,6 +72,23 @@ import org.apache.logging.log4j.util.Strings;
@Plugin(name = "asyncLogger", category = Node.CATEGORY, printObject = true)
public class AsyncLoggerConfig extends LoggerConfig {
+ @PluginBuilderFactory
+ public static <B extends Builder<B>> B newAsyncBuilder() {
+ return new Builder<B>().asBuilder();
+ }
+
+ public static class Builder<B extends Builder<B>> extends
LoggerConfig.Builder<B> {
+
+ @Override
+ public LoggerConfig build() {
+ final String name = getLoggerName().equals(ROOT) ? Strings.EMPTY :
getLoggerName();
+ LevelAndRefs container = LoggerConfig.getLevelAndRefs(getLevel(),
getRefs(), getLevelAndRefs(),
+ getConfig());
+ return new AsyncLoggerConfig(name, container.refs,getFilter(),
container.level, isAdditivity(),
+ getProperties(), getConfig(),
includeLocation(getIncludeLocation(), getConfig()));
+ }
+ }
+
private static final ThreadLocal<Boolean> ASYNC_LOGGER_ENTERED = new
ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
@@ -258,7 +276,7 @@ public class AsyncLoggerConfig extends LoggerConfig {
* @return A new LoggerConfig.
* @since 3.0
*/
- @PluginFactory
+ @Deprecated
public static LoggerConfig createLogger(
@PluginAttribute(value = "additivity", defaultBoolean = true)
final boolean additivity,
@PluginAttribute("level") final Level level,
@@ -284,6 +302,23 @@ public class AsyncLoggerConfig extends LoggerConfig {
@Plugin(name = "asyncRoot", category = Core.CATEGORY_NAME, printObject =
true)
public static class RootLogger extends LoggerConfig {
+ @PluginBuilderFactory
+ public static <B extends Builder<B>> B newAsyncRootBuilder() {
+ return new Builder<B>().asBuilder();
+ }
+
+ public static class Builder<B extends Builder<B>> extends
RootLogger.Builder<B> {
+
+ @Override
+ public LoggerConfig build() {
+ LevelAndRefs container =
LoggerConfig.getLevelAndRefs(getLevel(), getRefs(), getLevelAndRefs(),
+ getConfig());
+ return new AsyncLoggerConfig(LogManager.ROOT_LOGGER_NAME,
container.refs, getFilter(), container.level,
+ isAdditivity(), getProperties(), getConfig(),
includeLocation(getIncludeLocation(),
+ getConfig()));
+ }
+ }
+
/**
* @deprecated use {@link #createLogger(String, Level, String,
AppenderRef[], Property[], Configuration, Filter)}
*/
@@ -311,9 +346,9 @@ public class AsyncLoggerConfig extends LoggerConfig {
}
/**
- * @since 3.0
+ *
*/
- @PluginFactory
+ @Deprecated
public static LoggerConfig createLogger(
@PluginAttribute("additivity") final String additivity,
@PluginAttribute("level") final Level level,
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java
index d7d7ce6..cb76aed 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java
@@ -31,15 +31,20 @@ import org.apache.logging.log4j.core.Core;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender;
+import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
import org.apache.logging.log4j.core.async.AsyncLoggerContext;
import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import
org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+import org.apache.logging.log4j.core.config.properties.PropertiesConfiguration;
import org.apache.logging.log4j.core.filter.AbstractFilterable;
import org.apache.logging.log4j.core.impl.DefaultLogEventFactory;
import org.apache.logging.log4j.core.impl.LocationAware;
@@ -99,6 +104,136 @@ public class LoggerConfig extends AbstractFilterable
implements LocationAware {
}
}
+ @PluginBuilderFactory
+ public static <B extends Builder<B>> B newBuilder() {
+ return new Builder<B>().asBuilder();
+ }
+
+ /**
+ * Builds LoggerConfig instances.
+ *
+ * @param <B>
+ * The type to build
+ */
+ public static class Builder<B extends Builder<B>>
+ implements
org.apache.logging.log4j.core.util.Builder<LoggerConfig> {
+
+ @PluginBuilderAttribute
+ private Boolean additivity;
+ @PluginBuilderAttribute
+ private Level level;
+ @PluginBuilderAttribute
+ private String levelAndRefs;
+ @PluginBuilderAttribute("name")
+ @Required(message = "Loggers cannot be configured without a name")
+ private String loggerName;
+ @PluginBuilderAttribute
+ private String includeLocation;
+ @PluginElement("AppenderRef")
+ private AppenderRef[] refs;
+ @PluginElement("Properties")
+ private Property[] properties;
+ @PluginConfiguration
+ private Configuration config;
+ @PluginElement("Filter")
+ private Filter filter;
+
+ public boolean isAdditivity() {
+ return additivity == null || additivity;
+ }
+
+ public B withAdditivity(boolean additivity) {
+ this.additivity = additivity;
+ return asBuilder();
+ }
+
+ public Level getLevel() {
+ return level;
+ }
+
+ public B withLevel(Level level) {
+ this.level = level;
+ return asBuilder();
+ }
+
+ public String getLevelAndRefs() {
+ return levelAndRefs;
+ }
+
+ public B withLevelAndRefs(String levelAndRefs) {
+ this.levelAndRefs = levelAndRefs;
+ return asBuilder();
+ }
+
+ public String getLoggerName() {
+ return loggerName;
+ }
+
+ public B withLoggerName(String loggerName) {
+ this.loggerName = loggerName;
+ return asBuilder();
+ }
+
+ public String getIncludeLocation() {
+ return includeLocation;
+ }
+
+ public B withIncludeLocation(String includeLocation) {
+ this.includeLocation = includeLocation;
+ return asBuilder();
+ }
+
+ public AppenderRef[] getRefs() {
+ return refs;
+ }
+
+ public B withRefs(AppenderRef[] refs) {
+ this.refs = refs;
+ return asBuilder();
+ }
+
+ public Property[] getProperties() {
+ return properties;
+ }
+
+ public B withProperties(Property[] properties) {
+ this.properties = properties;
+ return asBuilder();
+ }
+
+ public Configuration getConfig() {
+ return config;
+ }
+
+ public B withConfig(Configuration config) {
+ this.config = config;
+ return asBuilder();
+ }
+
+ public Filter getFilter() {
+ return filter;
+ }
+
+ public B withtFilter(Filter filter) {
+ this.filter = filter;
+ return asBuilder();
+ }
+
+ @Override
+ public LoggerConfig build() {
+ final String name = loggerName.equals(ROOT) ? Strings.EMPTY :
loggerName;
+ LevelAndRefs container = LoggerConfig.getLevelAndRefs(level, refs,
levelAndRefs, config);
+ boolean useLocation = includeLocation(includeLocation, config);
+ return new LoggerConfig(name, container.refs, filter,
container.level, isAdditivity(), properties, config,
+ useLocation);
+ }
+
+ @SuppressWarnings("unchecked")
+ public B asBuilder() {
+ return (B) this;
+ }
+ }
+
/**
* Default constructor.
*/
@@ -598,22 +733,22 @@ public class LoggerConfig extends AbstractFilterable
implements LocationAware {
* @return A new LoggerConfig.
* @since 2.6
*/
- @PluginFactory
+ @Deprecated
public static LoggerConfig createLogger(
- // @formatter:off
- @PluginAttribute(value = "additivity", defaultBoolean = true) final
boolean additivity,
- @PluginAttribute("level") final Level level,
- @Required(message = "Loggers cannot be configured without a name")
@PluginAttribute("name") final String loggerName,
- @PluginAttribute("includeLocation") final String includeLocation,
- @PluginElement("AppenderRef") final AppenderRef[] refs,
- @PluginElement("Properties") final Property[] properties,
- @PluginConfiguration final Configuration config,
- @PluginElement("Filter") final Filter filter
- // @formatter:on
+ // @formatter:off
+ @PluginAttribute(value = "additivity", defaultBoolean = true)
final boolean additivity,
+ @PluginAttribute("level") final Level level,
+ @Required(message = "Loggers cannot be configured without a name")
@PluginAttribute("name") final String loggerName,
+ @PluginAttribute("includeLocation") final String includeLocation,
+ @PluginElement("AppenderRef") final AppenderRef[] refs,
+ @PluginElement("Properties") final Property[] properties,
+ @PluginConfiguration final Configuration config,
+ @PluginElement("Filter") final Filter filter
+ // @formatter:on
) {
final String name = loggerName.equals(ROOT) ? Strings.EMPTY :
loggerName;
return new LoggerConfig(name, Arrays.asList(refs), filter, level,
additivity, properties, config,
- includeLocation(includeLocation, config));
+ includeLocation(includeLocation, config));
}
/**
@@ -651,7 +786,124 @@ public class LoggerConfig extends AbstractFilterable
implements LocationAware {
@Plugin(name = ROOT, category = Core.CATEGORY_NAME, printObject = true)
public static class RootLogger extends LoggerConfig {
- @PluginFactory
+ @PluginBuilderFactory
+ public static <B extends Builder<B>> B newRootBuilder() {
+ return new Builder<B>().asBuilder();
+ }
+
+ /**
+ * Builds LoggerConfig instances.
+ *
+ * @param <B>
+ * The type to build
+ */
+ public static class Builder<B extends Builder<B>>
+ implements
org.apache.logging.log4j.core.util.Builder<LoggerConfig> {
+
+ @PluginBuilderAttribute
+ private boolean additivity;
+ @PluginBuilderAttribute
+ private Level level;
+ @PluginBuilderAttribute
+ private String levelAndRefs;
+ @PluginBuilderAttribute
+ private String includeLocation;
+ @PluginElement("AppenderRef")
+ private AppenderRef[] refs;
+ @PluginElement("Properties")
+ private Property[] properties;
+ @PluginConfiguration
+ private Configuration config;
+ @PluginElement("Filter")
+ private Filter filter;
+
+ public boolean isAdditivity() {
+ return additivity;
+ }
+
+ public B withAdditivity(boolean additivity) {
+ this.additivity = additivity;
+ return asBuilder();
+ }
+
+ public Level getLevel() {
+ return level;
+ }
+
+ public B withLevel(Level level) {
+ this.level = level;
+ return asBuilder();
+ }
+
+ public String getLevelAndRefs() {
+ return levelAndRefs;
+ }
+
+ public B withLevelAndRefs(String levelAndRefs) {
+ this.levelAndRefs = levelAndRefs;
+ return asBuilder();
+ }
+
+ public String getIncludeLocation() {
+ return includeLocation;
+ }
+
+ public B withIncludeLocation(String includeLocation) {
+ this.includeLocation = includeLocation;
+ return asBuilder();
+ }
+
+ public AppenderRef[] getRefs() {
+ return refs;
+ }
+
+ public B withRefs(AppenderRef[] refs) {
+ this.refs = refs;
+ return asBuilder();
+ }
+
+ public Property[] getProperties() {
+ return properties;
+ }
+
+ public B withProperties(Property[] properties) {
+ this.properties = properties;
+ return asBuilder();
+ }
+
+ public Configuration getConfig() {
+ return config;
+ }
+
+ public B withConfig(Configuration config) {
+ this.config = config;
+ return asBuilder();
+ }
+
+ public Filter getFilter() {
+ return filter;
+ }
+
+ public B withtFilter(Filter filter) {
+ this.filter = filter;
+ return asBuilder();
+ }
+
+ @Override
+ public LoggerConfig build() {
+ LevelAndRefs container = LoggerConfig.getLevelAndRefs(level,
refs, levelAndRefs, config);
+ return new LoggerConfig(LogManager.ROOT_LOGGER_NAME,
container.refs, filter, container.level,
+ additivity, properties, config,
includeLocation(includeLocation, config));
+ }
+
+ @SuppressWarnings("unchecked")
+ public B asBuilder() {
+ return (B) this;
+ }
+ }
+
+
+ @Deprecated
public static LoggerConfig createLogger(
// @formatter:off
@PluginAttribute("additivity") final String additivity,
@@ -665,12 +917,47 @@ public class LoggerConfig extends AbstractFilterable
implements LocationAware {
final List<AppenderRef> appenderRefs = Arrays.asList(refs);
final Level actualLevel = level == null ? Level.ERROR : level;
final boolean additive = Booleans.parseBoolean(additivity, true);
-
return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs,
filter, actualLevel, additive,
properties, config, includeLocation(includeLocation,
config));
}
}
+ protected static LevelAndRefs getLevelAndRefs(Level level, AppenderRef[]
refs, String levelAndRefs,
+ Configuration config) {
+ LevelAndRefs result = new LevelAndRefs();
+ if (levelAndRefs != null) {
+ if (config instanceof PropertiesConfiguration) {
+ if (level != null) {
+ LOGGER.warn("Level is ignored when levelAndRefs syntax is
used.");
+ }
+ if (refs != null && refs.length > 0) {
+ LOGGER.warn("Appender references are ignored when
levelAndRefs syntax is used");
+ }
+ final String[] parts = Strings.splitList(levelAndRefs);
+ result.level = Level.getLevel(parts[0]);
+ if (parts.length > 1) {
+ List<AppenderRef> refList = new ArrayList<>();
+ Arrays.stream(parts).skip(1).forEach((ref) ->
+ refList.add(AppenderRef.createAppenderRef(ref,
null, null)));
+ result.refs = refList;
+ }
+ } else {
+ LOGGER.warn("levelAndRefs are only allowed in a properties
configuration. The value is ignored.");
+ result.level = level;
+ result.refs = Arrays.asList(refs);
+ }
+ } else {
+ result.level = level;
+ result.refs = Arrays.asList(refs);
+ }
+ return result;
+ }
+
+ protected static class LevelAndRefs {
+ public Level level;
+ public List<AppenderRef> refs;
+ }
+
protected enum LoggerConfigPredicate {
ALL() {
@Override
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 bc7c0cd..6564a29 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
@@ -17,8 +17,12 @@
package org.apache.logging.log4j.core.config.properties;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.ConfigurationException;
@@ -39,15 +43,9 @@ 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.status.StatusLogger;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.apache.logging.log4j.util.Strings;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Properties;
-import java.util.concurrent.TimeUnit;
-
/**
* Helper builder for parsing properties files into a PropertiesConfiguration.
*
@@ -56,7 +54,6 @@ import java.util.concurrent.TimeUnit;
public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory
implements Builder<PropertiesConfiguration> {
- private static final Logger LOGGER = StatusLogger.getLogger();
private static final String ADVERTISER_KEY = "advertiser";
private static final String STATUS_KEY = "status";
private static final String SHUTDOWN_HOOK = "shutdownHook";
@@ -174,20 +171,23 @@ public class PropertiesConfigurationBuilder extends
ConfigurationBuilderFactory
}
}
} else {
- final Properties context =
PropertiesUtil.extractSubset(rootProperties, "logger");
- final Map<String, Properties> loggers =
PropertiesUtil.partitionOnCommonPrefixes(context);
+
+ 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)) {
- final Properties loggerProps = entry.getValue();
- useSyntheticLevelAndAppenderRefs(name, loggerProps,
context);
- builder.add(createLogger(name, loggerProps));
+ builder.add(createLogger(name, entry.getValue()));
}
}
}
+ String rootProp = rootProperties.getProperty("rootLogger");
final Properties props = PropertiesUtil.extractSubset(rootProperties,
"rootLogger");
- useSyntheticLevelAndAppenderRefs("rootLogger", props, rootProperties);
+ if (rootProp != null) {
+ props.setProperty("", rootProp);
+ rootProperties.remove("rootLogger");
+ }
if (props.size() > 0) {
builder.add(createRootLogger(props));
}
@@ -197,23 +197,6 @@ public class PropertiesConfigurationBuilder extends
ConfigurationBuilderFactory
return builder.build(false);
}
- private static void useSyntheticLevelAndAppenderRefs(final String
propertyName, final Properties loggerProps, final Properties context) {
- final String propertyValue = (String) context.remove(propertyName);
- if (propertyValue != null) {
- final String[] parts = propertyValue.split(",");
- if (parts.length > 0) {
- final String level = parts[0].trim();
- loggerProps.setProperty("level", level);
- LOGGER.debug("Using log level '{}' for logger '{}'", level,
propertyName);
- for (int i = 1; i < parts.length; i++) {
- final String appenderRef = parts[i].trim();
- LOGGER.debug("Adding synthetic appender ref '{}' for
logger '{}'", appenderRef, propertyName);
- loggerProps.setProperty("appenderRef.$" + i + ".ref",
appenderRef);
- }
- }
- }
- }
-
private ScriptComponentBuilder createScript(final Properties properties) {
final String name = (String) properties.remove("name");
final String language = (String) properties.remove("language");
@@ -231,7 +214,7 @@ public class PropertiesConfigurationBuilder extends
ConfigurationBuilderFactory
}
private AppenderComponentBuilder createAppender(final String key, final
Properties properties) {
- final String name = Objects.toString(properties.remove(CONFIG_NAME),
key);
+ final String name = (String) properties.remove(CONFIG_NAME);
if (Strings.isEmpty(name)) {
throw new ConfigurationException("No name attribute provided for
Appender " + key);
}
@@ -274,6 +257,7 @@ 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)) {
@@ -306,10 +290,14 @@ public class PropertiesConfigurationBuilder extends
ConfigurationBuilderFactory
if (!Strings.isEmpty(additivity)) {
loggerBuilder.addAttribute("additivity", additivity);
}
+ if (levelAndRefs != null) {
+ loggerBuilder.addAttribute("levelAndRefs", levelAndRefs);
+ }
return loggerBuilder;
}
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");
@@ -333,6 +321,9 @@ public class PropertiesConfigurationBuilder extends
ConfigurationBuilderFactory
loggerBuilder = builder.newRootLogger(level);
}
addLoggersToComponent(loggerBuilder, properties);
+ if (levelAndRefs != null) {
+ loggerBuilder.addAttribute("levelAndRefs", levelAndRefs);
+ }
return addFiltersToComponent(loggerBuilder, properties);
}
diff --git
a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationTest.java
b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationTest.java
index ec6fcd5..d975a00 100644
---
a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationTest.java
+++
b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationTest.java
@@ -31,6 +31,7 @@ import org.apache.logging.log4j.junit.LoggerContextSource;
import org.apache.logging.log4j.junit.Named;
import org.apache.logging.log4j.test.appender.ListAppender;
import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.SetSystemProperty;
import java.util.List;
import java.util.Map;
@@ -130,4 +131,20 @@ class PropertiesConfigurationTest {
assertEquals(firstEvents, secondEvents);
assertEquals(1, firstEvents.size());
}
+
+ @SetSystemProperty(key = "coreProps", value = "DEBUG, first, second")
+ @Test
+ @LoggerContextSource("LoggerLevelSysPropsAppenderTest.properties")
+ void testLoggerLevelSysPropsAppender(final LoggerContext context, @Named
final ListAppender first,
+ @Named final ListAppender second, @Named final ListAppender third)
{
+ context.getLogger(getClass()).atInfo().log("message");
+ context.getLogger(getClass()).atDebug().log("debug message");
+ context.getRootLogger().atInfo().log("test message");
+ final List<LogEvent> firstEvents = first.getEvents();
+ final List<LogEvent> secondEvents = second.getEvents();
+ assertEquals(firstEvents, secondEvents);
+ assertEquals(2, firstEvents.size());
+ final List<LogEvent> thirdEvents = third.getEvents();
+ assertEquals(1, thirdEvents.size());
+ }
}
diff --git a/log4j-core/src/test/resources/LoggerLevelAppenderTest.properties
b/log4j-core/src/test/resources/LoggerLevelAppenderTest.properties
index 231bb98..8ac9576 100644
--- a/log4j-core/src/test/resources/LoggerLevelAppenderTest.properties
+++ b/log4j-core/src/test/resources/LoggerLevelAppenderTest.properties
@@ -20,5 +20,7 @@ name = LoggerLevelAppenderTest
logger.core-config-properties.name =
org.apache.logging.log4j.core.config.properties
# whitespace added for testing trimming
logger.core-config-properties = INFO , first , second
+appender.first.name = first
appender.first.type = List
+appender.second.name = second
appender.second.type = List
diff --git a/log4j-core/src/test/resources/LoggerLevelAppenderTest.properties
b/log4j-core/src/test/resources/LoggerLevelSysPropsAppenderTest.properties
similarity index 80%
copy from log4j-core/src/test/resources/LoggerLevelAppenderTest.properties
copy to log4j-core/src/test/resources/LoggerLevelSysPropsAppenderTest.properties
index 231bb98..ffa5c35 100644
--- a/log4j-core/src/test/resources/LoggerLevelAppenderTest.properties
+++ b/log4j-core/src/test/resources/LoggerLevelSysPropsAppenderTest.properties
@@ -19,6 +19,12 @@ status = off
name = LoggerLevelAppenderTest
logger.core-config-properties.name =
org.apache.logging.log4j.core.config.properties
# whitespace added for testing trimming
-logger.core-config-properties = INFO , first , second
+logger.core-config-properties = ${sys:coreProps:-ERROR , first}
+logger.core-config-properties.additivity = false
+appender.first.name = first
appender.first.type = List
+appender.second.name = second
appender.second.type = List
+appender.third.name = third
+appender.third.type = List
+rootLogger = INFO, third
diff --git
a/log4j-core/src/test/resources/RootLoggerLevelAppenderTest.properties
b/log4j-core/src/test/resources/RootLoggerLevelAppenderTest.properties
index 3e08203..f753b9a 100644
--- a/log4j-core/src/test/resources/RootLoggerLevelAppenderTest.properties
+++ b/log4j-core/src/test/resources/RootLoggerLevelAppenderTest.properties
@@ -18,4 +18,5 @@
status = off
name = RootLoggerLevelAppenderTest
rootLogger = INFO,app
-appender.app.type = List
+appender.app.name = app
+appender.app.type = List
\ No newline at end of file
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 3cd72ec..580c6ea 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -230,8 +230,8 @@
<action type="add" dev="ggregory" due-to="Gary Gregory">
Add Configurator.setLevel(Logger, Level), setLevel(String, String),
and setLevel(Class, Level).
</action>
- <action type="add" dev="mattsicker" issue="LOG4J2-3341">
- Add shorthand syntax for properties configuration format for
specifying a logger level and appender refs and implicit appender names for
when no name is specified.
+ <action type="add" dev="rgoers" issue="LOG4J2-3341">
+ Add shorthand syntax for properties configuration format for
specifying a logger level and appender refs.
</action>
<action issue="LOG4J2-3391" type="add" dev="ggregory" due-to="Gary
Gregory">
Add optional additional fields to NoSQLAppender.
diff --git a/src/site/xdoc/manual/configuration.xml.vm
b/src/site/xdoc/manual/configuration.xml.vm
index fc84ee2..fa73922 100644
--- a/src/site/xdoc/manual/configuration.xml.vm
+++ b/src/site/xdoc/manual/configuration.xml.vm
@@ -940,20 +940,6 @@ Configuration:
definition in the rolling file appender below. You then define
each of the subcomponents below that
wrapper element, as the TimeBasedTriggeringPolicy and
SizeBasedTriggeringPolicy are defined below.
</p>
- <p>
- As of version 2.17.2,
- appenders can use the wrapper element as the implicit name of
the component.
- If no name is specified, the grouping element is used as the
name. For example, the following
- two properties snippets are equivalent.
- </p>
- <pre class="prettyprint linenums">
-appender.STDOUT.type = Console
- </pre>
- <pre class="prettyprint linenums">
-appender.console.type = Console
-appender.console.name = STDOUT
- </pre>
- <p>
As of version 2.17.2,
<tt>rootLogger</tt> and <tt>logger.<var>key</var></tt>
properties can be specified to set the
level and zero or more appender refs to create for that logger.
The level and appender refs are
@@ -968,9 +954,9 @@ appender.file.type = File
logger.app = INFO, stdout, file
logger.app.name = com.example.app
-# expanded to:
-appender.stdout.type = Console
-appender.stdout.name = stdout
+# is equivalent to:
+# appender.stdout.type = Console
+# appender.stdout.name = stdout
# ...
appender.file.type = File
appender.file.name = file
@@ -985,7 +971,7 @@ logger.app.appenderRef.$2.ref = file
shutdownTimeout, status, verbose, and dest attributes. See <a
href="#ConfigurationSyntax">Configuration Syntax</a>
for the definitions of these attributes.
</p>
- <pre class="prettyprint linenums">
+ <pre class="prettyprint linenums">
status = error
dest = err
name = PropertiesConfig