Repository: logging-log4j2 Updated Branches: refs/heads/LOG4J2-1431 [created] bd587cf4e
[LOG4J2-1431]: Provide support for simplified global configuration environment This provides the base support for implementing simplified property names along with environment variable support. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/bd587cf4 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/bd587cf4 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/bd587cf4 Branch: refs/heads/LOG4J2-1431 Commit: bd587cf4e514a62ecd3852e6f1182feeed049fdc Parents: 8cf9e4f Author: Matt Sicker <[email protected]> Authored: Sat Feb 4 03:12:58 2017 -0600 Committer: Matt Sicker <[email protected]> Committed: Sat Feb 4 03:12:58 2017 -0600 ---------------------------------------------------------------------- .../logging/log4j/util/PropertiesUtil.java | 138 +++++++++++++++++-- 1 file changed, 128 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bd587cf4/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java ---------------------------------------------------------------------- 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 16b9ef6..c57d1f9 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 @@ -25,6 +25,8 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * <em>Consider this class private.</em> @@ -37,7 +39,7 @@ public final class PropertiesUtil { private static final PropertiesUtil LOG4J_PROPERTIES = new PropertiesUtil("log4j2.component.properties"); - private final Properties props; + private final Environment environment; /** * Constructs a PropertiesUtil using a given Properties object as its source of defined properties. @@ -45,7 +47,7 @@ public final class PropertiesUtil { * @param props the Properties to use by default */ public PropertiesUtil(final Properties props) { - this.props = props; + this.environment = new Environment(props); } /** @@ -63,7 +65,7 @@ public final class PropertiesUtil { LowLevelLogUtil.logException("Unable to read " + url.toString(), ioe); } } - this.props = properties; + this.environment = new Environment(properties); } /** @@ -211,13 +213,7 @@ public final class PropertiesUtil { * @return the String value of the property or {@code null} if undefined. */ public String getStringProperty(final String name) { - String prop = null; - try { - prop = System.getProperty(name); - } catch (final SecurityException ignored) { - // Ignore - } - return prop == null ? props.getProperty(name) : prop; + return environment.get(name); } /** @@ -248,6 +244,128 @@ public final class PropertiesUtil { } /** + * Provides support for looking up global configuration properties via environment variables, property files, + * and system properties, in three variations: + * + * Normalized: all log4j-related prefixes removed, remaining property is camelCased with a log4j2 prefix for + * property files and system properties, or follows a LOG4J_FOO_BAR format for environment variables. + * + * Legacy: the original property name as defined in the source pre-2.9. + * + * Tokenized: loose matching based on word boundaries. + */ + private static class Environment { + + private static final String NORMALIZED_PREFIX = "log4j2."; + private static final String PREFIXES = "(?:[Ll]og4j2?|org\\.apache\\.logging\\.log4j\\.)?"; + private static final Pattern PROPERTY_TOKENIZER = Pattern.compile(PREFIXES + "([A-Z]*[a-z0-9]+)[-._/]?"); + + private final Map<String, String> literal = new ConcurrentHashMap<>(); + private final Map<String, String> normalized = new ConcurrentHashMap<>(); + private final Map<List<CharSequence>, String> tokenized = new ConcurrentHashMap<>(); + + private Environment(final Properties props) { + for (final Map.Entry<String, String> entry : System.getenv().entrySet()) { + final String envKey = entry.getKey(); + final String value = entry.getValue(); + if (envKey.startsWith("LOG4J_")) { + final String key = envKey.substring(6); + literal.put(key, value); + final List<CharSequence> tokens = tokenize(envKey); + if (!tokens.isEmpty()) { + tokenized.put(tokens, value); + normalized.put("LOG4J" + joinAsAllCapsUnderscored(tokens), value); + } + } + } + for (final Map.Entry<Object, Object> entry : props.entrySet()) { + final String key = (String) entry.getKey(); + final String value = (String) entry.getValue(); + literal.put(key, value); + final List<CharSequence> tokens = tokenize(key); + if (tokens.isEmpty()) { + normalized.put(NORMALIZED_PREFIX + key, value); + } else { + tokenized.put(tokens, value); + normalized.put(NORMALIZED_PREFIX + joinAsCamelCase(tokens), value); + } + + } + final Properties systemProperties = getSystemProperties(); + for (Map.Entry<Object, Object> entry : systemProperties.entrySet()) { + final String key = (String) entry.getKey(); + final String value = (String) entry.getValue(); + literal.put(key, value); + // TODO: should this include tokenized lookups? could get screwy + final List<CharSequence> tokens = tokenize(key); + if (tokens.isEmpty()) { + normalized.put(NORMALIZED_PREFIX + key, value); + } else { + tokenized.put(tokens, value); + normalized.put(NORMALIZED_PREFIX + joinAsCamelCase(tokens), value); + } + } + } + + private static CharSequence joinAsAllCapsUnderscored(final List<CharSequence> tokens) { + final StringBuilder sb = new StringBuilder(); + for (final CharSequence token : tokens) { + sb.append('_'); + for (int i = 0; i < token.length(); i++) { + sb.append(Character.toUpperCase(token.charAt(i))); + } + } + return sb.toString(); + } + + private static CharSequence joinAsCamelCase(final List<CharSequence> tokens) { + final StringBuilder sb = new StringBuilder(); + boolean first = true; + for (final CharSequence token : tokens) { + if (first) { + sb.append(token); + } else { + sb.append(Character.toUpperCase(token.charAt(0))); + if (token.length() > 1) { + sb.append(token.subSequence(1, token.length())); + } + } + first = false; + } + return sb.toString(); + } + + private static List<CharSequence> tokenize(final CharSequence value) { + // TODO: cache? + List<CharSequence> tokens = new ArrayList<>(); + final Matcher matcher = PROPERTY_TOKENIZER.matcher(value); + while (matcher.find()) { + tokens.add(matcher.group(1).toLowerCase()); + } + return tokens; + } + + private String get(final String key) { + if (normalized.containsKey(key)) { + return normalized.get(key); + } + if (literal.containsKey(key)) { + return literal.get(key); + } + String prop = null; + try { + prop = System.getProperty(key); + } catch (final SecurityException ignored) { + // Ignore + } + if (prop != null) { + return prop; + } + return tokenized.get(tokenize(key)); + } + } + + /** * Extracts properties that start with or are equals to the specific prefix and returns them in a new Properties * object with the prefix removed. *
