Browse Source

Switch Logback's file logging charset to UTF-8

This commit aligns the file charset between Log4j2 and Logback.

Prior to this commit, there was an inconsistency between the two: Log4j2
used UTF-8, while Logback used Charset.defaultCharset(), which is
platform-dependent.

See gh-46846

Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
pull/46890/head
Dmytro Nosan 4 months ago committed by Stéphane Nicoll
parent
commit
26372e78c6
  1. 15
      core/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java
  2. 6
      core/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystemProperties.java
  3. 68
      core/spring-boot/src/test/java/org/springframework/boot/logging/logback/DefaultLogbackConfigurationTests.java
  4. 4
      core/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemPropertiesTests.java

15
core/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java

@ -16,7 +16,9 @@
package org.springframework.boot.logging.logback; package org.springframework.boot.logging.logback;
import java.io.Console;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
@ -55,7 +57,7 @@ import org.springframework.util.StringUtils;
*/ */
class DefaultLogbackConfiguration { class DefaultLogbackConfiguration {
private static final String DEFAULT_CHARSET = Charset.defaultCharset().name(); private static final String DEFAULT_CHARSET = StandardCharsets.UTF_8.name();
private static final String NAME_AND_GROUP = "%esb(){APPLICATION_NAME}%esb{APPLICATION_GROUP}"; private static final String NAME_AND_GROUP = "%esb(){APPLICATION_NAME}%esb{APPLICATION_GROUP}";
@ -106,7 +108,7 @@ class DefaultLogbackConfiguration {
config.conversionRule("wEx", ExtendedWhitespaceThrowableProxyConverter.class, config.conversionRule("wEx", ExtendedWhitespaceThrowableProxyConverter.class,
ExtendedWhitespaceThrowableProxyConverter::new); ExtendedWhitespaceThrowableProxyConverter::new);
putProperty(config, "CONSOLE_LOG_PATTERN", CONSOLE_LOG_PATTERN); putProperty(config, "CONSOLE_LOG_PATTERN", CONSOLE_LOG_PATTERN);
putProperty(config, "CONSOLE_LOG_CHARSET", "${CONSOLE_LOG_CHARSET:-" + DEFAULT_CHARSET + "}"); putProperty(config, "CONSOLE_LOG_CHARSET", "${CONSOLE_LOG_CHARSET:-" + getConsoleCharset() + "}");
putProperty(config, "CONSOLE_LOG_THRESHOLD", "${CONSOLE_LOG_THRESHOLD:-TRACE}"); putProperty(config, "CONSOLE_LOG_THRESHOLD", "${CONSOLE_LOG_THRESHOLD:-TRACE}");
putProperty(config, "CONSOLE_LOG_STRUCTURED_FORMAT", "${CONSOLE_LOG_STRUCTURED_FORMAT:-}"); putProperty(config, "CONSOLE_LOG_STRUCTURED_FORMAT", "${CONSOLE_LOG_STRUCTURED_FORMAT:-}");
putProperty(config, "FILE_LOG_PATTERN", FILE_LOG_PATTERN); putProperty(config, "FILE_LOG_PATTERN", FILE_LOG_PATTERN);
@ -123,6 +125,15 @@ class DefaultLogbackConfiguration {
config.logger("org.springframework.boot.actuate.endpoint.jmx", Level.WARN); config.logger("org.springframework.boot.actuate.endpoint.jmx", Level.WARN);
} }
private String getConsoleCharset() {
Console console = getConsole();
return (console != null) ? console.charset().name() : DEFAULT_CHARSET;
}
@Nullable Console getConsole() {
return System.console();
}
void putProperty(LogbackConfigurator config, String name, String val) { void putProperty(LogbackConfigurator config, String name, String val) {
config.getContext().putProperty(name, resolve(config, val)); config.getContext().putProperty(name, resolve(config, val));
} }

6
core/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystemProperties.java

@ -17,7 +17,6 @@
package org.springframework.boot.logging.logback; package org.springframework.boot.logging.logback;
import java.io.Console; import java.io.Console;
import java.nio.charset.Charset;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
@ -78,11 +77,6 @@ public class LogbackLoggingSystemProperties extends LoggingSystemProperties {
return super.getConsole(); return super.getConsole();
} }
@Override
protected Charset getDefaultFileCharset() {
return Charset.defaultCharset();
}
@Override @Override
protected void apply(@Nullable LogFile logFile, PropertyResolver resolver) { protected void apply(@Nullable LogFile logFile, PropertyResolver resolver) {
super.apply(logFile, resolver); super.apply(logFile, resolver);

68
core/spring-boot/src/test/java/org/springframework/boot/logging/logback/DefaultLogbackConfigurationTests.java

@ -16,14 +16,19 @@
package org.springframework.boot.logging.logback; package org.springframework.boot.logging.logback;
import java.io.Console;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import ch.qos.logback.classic.LoggerContext;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.util.StreamUtils; import org.springframework.util.StreamUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
/** /**
* Tests for {@link DefaultLogbackConfiguration} * Tests for {@link DefaultLogbackConfiguration}
@ -32,6 +37,10 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
class DefaultLogbackConfigurationTests { class DefaultLogbackConfigurationTests {
private final LoggerContext loggerContext = new LoggerContext();
private final LogbackConfigurator logbackConfigurator = new LogbackConfigurator(this.loggerContext);
@Test @Test
void defaultLogbackXmlContainsConsoleLogPattern() throws Exception { void defaultLogbackXmlContainsConsoleLogPattern() throws Exception {
assertThatDefaultXmlContains("CONSOLE_LOG_PATTERN", DefaultLogbackConfiguration.CONSOLE_LOG_PATTERN); assertThatDefaultXmlContains("CONSOLE_LOG_PATTERN", DefaultLogbackConfiguration.CONSOLE_LOG_PATTERN);
@ -42,6 +51,48 @@ class DefaultLogbackConfigurationTests {
assertThatDefaultXmlContains("FILE_LOG_PATTERN", DefaultLogbackConfiguration.FILE_LOG_PATTERN); assertThatDefaultXmlContains("FILE_LOG_PATTERN", DefaultLogbackConfiguration.FILE_LOG_PATTERN);
} }
@Test
void consoleLogCharsetShouldUseConsoleCharsetIfConsoleAvailable() {
DefaultLogbackConfiguration logbackConfiguration = spy(new DefaultLogbackConfiguration(null));
Console console = mock(Console.class);
given(console.charset()).willReturn(StandardCharsets.UTF_16);
given(logbackConfiguration.getConsole()).willReturn(console);
logbackConfiguration.apply(this.logbackConfigurator);
assertThat(this.loggerContext.getProperty("CONSOLE_LOG_CHARSET")).isEqualTo(StandardCharsets.UTF_16.name());
}
@Test
void consoleLogCharsetShouldDefaultToUtf8WhenConsoleIsNull() {
DefaultLogbackConfiguration logbackConfiguration = spy(new DefaultLogbackConfiguration(null));
given(logbackConfiguration.getConsole()).willReturn(null);
logbackConfiguration.apply(this.logbackConfigurator);
assertThat(this.loggerContext.getProperty("CONSOLE_LOG_CHARSET")).isEqualTo(StandardCharsets.UTF_8.name());
}
@Test
void consoleLogCharsetShouldUseSystemPropertyIfSet() {
withSystemProperty("CONSOLE_LOG_CHARSET", StandardCharsets.US_ASCII.name(), () -> {
new DefaultLogbackConfiguration(null).apply(this.logbackConfigurator);
assertThat(this.loggerContext.getProperty("CONSOLE_LOG_CHARSET"))
.isEqualTo(StandardCharsets.US_ASCII.name());
});
}
@Test
void fileLogCharsetShouldUseSystemPropertyIfSet() {
withSystemProperty("FILE_LOG_CHARSET", StandardCharsets.ISO_8859_1.name(), () -> {
new DefaultLogbackConfiguration(null).apply(this.logbackConfigurator);
assertThat(this.loggerContext.getProperty("FILE_LOG_CHARSET"))
.isEqualTo(StandardCharsets.ISO_8859_1.name());
});
}
@Test
void fileLogCharsetShouldDefaultToUtf8() {
new DefaultLogbackConfiguration(null).apply(this.logbackConfigurator);
assertThat(this.loggerContext.getProperty("FILE_LOG_CHARSET")).isEqualTo(StandardCharsets.UTF_8.name());
}
private void assertThatDefaultXmlContains(String name, String value) throws Exception { private void assertThatDefaultXmlContains(String name, String value) throws Exception {
String expected = "<property name=\"%s\" value=\"%s\"/>".formatted(name, value); String expected = "<property name=\"%s\" value=\"%s\"/>".formatted(name, value);
assertThat(defaultXmlContent()).contains(expected); assertThat(defaultXmlContent()).contains(expected);
@ -51,4 +102,21 @@ class DefaultLogbackConfigurationTests {
return StreamUtils.copyToString(getClass().getResourceAsStream("defaults.xml"), StandardCharsets.UTF_8); return StreamUtils.copyToString(getClass().getResourceAsStream("defaults.xml"), StandardCharsets.UTF_8);
} }
private static void withSystemProperty(String name, String value, Runnable action) {
String previous = System.getProperty(name);
try {
System.setProperty(name, value);
action.run();
}
finally {
if (previous != null) {
System.setProperty(name, previous);
}
else {
System.clearProperty(name);
}
}
}
} }

4
core/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemPropertiesTests.java

@ -131,10 +131,10 @@ class LogbackLoggingSystemPropertiesTests {
} }
@Test @Test
void fileCharsetWhenNoPropertyUsesDefault() { void fileCharsetWhenNoPropertyUsesUtf8() {
new LoggingSystemProperties(new MockEnvironment()).apply(null); new LoggingSystemProperties(new MockEnvironment()).apply(null);
assertThat(System.getProperty(LoggingSystemProperty.FILE_CHARSET.getEnvironmentVariableName())) assertThat(System.getProperty(LoggingSystemProperty.FILE_CHARSET.getEnvironmentVariableName()))
.isEqualTo(Charset.defaultCharset().name()); .isEqualTo(StandardCharsets.UTF_8.name());
} }
} }

Loading…
Cancel
Save