[LOG4J2-1636] Console Appender does not pick up Oracle Java 8's sun.stdout.encoding and sun.stderr.encoding. This is a partial fix. See discussion in Jira.
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/d65bf73c Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/d65bf73c Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/d65bf73c Branch: refs/heads/LOG4J2-1390 Commit: d65bf73c02adbf7e82bd2dc66649bad512b944f2 Parents: eed7c07 Author: Gary Gregory <ggreg...@apache.org> Authored: Mon Oct 10 11:20:32 2016 -0700 Committer: Gary Gregory <ggreg...@apache.org> Committed: Mon Oct 10 11:20:32 2016 -0700 ---------------------------------------------------------------------- .../logging/log4j/util/PropertiesUtil.java | 597 +- .../log4j/core/appender/AbstractAppender.java | 8 + .../log4j/core/appender/ConsoleAppender.java | 23 +- .../appender/ConsoleAppenderBuilderTest.java | 80 +- src/changes/changes.xml | 6175 +++++++++--------- 5 files changed, 3477 insertions(+), 3406 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d65bf73c/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 1bc4e43..af0b29e 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 @@ -1,287 +1,310 @@ -/* - * 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.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ConcurrentHashMap; - -/** - * <em>Consider this class private.</em> - * <p> - * Helps access properties. This utility provides a method to override system properties by specifying properties in a - * properties file. - * </p> - */ -public final class PropertiesUtil { - - private static final PropertiesUtil LOG4J_PROPERTIES = new PropertiesUtil("log4j2.component.properties"); - - private final Properties props; - - /** - * Constructs a PropertiesUtil using a given Properties object as its source of defined properties. - * - * @param props the Properties to use by default - */ - public PropertiesUtil(final Properties props) { - this.props = props; - } - - /** - * Constructs a PropertiesUtil for a given properties file name on the classpath. The properties specified in this - * file are used by default. If a property is not defined in this file, then the equivalent system property is used. - * - * @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.props = properties; - } - - /** - * Loads and closes the given property input stream. If an error occurs, log to the status logger. - * - * @param in a property input stream. - * @param source a source object describing the source, like a resource string or a URL. - * @return a new Properties object - */ - static Properties loadClose(final InputStream in, final Object source) { - final Properties props = new Properties(); - if (null != in) { - try { - props.load(in); - } catch (final IOException e) { - LowLevelLogUtil.logException("Unable to read " + source, e); - } finally { - try { - in.close(); - } catch (final IOException e) { - LowLevelLogUtil.logException("Unable to close " + source, e); - } - } - } - return props; - } - - /** - * Returns the PropertiesUtil used by Log4j. - * - * @return the main Log4j PropertiesUtil instance. - */ - public static PropertiesUtil getProperties() { - return LOG4J_PROPERTIES; - } - - /** - * Gets the named property as a boolean value. If the property matches the string {@code "true"} (case-insensitive), - * then it is returned as the boolean value {@code true}. Any other non-{@code null} text in the property is - * considered {@code false}. - * - * @param name the name of the property to look up - * @return the boolean value of the property or {@code false} if undefined. - */ - public boolean getBooleanProperty(final String name) { - return getBooleanProperty(name, false); - } - - /** - * Gets the named property as a boolean value. - * - * @param name the name of the property to look up - * @param defaultValue the default value to use if the property is undefined - * @return the boolean value of the property or {@code defaultValue} if undefined. - */ - public boolean getBooleanProperty(final String name, final boolean defaultValue) { - final String prop = getStringProperty(name); - return (prop == null) ? defaultValue : "true".equalsIgnoreCase(prop); - } - - /** - * Gets the named property as a double. - * - * @param name the name of the property to look up - * @param defaultValue the default value to use if the property is undefined - * @return the parsed double value of the property or {@code defaultValue} if it was undefined or could not be parsed. - */ - public double getDoubleProperty(final String name, final double defaultValue) { - final String prop = getStringProperty(name); - if (prop != null) { - try { - return Double.parseDouble(prop); - } catch (final Exception ignored) { - return defaultValue; - } - } - return defaultValue; - } - - /** - * Gets the named property as an integer. - * - * @param name the name of the property to look up - * @param defaultValue the default value to use if the property is undefined - * @return the parsed integer value of the property or {@code defaultValue} if it was undefined or could not be - * parsed. - */ - public int getIntegerProperty(final String name, final int defaultValue) { - final String prop = getStringProperty(name); - if (prop != null) { - try { - return Integer.parseInt(prop); - } catch (final Exception ignored) { - return defaultValue; - } - } - return defaultValue; - } - - /** - * Gets the named property as a long. - * - * @param name the name of the property to look up - * @param defaultValue the default value to use if the property is undefined - * @return the parsed long value of the property or {@code defaultValue} if it was undefined or could not be parsed. - */ - public long getLongProperty(final String name, final long defaultValue) { - final String prop = getStringProperty(name); - if (prop != null) { - try { - return Long.parseLong(prop); - } catch (final Exception ignored) { - return defaultValue; - } - } - return defaultValue; - } - - /** - * Gets the named property as a String. - * - * @param name the name of the property to look up - * @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; - } - - /** - * Gets the named property as a String. - * - * @param name the name of the property to look up - * @param defaultValue the default value to use if the property is undefined - * @return the String value of the property or {@code defaultValue} if undefined. - */ - public String getStringProperty(final String name, final String defaultValue) { - final String prop = getStringProperty(name); - return (prop == null) ? defaultValue : prop; - } - - /** - * Return the system properties or an empty Properties object if an error occurs. - * - * @return The system properties. - */ - public static Properties getSystemProperties() { - try { - return new Properties(System.getProperties()); - } catch (final SecurityException ex) { - LowLevelLogUtil.logException("Unable to access system properties.", ex); - // Sandboxed - can't read System Properties - return new Properties(); - } - } - - /** - * Extracts properties that start with or are equals to the specific prefix and returns them in a new Properties - * object with the prefix removed. - * - * @param properties The Properties to evaluate. - * @param prefix The prefix to extract. - * @return The subset of properties. - */ - public static Properties extractSubset(final Properties properties, final String prefix) { - final Properties subset = new Properties(); - - if (prefix == null || prefix.length() == 0) { - return subset; - } - - final String prefixToMatch = prefix.charAt(prefix.length() - 1) != '.' ? prefix + '.' : prefix; - - final List<String> keys = new ArrayList<>(); - - for (final String key : properties.stringPropertyNames()) { - if (key.startsWith(prefixToMatch)) { - subset.setProperty(key.substring(prefixToMatch.length()), properties.getProperty(key)); - keys.add(key); - } - } - for (final String key : keys) { - properties.remove(key); - } - - return subset; - } - - /** - * Partitions a properties map based on common key prefixes up to the first period. - * - * @param properties properties to partition - * @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.6 - */ - public static Map<String, Properties> partitionOnCommonPrefixes(final Properties properties) { - final Map<String, Properties> parts = new ConcurrentHashMap<>(); - for (final String key : properties.stringPropertyNames()) { - final String prefix = key.substring(0, key.indexOf('.')); - if (!parts.containsKey(prefix)) { - parts.put(prefix, new Properties()); - } - parts.get(prefix).setProperty(key.substring(key.indexOf('.') + 1), properties.getProperty(key)); - } - return parts; - } - - /** - * Returns true if system properties tell us we are running on Windows. - * @return true if system properties tell us we are running on Windows. - */ - public boolean isOsWindows() { - return getStringProperty("os.name").startsWith("Windows"); - } - -} +/* + * 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.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +/** + * <em>Consider this class private.</em> + * <p> + * Helps access properties. This utility provides a method to override system properties by specifying properties in a + * properties file. + * </p> + */ +public final class PropertiesUtil { + + private static final PropertiesUtil LOG4J_PROPERTIES = new PropertiesUtil("log4j2.component.properties"); + + private final Properties props; + + /** + * Constructs a PropertiesUtil using a given Properties object as its source of defined properties. + * + * @param props the Properties to use by default + */ + public PropertiesUtil(final Properties props) { + this.props = props; + } + + /** + * Constructs a PropertiesUtil for a given properties file name on the classpath. The properties specified in this + * file are used by default. If a property is not defined in this file, then the equivalent system property is used. + * + * @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.props = properties; + } + + /** + * Loads and closes the given property input stream. If an error occurs, log to the status logger. + * + * @param in a property input stream. + * @param source a source object describing the source, like a resource string or a URL. + * @return a new Properties object + */ + static Properties loadClose(final InputStream in, final Object source) { + final Properties props = new Properties(); + if (null != in) { + try { + props.load(in); + } catch (final IOException e) { + LowLevelLogUtil.logException("Unable to read " + source, e); + } finally { + try { + in.close(); + } catch (final IOException e) { + LowLevelLogUtil.logException("Unable to close " + source, e); + } + } + } + return props; + } + + /** + * Returns the PropertiesUtil used by Log4j. + * + * @return the main Log4j PropertiesUtil instance. + */ + public static PropertiesUtil getProperties() { + return LOG4J_PROPERTIES; + } + + /** + * Gets the named property as a boolean value. If the property matches the string {@code "true"} (case-insensitive), + * then it is returned as the boolean value {@code true}. Any other non-{@code null} text in the property is + * considered {@code false}. + * + * @param name the name of the property to look up + * @return the boolean value of the property or {@code false} if undefined. + */ + public boolean getBooleanProperty(final String name) { + return getBooleanProperty(name, false); + } + + /** + * Gets the named property as a boolean value. + * + * @param name the name of the property to look up + * @param defaultValue the default value to use if the property is undefined + * @return the boolean value of the property or {@code defaultValue} if undefined. + */ + public boolean getBooleanProperty(final String name, final boolean defaultValue) { + final String prop = getStringProperty(name); + return (prop == null) ? defaultValue : "true".equalsIgnoreCase(prop); + } + + /** + * Gets the named property as a Charset value. + * + * @param name the name of the property to look up + * @return the Charset value of the property or {@link Charset#defaultCharset()} if undefined. + */ + public Charset getCharsetProperty(final String name) { + return getCharsetProperty(name, Charset.defaultCharset()); + } + + /** + * Gets the named property as a Charset value. + * + * @param name the name of the property to look up + * @param defaultValue the default value to use if the property is undefined + * @return the Charset value of the property or {@code defaultValue} if undefined. + */ + public Charset getCharsetProperty(String name, Charset defaultValue) { + final String prop = getStringProperty(name); + return prop == null ? defaultValue : Charset.forName(name); + } + + /** + * Gets the named property as a double. + * + * @param name the name of the property to look up + * @param defaultValue the default value to use if the property is undefined + * @return the parsed double value of the property or {@code defaultValue} if it was undefined or could not be parsed. + */ + public double getDoubleProperty(final String name, final double defaultValue) { + final String prop = getStringProperty(name); + if (prop != null) { + try { + return Double.parseDouble(prop); + } catch (final Exception ignored) { + return defaultValue; + } + } + return defaultValue; + } + + /** + * Gets the named property as an integer. + * + * @param name the name of the property to look up + * @param defaultValue the default value to use if the property is undefined + * @return the parsed integer value of the property or {@code defaultValue} if it was undefined or could not be + * parsed. + */ + public int getIntegerProperty(final String name, final int defaultValue) { + final String prop = getStringProperty(name); + if (prop != null) { + try { + return Integer.parseInt(prop); + } catch (final Exception ignored) { + return defaultValue; + } + } + return defaultValue; + } + + /** + * Gets the named property as a long. + * + * @param name the name of the property to look up + * @param defaultValue the default value to use if the property is undefined + * @return the parsed long value of the property or {@code defaultValue} if it was undefined or could not be parsed. + */ + public long getLongProperty(final String name, final long defaultValue) { + final String prop = getStringProperty(name); + if (prop != null) { + try { + return Long.parseLong(prop); + } catch (final Exception ignored) { + return defaultValue; + } + } + return defaultValue; + } + + /** + * Gets the named property as a String. + * + * @param name the name of the property to look up + * @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; + } + + /** + * Gets the named property as a String. + * + * @param name the name of the property to look up + * @param defaultValue the default value to use if the property is undefined + * @return the String value of the property or {@code defaultValue} if undefined. + */ + public String getStringProperty(final String name, final String defaultValue) { + final String prop = getStringProperty(name); + return (prop == null) ? defaultValue : prop; + } + + /** + * Return the system properties or an empty Properties object if an error occurs. + * + * @return The system properties. + */ + public static Properties getSystemProperties() { + try { + return new Properties(System.getProperties()); + } catch (final SecurityException ex) { + LowLevelLogUtil.logException("Unable to access system properties.", ex); + // Sandboxed - can't read System Properties + return new Properties(); + } + } + + /** + * Extracts properties that start with or are equals to the specific prefix and returns them in a new Properties + * object with the prefix removed. + * + * @param properties The Properties to evaluate. + * @param prefix The prefix to extract. + * @return The subset of properties. + */ + public static Properties extractSubset(final Properties properties, final String prefix) { + final Properties subset = new Properties(); + + if (prefix == null || prefix.length() == 0) { + return subset; + } + + final String prefixToMatch = prefix.charAt(prefix.length() - 1) != '.' ? prefix + '.' : prefix; + + final List<String> keys = new ArrayList<>(); + + for (final String key : properties.stringPropertyNames()) { + if (key.startsWith(prefixToMatch)) { + subset.setProperty(key.substring(prefixToMatch.length()), properties.getProperty(key)); + keys.add(key); + } + } + for (final String key : keys) { + properties.remove(key); + } + + return subset; + } + + /** + * Partitions a properties map based on common key prefixes up to the first period. + * + * @param properties properties to partition + * @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.6 + */ + public static Map<String, Properties> partitionOnCommonPrefixes(final Properties properties) { + final Map<String, Properties> parts = new ConcurrentHashMap<>(); + for (final String key : properties.stringPropertyNames()) { + final String prefix = key.substring(0, key.indexOf('.')); + if (!parts.containsKey(prefix)) { + parts.put(prefix, new Properties()); + } + parts.get(prefix).setProperty(key.substring(key.indexOf('.') + 1), properties.getProperty(key)); + } + return parts; + } + + /** + * Returns true if system properties tell us we are running on Windows. + * @return true if system properties tell us we are running on Windows. + */ + public boolean isOsWindows() { + return getStringProperty("os.name").startsWith("Windows"); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d65bf73c/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractAppender.java index 71af242..94f2fec 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractAppender.java @@ -17,6 +17,7 @@ package org.apache.logging.log4j.core.appender; import java.io.Serializable; +import java.nio.charset.Charset; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.ErrorHandler; @@ -87,6 +88,13 @@ public abstract class AbstractAppender extends AbstractFilterable implements App return layout; } + public Layout<? extends Serializable> getOrCreateLayout(Charset charset) { + if (layout == null) { + return PatternLayout.newBuilder().withCharset(charset).build(); + } + return layout; + } + } private final String name; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d65bf73c/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java index fec5997..b865caf 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java @@ -64,9 +64,26 @@ public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputSt */ public enum Target { /** Standard output. */ - SYSTEM_OUT, + SYSTEM_OUT { + @Override + public Charset getDefaultCharset() { + return getCharset("sun.stdout.encoding"); + } + }, /** Standard error output. */ - SYSTEM_ERR + SYSTEM_ERR { + @Override + public Charset getDefaultCharset() { + return getCharset("sun.stderr.encoding"); + } + }; + + public abstract Charset getDefaultCharset(); + + protected Charset getCharset(String property) { + return new PropertiesUtil(PropertiesUtil.getSystemProperties()).getCharsetProperty(property); + } + } private ConsoleAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter, @@ -197,7 +214,7 @@ public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputSt if (follow && direct) { throw new IllegalArgumentException("Cannot use both follow and direct on ConsoleAppender '" + getName() + "'"); } - final Layout<? extends Serializable> layout = getOrCreateLayout(); + final Layout<? extends Serializable> layout = getOrCreateLayout(target.getDefaultCharset()); return new ConsoleAppender(getName(), layout, getFilter(), getManager(target, follow, direct, layout), isIgnoreExceptions(), target); } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d65bf73c/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderBuilderTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderBuilderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderBuilderTest.java index 2531a31..157b8c0 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderBuilderTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderBuilderTest.java @@ -1,31 +1,49 @@ -/* - * 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.appender; - -import org.junit.Assert; -import org.junit.Test; - -public class ConsoleAppenderBuilderTest { - - /** - * Tests https://issues.apache.org/jira/browse/LOG4J2-1620 - */ - @Test - public void testDefaultImmediateFlush() { - Assert.assertTrue(ConsoleAppender.newBuilder().isImmediateFlush()); - } -} +/* + * 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.appender; + +import java.nio.charset.Charset; + +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.junit.Assert; +import org.junit.Test; + +public class ConsoleAppenderBuilderTest { + + /** + * Tests https://issues.apache.org/jira/browse/LOG4J2-1620 + */ + @Test + public void testDefaultImmediateFlush() { + Assert.assertTrue(ConsoleAppender.newBuilder().isImmediateFlush()); + } + + /** + * Tests https://issues.apache.org/jira/browse/LOG4J2-1636 + * + * Tested with Oracle 7 and 8 and IBM Java 8. + */ + @Test + public void testDefaultLayoutDefaultCharset() { + final ConsoleAppender appender = ConsoleAppender.newBuilder().build(); + final PatternLayout layout = (PatternLayout) appender.getLayout(); + final String charsetName = System.getProperty("sun.stdout.encoding"); + final String expectedName = charsetName != null ? charsetName : Charset.defaultCharset().name(); + Assert.assertEquals(expectedName, layout.getCharset().name()); + } + +}