本文主要讲述在spring boot中使用logback时出现的一些中文乱码问题,在springMVC中基本也是适用的。

logback常用配置可参考

输出到文件中,配置如下:

<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中明确设置好编码,以免受到惊吓。。