Add properties for logging charsets

Add `logging.charset.console` and `logging.charset.file` properties
that can be used to configure charsets for Logback/Log4J2.

Closes gh-23827
pull/23886/head
Phillip Webb 4 years ago
parent e790828e19
commit 062bd90d87

@ -2204,10 +2204,18 @@ To help with the customization, some other properties are transferred from the S
| `LOG_DATEFORMAT_PATTERN`
| Appender pattern for log date format.
| configprop:logging.charset.console[]
| `CONSOLE_LOG_CHARSET`
| The charset to use for console logging.
| configprop:logging.pattern.file[]
| `FILE_LOG_PATTERN`
| The log pattern to use in a file (if `LOG_FILE` is enabled).
| configprop:logging.charset.file[]
| `FILE_LOG_CHARSET`
| The charset to use for file logging (if `LOG_FILE` is enabled).
| configprop:logging.pattern.level[]
| `LOG_LEVEL_PATTERN`
| The format to use when rendering the log level (default `%5p`).

@ -16,6 +16,9 @@
package org.springframework.boot.logging;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.springframework.boot.system.ApplicationPid;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
@ -61,11 +64,21 @@ public class LoggingSystemProperties {
*/
public static final String CONSOLE_LOG_PATTERN = "CONSOLE_LOG_PATTERN";
/**
* The name of the System property that contains the console log charset.
*/
public static final String CONSOLE_LOG_CHARSET = "CONSOLE_LOG_CHARSET";
/**
* The name of the System property that contains the file log pattern.
*/
public static final String FILE_LOG_PATTERN = "FILE_LOG_PATTERN";
/**
* The name of the System property that contains the file log charset.
*/
public static final String FILE_LOG_CHARSET = "FILE_LOG_CHARSET";
/**
* The name of the System property that contains the rolled-over log file name
* pattern.
@ -128,6 +141,10 @@ public class LoggingSystemProperties {
this.environment = environment;
}
protected Charset getDefaultCharset() {
return StandardCharsets.UTF_8;
}
public final void apply() {
apply(null);
}
@ -141,8 +158,10 @@ public class LoggingSystemProperties {
setSystemProperty(resolver, EXCEPTION_CONVERSION_WORD, "logging.exception-conversion-word");
setSystemProperty(PID_KEY, new ApplicationPid().toString());
setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "logging.pattern.console");
setSystemProperty(resolver, CONSOLE_LOG_CHARSET, "logging.charset.console", getDefaultCharset().name());
setSystemProperty(resolver, LOG_DATEFORMAT_PATTERN, "logging.pattern.dateformat");
setSystemProperty(resolver, FILE_LOG_PATTERN, "logging.pattern.file");
setSystemProperty(resolver, FILE_LOG_CHARSET, "logging.charset.file", getDefaultCharset().name());
setSystemProperty(resolver, LOG_LEVEL_PATTERN, "logging.pattern.level");
applyDeprecated(resolver);
if (logFile != null) {
@ -170,7 +189,14 @@ public class LoggingSystemProperties {
}
protected final void setSystemProperty(PropertyResolver resolver, String systemPropertyName, String propertyName) {
setSystemProperty(systemPropertyName, resolver.getProperty(propertyName));
setSystemProperty(resolver, systemPropertyName, propertyName, null);
}
protected final void setSystemProperty(PropertyResolver resolver, String systemPropertyName, String propertyName,
String defaultValue) {
String value = resolver.getProperty(propertyName);
value = (value != null) ? value : defaultValue;
setSystemProperty(systemPropertyName, value);
}
protected final void setSystemProperty(String name, String value) {

@ -16,6 +16,8 @@
package org.springframework.boot.logging.logback;
import java.nio.charset.Charset;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
@ -87,6 +89,7 @@ class DefaultLogbackConfiguration {
ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>();
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setPattern(resolve(config, "${CONSOLE_LOG_PATTERN}"));
encoder.setCharset(resolveCharset(config, "${CONSOLE_LOG_CHARSET}"));
config.start(encoder);
appender.setEncoder(encoder);
config.appender("CONSOLE", appender);
@ -97,6 +100,7 @@ class DefaultLogbackConfiguration {
RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>();
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setPattern(resolve(config, "${FILE_LOG_PATTERN}"));
encoder.setCharset(resolveCharset(config, "${FILE_LOG_CHARSET}"));
appender.setEncoder(encoder);
config.start(encoder);
appender.setFile(logFile);
@ -133,6 +137,10 @@ class DefaultLogbackConfiguration {
return FileSize.valueOf(resolve(config, val));
}
private Charset resolveCharset(LogbackConfigurator config, String val) {
return Charset.forName(resolve(config, val));
}
private String resolve(LogbackConfigurator config, String val) {
return OptionHelper.substVars(val, config.getContext());
}

@ -16,6 +16,8 @@
package org.springframework.boot.logging.logback;
import java.nio.charset.Charset;
import ch.qos.logback.core.util.FileSize;
import org.springframework.boot.logging.LogFile;
@ -64,6 +66,11 @@ public class LogbackLoggingSystemProperties extends LoggingSystemProperties {
super(environment);
}
@Override
protected Charset getDefaultCharset() {
return Charset.defaultCharset();
}
@Override
protected void apply(LogFile logFile, PropertyResolver resolver) {
super.apply(logFile, resolver);

@ -19,6 +19,16 @@
"description": "Location of the logging configuration file. For instance, `classpath:logback.xml` for Logback.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
},
{
"name": "logging.charset.console",
"type": "java.nio.Charset",
"description": "The charset to use for console output"
},
{
"name": "logging.charset.file",
"type": "java.nio.Charset",
"description": "The charset to use for file output"
},
{
"name": "logging.exception-conversion-word",
"type": "java.lang.String",

@ -9,12 +9,10 @@
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" />
<PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" charset="${sys:CONSOLE_LOG_CHARSET}" />
</Console>
<RollingFile name="File" fileName="${sys:LOG_FILE}" filePattern="${sys:LOG_PATH}/$${date:yyyy-MM}/app-%d{yyyy-MM-dd-HH}-%i.log.gz">
<PatternLayout>
<Pattern>${sys:FILE_LOG_PATTERN}</Pattern>
</PatternLayout>
<PatternLayout pattern="${sys:FILE_LOG_PATTERN}" charset="${sys:FILE_LOG_CHARSET}"/>
<Policies>
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>

@ -9,7 +9,7 @@
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" />
<PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" charset="${sys:CONSOLE_LOG_CHARSET}"/>
</Console>
</Appenders>
<Loggers>

@ -9,6 +9,7 @@ initialization performed by Boot
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>${CONSOLE_LOG_CHARSET}</charset>
</encoder>
</appender>
</included>

@ -6,10 +6,10 @@ initialization performed by Boot
-->
<included>
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${FILE_LOG_CHARSET}</charset>
</encoder>
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">

@ -43,6 +43,8 @@ class LoggingSystemPropertiesTests {
@BeforeEach
void captureSystemPropertyNames() {
System.getProperties().remove(LoggingSystemProperties.CONSOLE_LOG_CHARSET);
System.getProperties().remove(LoggingSystemProperties.FILE_LOG_CHARSET);
this.systemPropertyNames = new HashSet<>(System.getProperties().keySet());
}
@ -64,6 +66,19 @@ class LoggingSystemPropertiesTests {
assertThat(System.getProperty(LoggingSystemProperties.CONSOLE_LOG_PATTERN)).isEqualTo("console pattern");
}
@Test
void consoleCharsetWhenNoPropertyUsesUtf8() {
new LoggingSystemProperties(new MockEnvironment()).apply(null);
assertThat(System.getProperty(LoggingSystemProperties.CONSOLE_LOG_CHARSET)).isEqualTo("UTF-8");
}
@Test
void consoleCharsetIsSet() {
new LoggingSystemProperties(new MockEnvironment().withProperty("logging.charset.console", "UTF-16"))
.apply(null);
assertThat(System.getProperty(LoggingSystemProperties.CONSOLE_LOG_CHARSET)).isEqualTo("UTF-16");
}
@Test
void fileLogPatternIsSet() {
new LoggingSystemProperties(new MockEnvironment().withProperty("logging.pattern.file", "file pattern"))
@ -71,6 +86,18 @@ class LoggingSystemPropertiesTests {
assertThat(System.getProperty(LoggingSystemProperties.FILE_LOG_PATTERN)).isEqualTo("file pattern");
}
@Test
void fileCharsetWhenNoPropertyUsesUtf8() {
new LoggingSystemProperties(new MockEnvironment()).apply(null);
assertThat(System.getProperty(LoggingSystemProperties.FILE_LOG_CHARSET)).isEqualTo("UTF-8");
}
@Test
void fileCharsetIsSet() {
new LoggingSystemProperties(new MockEnvironment().withProperty("logging.charset.file", "UTF-16")).apply(null);
assertThat(System.getProperty(LoggingSystemProperties.FILE_LOG_CHARSET)).isEqualTo("UTF-16");
}
@Test
void consoleLogPatternCanReferencePid() {
new LoggingSystemProperties(environment("logging.pattern.console", "${PID:unknown}")).apply(null);

@ -16,6 +16,7 @@
package org.springframework.boot.logging.logback;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Set;
@ -43,6 +44,8 @@ class LogbackLoggingSystemPropertiesTests {
@BeforeEach
void captureSystemPropertyNames() {
System.getProperties().remove(LoggingSystemProperties.CONSOLE_LOG_CHARSET);
System.getProperties().remove(LoggingSystemProperties.FILE_LOG_CHARSET);
this.systemPropertyNames = new HashSet<>(System.getProperties().keySet());
this.environment = new MockEnvironment();
this.environment
@ -94,4 +97,18 @@ class LogbackLoggingSystemPropertiesTests {
.containsEntry(LogbackLoggingSystemProperties.ROLLINGPOLICY_MAX_HISTORY, "mh");
}
@Test
void consoleCharsetWhenNoPropertyUsesDefault() {
new LoggingSystemProperties(new MockEnvironment()).apply(null);
assertThat(System.getProperty(LoggingSystemProperties.CONSOLE_LOG_CHARSET))
.isEqualTo(Charset.defaultCharset().name());
}
@Test
void fileCharsetWhenNoPropertyUsesDefault() {
new LoggingSystemProperties(new MockEnvironment()).apply(null);
assertThat(System.getProperty(LoggingSystemProperties.FILE_LOG_CHARSET))
.isEqualTo(Charset.defaultCharset().name());
}
}

@ -17,6 +17,7 @@
package org.springframework.boot.logging.logback;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
@ -30,6 +31,7 @@ import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import org.junit.jupiter.api.AfterEach;
@ -564,6 +566,18 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
assertThat(getRollingPolicy().getFileNamePattern()).isEqualTo(rollingFile);
}
@Test
void customCharset() {
this.environment.setProperty("logging.charset.console", "UTF-16");
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
initialize(loggingInitializationContext, null, logFile);
this.logger.info("Hello world");
LayoutWrappingEncoder<?> encoder = (LayoutWrappingEncoder<?>) getConsoleAppender().getEncoder();
assertThat(encoder.getCharset()).isEqualTo(StandardCharsets.UTF_16);
}
private void initialize(LoggingInitializationContext context, String configLocation, LogFile logFile) {
this.loggingSystem.getSystemProperties((ConfigurableEnvironment) context.getEnvironment()).apply(logFile);
this.loggingSystem.initialize(context, configLocation, logFile);

Loading…
Cancel
Save