logback自定义输出模板, 匹配ELK json格式

最近要把日志导入ELK管理, 需要把java日志打印成json格式, 谷歌了一通, 主要都是通过logback配置的方式来实现

配置方式一

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appender name="stash" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>info</level>
    </filter>
    <file>/some/path/to/your/file.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>/some/path/to/your/file.log.%d{yyyy-MM-dd}</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder class="net.logstash.logback.encoder.LogstashEncoder" />
  </appender>
  <root level="all">
    <appender-ref ref="stash" />
  </root>
</configuration>

这种配置输出的日志格式:

{
  "@timestamp": "2018-09-04T10:14:40.215+08:00",
  "@version": "1",
  "message": "invoke getSchedules request:{\r\n  \"token\": \"JI1mVZxAjLTLLIp4em1slPWGDWwrAPRqLQh2rC7rAmsXQg==\",\r\n  \"timeZone\": \"Asia/Shanghai\",\r\n  \"accountID\": \"1000009\",\r\n  \"uuid\": \"f7dda551-84a5-45ec-8b68-4e9803101dc7\"\r\n}",
  "logger_name": "com.etekcity.airpurifier131.controller.v1.ScheduleController",
  "thread_name": "http-nio-8080-exec-1",
  "level": "INFO",
  "level_value": 20000
}

通过这种配置, 该json的key都是无法自定义的, 我们想要的信息是message里的各个信息, 但是输出的日志message对应的是字符串, 只能通过logstash后期处理解决. 在message里, 由于json字符串前面又有打印的文字信息, 增大了解析的难度. 当然, 解析处理应该是可行的.

配置方式二

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 配置文件轮转 -->
    <appender name="logfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>/var/log/tomcat/AirPurifier131/AirPurifier131.%d{yyyy-MM-dd-HH}.log</FileNamePattern>
            <maxHistory>48</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="com.etekcity.airpurifier131.config.MyLogLayout" />
        </encoder>
    </appender>
    <root level="all">
        <appender-ref ref="logfile"/>
    </root>
</configuration>

此配置需要自己写一个自定义的LayOut类继承LayOutBase类, 可以在该类里自定义日志输出的格式, 自由度较高, 我采用的是这种方式
MyLogLayout类:

package com.etekcity.airpurifier131.config;

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

import java.sql.Timestamp;

public class MyLogLayout extends LayoutBase<ILoggingEvent> {

    private static final String PROJECT_NAME = "AirPurifier131";

    @Override
    public String doLayout(ILoggingEvent event) {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append("\"P\":");
        sb.append("\""+PROJECT_NAME+"\", ");
        sb.append("\"T\":");
        sb.append("\"").append(new Timestamp(event.getTimeStamp())).append("\"");
        sb.append(", \"L\":");
        sb.append("\"").append(event.getLevel()).append("\"");
        sb.append(", \"THREAD\":");
        sb.append("\"").append(event.getThreadName()).append("\"");
        sb.append(", \"CLASS\": ");
        sb.append("\"").append(event.getLoggerName()).append("\"");
        sb.append(",\"message\": ");
        String message = event.getFormattedMessage();
        if (event.getThrowableProxy()!=null){
            ExtendedThrowableProxyConverter throwableConverter = new ExtendedThrowableProxyConverter();
            throwableConverter.start();
            message = event.getFormattedMessage() + "\n" + throwableConverter.convert(event);
            throwableConverter.stop();
        }
        int beginIndex = message.indexOf("{");
        int endIndex = message.lastIndexOf("}")+1;
        if (beginIndex >= 0 && endIndex >= 0) {
            String Additional = message.substring(0, beginIndex);
            String jsonMsg = message.substring(beginIndex, endIndex);
            sb.append(jsonMsg);
            sb.append(",\"Additional\": ");
            sb.append("\"").append(Additional).append("\"");
        }else {
            sb.append("\"").append(message).append("\"");
        }
        sb.append("}");
        sb.append(CoreConstants.LINE_SEPARATOR);
        return sb.toString();
    }
}

可以看到我在该类里重写的doLayout方法, 自定义了日志的格式, 还初步解析了message, 拿出了较为完整的json, 本来可以继续解析, 将message里的json键值全部移至外层, 但是logstash解析json格式已经很方便了, 就放在了logstash中去解析. 这是java项目输出的日志:

{
  "P": "AirPurifier131",
  "T": "2018-09-07 09:20:49.966",
  "L": "INFO",
  "THREAD": "http-nio-8080-exec-2",
  "CLASS": "com.etekcity.airpurifier131.controller.v1.ScheduleController",
  "message": {
    "token": "JI1mVZxAjLTLLIp4em1slPWGDWwrAPRqLQh2rC7rAmsXQg==",
    "timeZone": "Asia/Shanghai",
    "accountID": "1000009",
    "uuid": "f7dda551-84a5-45ec-8b68-4e9803101dc7"
  },
  "Additional": "invoke getSchedules request:"
}

可见, 自定义之后日志结构清晰了很多.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一川云水。一朵彼岸 一影碎念。一场风花 一杯茗茶香,浸没我半世烛光。
    猫腻2阅读 103评论 0 0
  • Design Facebook collage ByChristophe Tauziet(Facebook des...
    风之郁少阅读 617评论 0 7
  • 好久没有看朋友圈了,今天早上看了看,有哪么多的朋友关心我,鼓励我,谢谢你们,我爱你们!我也要关顾一下朋友们发的微信...
    cai彩阅读 188评论 1 1