使用spring boot+logback的一些編碼問題整理
本文主要講述在spring boot中使用logback時出現的一些中文亂碼問題,在springMVC中基本也是適用的。
輸出到檔案中,配置如下:
<appender name="STDOUT" class="ch.qos.logback.core.FileAppender">
<file>D:\firstLog.log</file>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-2level %logger{36} [%method][%line] - %msg%n</Pattern >
</layout>
</appender>
<root level="ERROR">
<appender-ref ref="STDOUT"/>
</root>
這個配置可以成功輸出日誌到指定的檔案,但當輸出中文時,出現了亂碼,有些同學可能沒有發現亂碼的情況,是因為開啟文字的工具預設編碼有區別,直接用windows記事本開啟可能沒有亂碼,但用Nodepad++等其他文字工具開啟可能就是亂碼。重要的是我們得明白logback輸出的是什麼編碼。
新增的appender,如果沒有指定編碼,那預設編碼是什麼呢,預設就是Null,最後在輸出的地方會取當前呼叫當前執行環境的預設編碼。
private byte[] convertToBytes(String s) {
if(this.charset == null) {
return s.getBytes();
} else {
try {
return s.getBytes(this.charset.name());
} catch (UnsupportedEncodingException var3) {
throw new IllegalStateException("An existing charset cannot possibly be unsupported." );
}
}
}
由於charset==null
//String.getBytes()
public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);
}
//StringCoding.encode(char[] ca, int off, int len)
static byte[] encode(char[] ca, int off, int len) {
String csn = Charset.defaultCharset().name();
try {
// use charset name encode() variant which provides caching.
return encode(csn, ca, off, len);
} catch (UnsupportedEncodingException x) {
warnUnsupportedCharset(csn);
}
try {
return encode("ISO-8859-1", ca, off, len);
} catch (UnsupportedEncodingException x) {
// If this code is hit during VM initialization, MessageUtils is
// the only way we will be able to get any kind of error message.
MessageUtils.err("ISO-8859-1 charset not available: "
+ x.toString());
// If we can not find ISO-8859-1 (a required encoding) then things
// are seriously wrong with the installation.
System.exit(1);
return null;
}
}
這個預設編碼是哪裡的預設編碼呢?是tomcat的預設編碼,那我們可以去修改tomcat的預設編碼:
有人提供了簡便的設定tomcat charset的方式:
在tomcat/bin目錄下建一個檔案,檔名為 setenv.bat ,並將下面程式碼寫入檔案,重啟tomcat,預設編碼就變為UTF-8了,用Logback輸出就沒有中文亂碼情況了。
set "JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF8"
這是一種處理方式,但個人不太支援這種做法,因為很多預設輸出用的是GBK的,將tomcat編碼設定為UTF-8只是方便了自己輸出日誌,但有可能導致其他編碼的輸出會變成亂碼,所以最好不要去動tomcat的預設編碼。
解決方法當然是設定logback本身的輸出編碼,我們可以參見spring boot提供的console-appender.xml
<included>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
</included>
設定編碼一定要在encoder節點下設定,有些人說可以直接在appender節點下新增charset,略翻原始碼發現最後的輸出是在類ch.qos.logback.core.encoder.LayoutWrappingEncoder下完成的,這個類有個Charset成員變數,最終輸出編碼獲取的就是這個變數,如果沒有則取當前執行環境的預設編碼。
設定編碼可以如下:
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%date{HH:mm:ss.SSS} [%thread] %-5level %logger{10} [%file:%line] - %msg%n</pattern>
</layout>
<charset>UTF-8</charset>
</encoder>
如果沒有特殊要求可以引用spring boot寫好的base.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
Base logback configuration provided for compatibility with Spring Boot 1.1
-->
<included>
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</included>
我們可以看到base.xml引入了兩個appender,他的預設輸出編碼都是UTF-8的,但是執行環境不預設編碼為GBK,編碼有衝突,所以在console中輸出的又是亂碼,我們可以把他的預設配置copy到我們自定義的logback-spring.xml下,修改下預設的名稱為CONSOLE的appender預設編碼為GBK。
總結下來logback輸出中文的亂碼情況我們要考慮兩個因素:
- 位元組流的編碼 byte[],通常為 s.getBytes(this.charset.name());
- 當前執行環境的預設編碼,這個基本上可以確定是伺服器的編碼,這個我們可以通過Charset.defaultCharset().name()檢視。值得注意的是通過spring boot內建的tomcat執行時,他的預設編碼是UTF-8,所以無需任何設定,但當打包成war包時就會出現亂碼情況了,所以最好在logback中明確設定好編碼,以免受到驚嚇。。
歡迎關注我的個人公眾號:逍遙的心。主推程式設計師寫的生活類文章,有興趣的朋友可以共同探討下: