Spring Boot日志格式化JSON方案

在工作中需要对Spring Boot日志进行JSON格式化,主要是为了传递给ELK做日志收集平台分析。

考虑了以下几种方案:

  1. 自定义Converter实现
  2. 重新覆写Appender和Layout类,自定义实现
  3. 使用logstash-logback-encoder来实现

最后使用logstash-logback-encoder来实现,今后也方便扩展,在这里总结一下。

在Spring Boot的项目中,主要是使用了LogBack,这里简单介绍一下LogBack,Logback继承自log4j。

Logback的架构非常的通用,适用不同的使用场景。Logback 被分成三个不同的模块:logback-core,logback-classic,logback-access。

logback-core 是其它两个模块的基础。logback-classic 模块可以看作是 log4j 的一个优化版本,它天然的支持 SLF4J,所以你可以随意的从其它日志框架(例如:log4j 或者 java.util.logging)切回到 logack。

logback-access 可以与 Servlet 容器进行整合,例如:Tomcat、Jetty。它提供了 http 访问日志的功能。

在Spring Boot中默认内部集成了LogBack日志依赖,而Spring Boot默认使用了LogBack记录日志信息。所以我们可以直接就进行使用。

一、自定义Converter实现

主要是继承ch.qos.logback.classic.pattern.ClassicConverter这个类,然后覆写convert方法实现逻辑。我们知道在logback中包含level,msg这些变量,使用Coverter我们便可以定义自己的变量,然后再在convert方法里面进行Json处理。比如这里我们新建类JsonLogConverter,如下:

package net.anumbrella.spring.log.config;

import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import org.json.JSONObject;

/**
 * @auther anumbrella
 */

public class JsonLogConverter extends ClassicConverter {

    private JSONObject object = new JSONObject();

    @Override
    public String convert(ILoggingEvent iLoggingEvent) {
        object.put("msg",iLoggingEvent.getMessage());
        object.put("level", iLoggingEvent.getLevel().levelStr);
        object.put("threadName", iLoggingEvent.getThreadName());
        object.put("method", iLoggingEvent.getLoggerName());
        return object.toString();
    }
}

然后再在Resources下面添加logback-converter.xml文件,通过application.properties 配置logging.config=classpath:logback-converter.xml指定logback的路径,然后logback会继续寻找名为logback-converter.xml的文件并读取相关配置。

这里我们还需要添加

 <conversionRule conversionWord="customJson" converterClass="net.anumbrella.spring.log.config.JsonLogConverter" />

这个便是自定转换规则,conversionWord为自定义变量,而converterClass为自定义转换规则。具体logback.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds" packagingData="true">

    <conversionRule conversionWord="customJson"
                    converterClass="net.anumbrella.spring.log.config.JsonLogConverter" />

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%customJson%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

启动后我们可以在控制台看到具体的JSON日志,如下:

converter

这种方式需要所有变量都要进行处理,同时灵活性也较差。

二、重新覆写Layout类,自定义实现

通过自定义Layout类来实现json输出,主要是继承ch.qos.logback.core.LayoutBase这个类,然后覆写doLayout方法。比如这里我们新建类JsonLogLayout,如下:

package net.anumbrella.spring.log.config;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

/**
 * @auther anumbrella
 */

public class JsonLogLayout extends LayoutBase<ILoggingEvent> {

    @Override
    public String doLayout(ILoggingEvent event) {
        StringBuilder result = new StringBuilder();
        if (null != event) {
            result.append("{");

            result.append("\"time\":\"");
            result.append(System.currentTimeMillis());
            result.append("\",");

            result.append("\"level\":\"");
            result.append(event.getLevel());
            result.append("\",");

            result.append("\"threadName\":\"");
            result.append(event.getThreadName());
            result.append("\",");

            result.append("\"msg\":\"");
            result.append(event.getFormattedMessage().replace("\"", "\\\""));
            result.append("\",");

            result.append("\"method\":\"");
            result.append(event.getLoggerName());
            result.append("\"} \n");
        }
        return result.toString();
    }
}

同理我们新建logback-layout.xml文件,然后在application.properties更改指定配置。然后还需要在logback-layout.xml中配置自定义的layout,并添加在encoder里。

   <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
       <layout class="net.anumbrella.spring.log.config.JsonLogLayout" />
   </encoder>

具体XML如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds" packagingData="true">


    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="net.anumbrella.spring.log.config.JsonLogLayout" />
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

启动应用后,我们发现一样能实现相同功能。

这种方式和前面类似,所有变量都要进行处理,同时灵活性也较差。

三、使用logstash-logback-encoder来实现

logstash-logback-encoder作为第三方类库来使用,功能性和扩展性都比较好,github开发者也很活跃,地址

而且考虑到日志系统主要对接elasticsearch系统,通过logstash提供的类库来实现也是一种不错的解决方案。首先我们在pom.xml中引入类库logstash-logback-encoder

     <dependency>
         <groupId>net.logstash.logback</groupId>
         <artifactId>logstash-logback-encoder</artifactId>
         <version>6.2</version>
     </dependency>

然后在logback-logstash.xml中添加:

    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
        <jsonGeneratorDecorator
                class="net.logstash.logback.decorate.FeatureJsonGeneratorDecorator"/>
    </encoder>

然后设置pattern模式,如下:

   <providers>
       <pattern>
           <pattern>
               {
                   "date": "%date{yyyy-MM-dd'T'HH:mm:ss.SSSZ}",
                   "level": "%level",
                   "msg": "%msg %n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"
               }
           </pattern>
       </pattern>
   </providers>

注意:providers是要包含在encoder下面。

具体XML详情如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds" packagingData="true">

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <jsonGeneratorDecorator
                    class="net.logstash.logback.decorate.FeatureJsonGeneratorDecorator"/>
            <providers>
                <pattern>
                    <pattern>
                        {
                        "date": "%date{yyyy-MM-dd'T'HH:mm:ss.SSSZ}",
                        "level": "%level",
                        "msg": "%msg"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>

    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

配置就那么点点,是不是觉得非常的简单,这样就能实现日志json输出,而且如果想要对json进行格式化输入,直接配置更改jsonGeneratorDecorator就可以了。更改为如下:

<jsonGeneratorDecorator class="net.logstash.logback.decorate.PrettyPrintingJsonGeneratorDecorator"/>

结果如图:

pretty json
相对来说使用logstash-logback-encoder实现更简单,而且而且官方也提供了其他扩展性功能,也能够实现自定义扩展。

四、扩展

在开发的时候我们希望能够走Spring Boot原生的日志显示,生产时候显示Json或者格式化显示Json。如果可配置那就方便了,其实可以通过logging.config指定不同文件就可以了。但还有一种方式——表达式。logback 在配置文件中支持通过 <if>、<then>、<else> 元素作为条件语句来区分不同的环境。条件处理需要 Janino 环境的支持。需要引入依赖,如下:

<dependency>
    <groupId>org.codehaus.janino</groupId>
    <artifactId>janino</artifactId>
    <version>2.6.1</version>
</dependency>

同时我们可以在logback中定义变量读取配置文件的信息,如下:

<springProperty scope="context" name="logType" source="logging.type" defaultValue="normal" />

接着在表达式里面进行判断:

<if condition='property("logType").equals("normal")'>
......
</if>

代码实例:log

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值