Logback输出json格式日志,及异常信息不在Json串内的原因分析

1,086 阅读3分钟

前言

日志什么场景要输出为json格式,以及关于logback的介绍等,不是本文讨论的重点,所以不再说明。

下面的部分假定你已经熟悉logback了,如果你平常也用logback,但是每次都是从网上随便复制一个配置文件直接用,对其中的标签并不了解,那看这篇文章不能保证很轻松。

日志格式

logback的配置文件中,我们可以指定输出日志的格式,如下是一个特别简单的格式:

<appender name="SIMPLE-CONSOLE" class="ch.qos.logback.core.ConsoleAppender">  
    <encoder>  
        <pattern>%level %thread %msg \r\n</pattern>  
        <charset>UTF-8</charset>  
    </encoder>  
</appender>

打印日志的级别、线程、日志信息,最后加上了回车换行符。 这是效果:

image.png

至于有哪些字段可以输出,可以看ch.qos.logback.classic.PatternLayout 里面设置的转换器。

json格式

现在想把日志输出为json格式,那也很简单,如下定义:

<appender name="JSON-CONSOLE" class="ch.qos.logback.core.ConsoleAppender">  
    <encoder>  
        <pattern>{ "level":"%level", "thread": "%thread", "msg":"%msg" }\r\n</pattern>  
        <charset>UTF-8</charset>  
    </encoder>  
</appender>

打印一下效果:

image.png 看起来也是比较预期。

如果抛个异常呢,最开始示例的常规文本输出格式,异常也是会正常打印的,如果是Json格式呢,如下:

image.png

异常信息和msg信息是分离的,并不在一个json体内,这就很不预期了,实际期望的应该是msg里面包含着异常信息。

字段转换

image.png 如上图所示,定义appender的时候,指定了日志格式,及输出哪些字段。

logback就是将pattern里的字段转换为对应的值,然后按照对应的格式输出。所以我们定义为json格式的时候,logback也是将其中的相关变量字段替换为实际值,对于其中的字面值,比如"{ }"等部分,还是原样拼接成一个字符。

encoder标签默认使用的实现类是ch.qos.logback.classic.encoder.PatternLayoutEncoder类

pattern标签默认使用的实现类是ch.qos.logback.classic.PatternLayout类

打个断点看下这个拼接的过程,这是拼了一半的样子:

image.png

这部分的源码,一两句话并不能说清楚,简单来说,就是logback使用对应字段的转换器,将字段转换为对应的值。

image.png

异常的输出

各个字段会使用对应的转换器替换为实际的值输出,%msg对应的转换器,实际只是输出原始日志,并不包含异常信息:

image.png

而我们实际也没并有要输出异常信息,我们只定义了这三个字段:

<pattern>%level %thread %msg \r\n</pattern>  

没有指定异常字段,异常字段是下面这几个:

image.png

但是打印日志依然输出了异常,究其原因,是因为logback自动帮我们拼接了一个异常的转换器,可以这样理解,我们定义的是:

<pattern>{ "level":"%level", "thread": "%thread", "msg":"%msg" }\r\n</pattern>  

实际上是:

<pattern>{ "level":"%level", "thread": "%thread", "msg":"%msg" }\r\n %ex</pattern>  

这个过程,是在new PatternLayout()实例的时候发生的。

image.png

所以我们虽然输出Json,却在json串后面打印了一个异常信息。这是相关代码注释,大意是检查定义的转换器链是否处理了异常,如果没有就添加一个到链的末尾:

This implementation checks if any of the converters in the chain handles exceptions. If not, then this method adds a ExtendedThrowableProxyConverter instance to the end of the chain.

这就是最后很突兀的打印异常栈的原因,至于解决办法,下一篇文章进行说明。