[英]Logback System.err output uses wrong encoding
我在 Windows 10 上使用带有 Java 17 的 Logback 1.2.11。我正在使用以下logback.xml
:
<configuration>
<property scope="context" name="COLORIZER_COLORS" value="boldred@,boldyellow@,boldcyan@,@,@" />
<conversionRule conversionWord="colorize" converterClass="org.tuxdude.logback.extensions.LogColorizer" />
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
<target>System.err</target>
<withJansi>true</withJansi>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>[%colorize(%level)] %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDERR" />
</root>
</configuration>
如果在我的代码中使用System.out.println("é")
或System.err.println("é")
,我会按预期在控制台上看到一个é
(U+00E9,一个带重音的小写字母 e) . 但是,如果我通过 Logback(通过 SLF4J)登录,它会在屏幕上显示一个Θ
字符(U+0398,希腊大写字母 theta)。 无论我在logback.xml
文件中使用<target>System.out</target>
还是<target>System.err</target>
都会发生这种情况。
默认情况下, ConsoleAppender
的PatternLoutEncoder
应该使用系统默认编码。 (有关详细讨论,请参阅LayoutWrappingEncoder? 的 LogBack 默认字符集。)我的语言环境中的 Windows 10 控制台编码应该是 Windows-1252(或 Powershell 中的 ISO-8859-1)。 Θ 字符甚至没有出现在这些字符集中。
为什么 Logback 在应该打印é
字符时将Θ
字符打印到标准输出? 更一般地说,为什么在打印到System.out
或System.err
时 Logback 不使用默认编码?
看起来 Logback 使用了错误的“默认字符集”。 System.out
的 API Javadocs 说明了它的默认字符集(也适用于System.err
):
“标准”输出流。 此流已经打开并准备好接受输出数据。 通常,此流对应于主机环境或用户指定的显示输出或另一个输出目的地。 如果
Console
存在,则从字符到字节的转换中使用的编码等效于Console.charset()
,否则等效于Charset.defaultCharset()
。
在我的 Windows 10 命令提示符上, Charset.defaultCharset()
返回windows-1252
,而System.console().charset()
返回IBM437
。 如果创建一个new OutputStreamWriter(System.out, System.console().charset())
并写入字符串"é"
,它将按预期生成é
。 但是如果我使用new OutputStreamWriter(System.out, Charset.defaultCharset())
并写"é"
,它肯定会产生Θ
! 这就是 Θ 的来源——它是IBM437
字符集的一部分!
我不会在这里问为什么我的 Windows 10 命令提示符默认使用IBM437
作为其默认字符集; 在这个问题的背景下,这是无关紧要的。
根本问题似乎是 Logback 错误地检索了默认字符集。 (说来话长,但基本上 Logback依赖于String.getBytes String.getBytes()
的默认字符集。) LayoutWrappingEncoder 中的LayoutWrappingEncoder
最终依赖于Charset.defaultCharset()
的值,这与控制台的值不匹配; 相反,它应该默认为System.console().charset()
如果它想要匹配控制台的默认字符集。
显然LayoutWrappingEncoder
不知道它是在写入控制台还是其他实际上使用Charset.defaultCharset()
的输出流。 也许需要某种方式ch.qos.logback.core.OutputStreamAppender
可以将其字符集公开给LayoutWrappingEncoder
,而ch.qos.logback.core.ConsoleAppender
可以覆盖基于System.console().charset()
的默认值的Charset.defaultCharset()
。
无论如何,这里的罪魁祸首似乎是 Logback 为System.out
和System.err
的控制台使用了错误的默认字符集。 (任何人都知道我如何告诉 Logback 使用System.console().charset()
而不是Charset.defaultCharset()
?我当然没有办法提前知道默认的控制台字符集,所以我不能将其硬编码到logback.xml
中。)
我已经提交了 Logback 错误LOGBACK-1642 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.