You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2017/02/05 18:46:59 UTC
logging-log4j2 git commit: [LOG4J2-1809]: Add global configuration
environment SPI
Repository: logging-log4j2
Updated Branches:
refs/heads/LOG4J2-1431 bd587cf4e -> a9bcf176c
[LOG4J2-1809]: Add global configuration environment SPI
This adds a PropertySource service provider interface for providing
global configuration properties. This also refactors support in
LOG4J2-1431 to use these PropertySource providers.
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/a9bcf176
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/a9bcf176
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/a9bcf176
Branch: refs/heads/LOG4J2-1431
Commit: a9bcf176cc154fadae2a464c9743788809cf8ae6
Parents: bd587cf
Author: Matt Sicker <bo...@gmail.com>
Authored: Sun Feb 5 12:45:19 2017 -0600
Committer: Matt Sicker <bo...@gmail.com>
Committed: Sun Feb 5 12:45:19 2017 -0600
----------------------------------------------------------------------
.../log4j/util/EnvironmentPropertySource.java | 55 +++++++++
.../log4j/util/PropertiesPropertySource.java | 54 ++++++++
.../logging/log4j/util/PropertiesUtil.java | 123 ++++---------------
.../log4j/util/PropertyFilePropertySource.java | 52 ++++++++
.../logging/log4j/util/PropertySource.java | 115 +++++++++++++++++
.../util/SystemPropertiesPropertySource.java | 48 ++++++++
...org.apache.logging.log4j.util.PropertySource | 16 +++
7 files changed, 367 insertions(+), 96 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java
new file mode 100644
index 0000000..d413d61
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java
@@ -0,0 +1,55 @@
+/*
+ * 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.util;
+
+import java.util.Map;
+
+/**
+ * PropertySource implementation that uses environment variables as a source. All environment variables must begin
+ * with {@code LOG4J_} so as not to conflict with other variables. Normalized environment variables follow a scheme
+ * like this: {@code log4j2.fooBarProperty} would normalize to {@code LOG4J_FOO_BAR_PROPERTY}.
+ *
+ * @since 2.9
+ */
+public class EnvironmentPropertySource implements PropertySource {
+ @Override
+ public int getPriority() {
+ return -100;
+ }
+
+ @Override
+ public void forEach(final BiConsumer<String, String> action) {
+ for (final Map.Entry<String, String> entry : System.getenv().entrySet()) {
+ final String key = entry.getKey();
+ if (key.startsWith("LOG4J_")) {
+ action.accept(key.substring(6), entry.getValue());
+ }
+ }
+ }
+
+ @Override
+ public CharSequence getNormalForm(final Iterable<? extends CharSequence> tokens) {
+ final StringBuilder sb = new StringBuilder("LOG4J");
+ 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();
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java
new file mode 100644
index 0000000..7622509
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java
@@ -0,0 +1,54 @@
+/*
+ * 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.util;
+
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * PropertySource backed by a {@link Properties} instance. Normalized property names follow a scheme like this:
+ * {@code Log4jContextSelector} would normalize to {@code log4j2.contextSelector}.
+ *
+ * @since 2.9
+ */
+public class PropertiesPropertySource implements PropertySource {
+
+ private static final String PREFIX = "log4j2.";
+
+ private final Properties properties;
+
+ public PropertiesPropertySource(final Properties properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public int getPriority() {
+ return 0;
+ }
+
+ @Override
+ public void forEach(final BiConsumer<String, String> action) {
+ for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
+ action.accept(((String) entry.getKey()), ((String) entry.getValue()));
+ }
+ }
+
+ @Override
+ public CharSequence getNormalForm(final Iterable<? extends CharSequence> tokens) {
+ return PREFIX + Util.joinAsCamelCase(tokens);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/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 c57d1f9..1b26b38 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
@@ -18,15 +18,16 @@ package org.apache.logging.log4j.util;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* <em>Consider this class private.</em>
@@ -47,7 +48,7 @@ public final class PropertiesUtil {
* @param props the Properties to use by default
*/
public PropertiesUtil(final Properties props) {
- this.environment = new Environment(props);
+ this.environment = new Environment(new PropertiesPropertySource(props));
}
/**
@@ -57,15 +58,7 @@ public final class PropertiesUtil {
* @param propertiesFileName the location of properties file to load
*/
public PropertiesUtil(final String propertiesFileName) {
- final Properties properties = new Properties();
- for (final URL url : LoaderUtil.findResources(propertiesFileName)) {
- try (final InputStream in = url.openStream()) {
- properties.load(in);
- } catch (final IOException ioe) {
- LowLevelLogUtil.logException("Unable to read " + url.toString(), ioe);
- }
- }
- this.environment = new Environment(properties);
+ this.environment = new Environment(new PropertyFilePropertySource(propertiesFileName));
}
/**
@@ -256,93 +249,31 @@ public final class PropertiesUtil {
*/
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<CharSequence, String> literal = new ConcurrentHashMap<>();
+ private final Map<CharSequence, 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)));
- }
+ private Environment(final PropertySource propertySource) {
+ final Set<PropertySource> sources = new TreeSet<>(new PropertySource.Comparator());
+ sources.add(propertySource);
+ for (final PropertySource source : ServiceLoader.load(PropertySource.class)) {
+ sources.add(source);
}
- 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()));
+ for (final PropertySource source : sources) {
+ source.forEach(new BiConsumer<String, String>() {
+ @Override
+ public void accept(final String key, final String value) {
+ literal.put(key, value);
+ final List<CharSequence> tokens = PropertySource.Util.tokenize(key);
+ if (tokens.isEmpty()) {
+ normalized.put(source.getNormalForm(Collections.singleton(key)), value);
+ } else {
+ normalized.put(source.getNormalForm(tokens), value);
+ tokenized.put(tokens, value);
+ }
}
- }
- 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) {
@@ -361,7 +292,7 @@ public final class PropertiesUtil {
if (prop != null) {
return prop;
}
- return tokenized.get(tokenize(key));
+ return tokenized.get(PropertySource.Util.tokenize(key));
}
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java
new file mode 100644
index 0000000..2920460
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java
@@ -0,0 +1,52 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Properties;
+
+/**
+ * PropertySource backed by a properties file. Follows the same conventions as {@link PropertiesPropertySource}.
+ *
+ * @since 2.9
+ */
+public class PropertyFilePropertySource extends PropertiesPropertySource {
+
+ public PropertyFilePropertySource(final String fileName) {
+ super(loadPropertiesFile(fileName));
+ }
+
+ private static Properties loadPropertiesFile(final String fileName) {
+ final Properties props = new Properties();
+ for (final URL url : LoaderUtil.findResources(fileName)) {
+ try (final InputStream in = url.openStream()) {
+ props.load(in);
+ } catch (IOException e) {
+ LowLevelLogUtil.logException("Unable to read " + url, e);
+ }
+ }
+ return props;
+ }
+
+ @Override
+ public int getPriority() {
+ return 0;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java
new file mode 100644
index 0000000..4dd9cf3
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java
@@ -0,0 +1,115 @@
+package org.apache.logging.log4j.util;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A source for global configuration properties.
+ *
+ * @since 2.9
+ */
+public interface PropertySource {
+
+ /**
+ * Returns the order in which this PropertySource has priority. A higher value means that the source will be
+ * applied later so as to take precedence over other property sources.
+ *
+ * @return priority value
+ */
+ int getPriority();
+
+ /**
+ * Iterates over all properties and performs an action for each key/value pair.
+ *
+ * @param action action to perform on each key/value pair
+ */
+ void forEach(BiConsumer<String, String> action);
+
+ /**
+ * Converts a list of property name tokens into a normal form. For example, a list of tokens such as
+ * "foo", "bar", "baz", might be normalized into the property name "log4j2.fooBarBaz".
+ *
+ * @param tokens list of property name tokens
+ * @return a normalized property name using the given tokens
+ */
+ CharSequence getNormalForm(Iterable<? extends CharSequence> tokens);
+
+ /**
+ * Comparator for ordering PropertySource instances by priority.
+ *
+ * @since 2.9
+ */
+ class Comparator implements java.util.Comparator<PropertySource>, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public int compare(final PropertySource o1, final PropertySource o2) {
+ return Integer.compare(Objects.requireNonNull(o1).getPriority(), Objects.requireNonNull(o2).getPriority());
+ }
+ }
+
+ /**
+ * Utility methods useful for PropertySource implementations.
+ *
+ * @since 2.9
+ */
+ final class Util {
+ 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 static final Map<CharSequence, List<CharSequence>> CACHE = new ConcurrentHashMap<>();
+
+ /**
+ * Converts a property name string into a list of tokens. This will strip a prefix of {@code log4j},
+ * {@code log4j2}, {@code Log4j}, or {@code org.apache.logging.log4j}, along with separators of
+ * dash {@code -}, dot {@code .}, underscore {@code _}, and slash {@code /}. Tokens can also be separated
+ * by camel case conventions without needing a separator character in between.
+ *
+ * @param value property name
+ * @return the property broken into lower case tokens
+ */
+ public static List<CharSequence> tokenize(final CharSequence value) {
+ if (CACHE.containsKey(value)) {
+ return CACHE.get(value);
+ }
+ List<CharSequence> tokens = new ArrayList<>();
+ final Matcher matcher = PROPERTY_TOKENIZER.matcher(value);
+ while (matcher.find()) {
+ tokens.add(matcher.group(1).toLowerCase());
+ }
+ CACHE.put(value, tokens);
+ return tokens;
+ }
+
+ /**
+ * Joins a list of strings using camelCaseConventions.
+ *
+ * @param tokens tokens to convert
+ * @return tokensAsCamelCase
+ */
+ public static CharSequence joinAsCamelCase(final Iterable<? extends 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 Util() {
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/java/org/apache/logging/log4j/util/SystemPropertiesPropertySource.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/SystemPropertiesPropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/SystemPropertiesPropertySource.java
new file mode 100644
index 0000000..8733f23
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/SystemPropertiesPropertySource.java
@@ -0,0 +1,48 @@
+/*
+ * 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.util;
+
+import java.util.Map;
+
+/**
+ * PropertySource backed by the current system properties. Other than having a higher priority over normal properties,
+ * this follows the same rules as {@link PropertiesPropertySource}.
+ *
+ * @since 2.9
+ */
+public class SystemPropertiesPropertySource implements PropertySource {
+
+ private static final String PREFIX = "log4j2.";
+
+ @Override
+ public int getPriority() {
+ return 100;
+ }
+
+ @Override
+ public void forEach(final BiConsumer<String, String> action) {
+ for (final Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
+ action.accept(((String) entry.getKey()), ((String) entry.getValue()));
+ }
+ }
+
+ @Override
+ public CharSequence getNormalForm(final Iterable<? extends CharSequence> tokens) {
+ return PREFIX + Util.joinAsCamelCase(tokens);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource b/log4j-api/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
new file mode 100644
index 0000000..39c959c
--- /dev/null
+++ b/log4j-api/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
@@ -0,0 +1,16 @@
+# 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.
+org.apache.logging.log4j.util.EnvironmentPropertySource
+org.apache.logging.log4j.util.SystemPropertiesPropertySource
\ No newline at end of file