Spring Boot 使用 Log4j2 & Logback 輸出日志到 EKL


轉載自:https://cloud.tencent.com/developer/article/1433238

1、ELK 介紹

ELK 是 Elasticsearch , Logstash, Kibana 的縮寫,Elasticsearch 是開源分布式搜索引擎,提供搜集、分析、存儲數據等功能,Logstash 主要是用來日志的搜集、分析、過濾日志的工具,Kibana 為 Elasticsearch 提供分析和可視化的 Web 平台,可以在 Elasticsearch 的索引中查找,交互數據,並生成各種維度的表圖。一句話:日志收集(Logstash),數據存儲(Elasticsearch),日志可視化(Kibana),三者有機結合起來,非常方便集中式處理日志。

2、環境、軟件准備

本次演示環境,我是在本機 MAC OS 上操作,以下是安裝的軟件及版本:

  • Java: 1.8.0_211
  • Elasticsearch: 7.1.0
  • Logstash: 7.1.0
  • Kibana: 7.1.0
  • Spring Boot: 2.1.4.RELEASE

注意:本次主要演示如何在 Spring-Boot 項目中配置 Log4j2 以及 Logback 輸出日志到 ELK 中,並能夠在 Kibana 中可以正確檢索出來,Elasticsearch 及 Spring-Boot 項目底層需要 Java 環境,所以需要提前本地安裝好 Java 環境,這里忽略 Java 安裝過程。

3、ELK 環境搭建

我們可以去 官網 分別下載系統對應最新版 ElasticsearchLogstashKibana,截止目前已更新到 7.1.0 版本,可通過以下鏈接獲取安裝包並提供默認配置啟動步驟。

這里我先按照默認配置啟動 Elasticsearch 服務,啟動完畢,本地可以通過 http://127.0.0.1:9200 地址訪問服務是否啟動正常。注意:先不啟動 LogstashKibana,因為他們的配置需要更改,下邊會講到。

img

4、Spring Boot 配置示例

使用 Idea 創建一個 Spring Boot 項目,我們先添加 Log4j2支持,演示如何使用 Log4j2 將日志直接輸出到本地的 ELK 中,然后演示下通過 Logback 動態輸出索引名稱到日志中,方便分類檢索日志。

4.1、Log4j2 方式配置

首先修改 pom.xml 增加 Log4j2 日志框架支持,注意 spring-boot-starter 默認使用 Logback 作為日志框架,所以需要先移除默認日志配置 spring-boot-starter-logging

pom.xml 增加如下配置:

	 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
	 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>
    <dependency>
        <groupId>com.lmax</groupId>
        <artifactId>disruptor</artifactId>
        <version>3.4.2</version>
    </dependency>

注意:disruptor 是一個輕量的高性能並發框架,Log4j2 包含了基於 LMAX Disruptor(高性能線程間消息通信庫)的下一代 Asynchronous Loggers。在多線程環境下 Asynchronous Loggers 的吞吐量是 Log4j1Logback 的 18 倍,而延遲時間也要低一個數量級。如果使用異步日志時,添加 disruptor 支持,會大大提高效率,當然不添加也是沒有問題的。

增加 log4j2-spring.xml 配置輸出到 ELK 中,大概配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="OFF" monitorInterval="60">
    <Appenders>
        <!-- Console 日志,只輸出 level 及以上級別的信息,並配置各級別日志輸出顏色 -->
        <Console name="Console" target="SYSTEM_OUT">
            <!--控制台只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="%highlight{%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %M() @%L - %msg%n}{FATAL=Bright Red, ERROR=Bright Magenta, WARN=Bright Yellow, INFO=Bright Green, DEBUG=Bright Cyan, TRACE=Bright White}"/>
        </Console>
		 <!-- socket 日志,輸出日志到 Logstash 中做日志收集 -->
        <Socket name="Socket" host="127.0.0.1" port="4560" protocol="TCP">
            <JsonLayout properties="true" compact="true" eventEol="true" />
            <PatternLayout pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %M() @%L - %msg%n"/>
        </Socket>
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <appender-ref ref="Socket"/>
            <appender-ref ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

注意:這里配置 Socket 輸出方式,輸出日志到本地 Logstash 做日志收集,格式化處理后自動輸出到本地 Elasticsearch 中存儲,最后通過 Kibana 檢索索引通過 Web 頁面展示出來。需要指定 hostportprotocol,這里就配置成本地 Logstash 指定配置的端口和地址即可。

同時可以在 application.properties 中配置日志輸出級別,注意這里可以不指定加載 log4j2-spring.xml 文件,Spring Boot 會默認加載該配置文件。

logging.level.root=info

最后,代碼中在 Controller 寫入一些特定日志和異常信息,方便在 Kibana 中查看驗證。

@RestController
@RequestMapping("/test")
public class LogController {

    private Logger logger = LogManager.getLogger(LogController.class);
    
    @RequestMapping(value = "/log4j2", method = RequestMethod.GET)
    public String testLog(){
        try {
            logger.info("Hello 這是 info message. 信息");
            logger.error("Hello 這是 error message. 報警");
            logger.warn("Hello 這是 warn message. 警告");
            logger.debug("Hello 這是 debug message. 調試");
            logger.fatal("Hello 這是 fatal message. 嚴重");
            List<String> list = new ArrayList<>();
            System.out.println(list.get(2));
        } catch (Exception e) {
            logger.error("testLog", e);
        }
        return "";
    }
}

OK,Spring Boot 工程添加 Log4j2 支持配置完畢,接下來,我們需要配置 LogstashKibana,在 Logstash 安裝目錄下 config 目錄,新建 test-log4j2.conf 配置文件,配置如下:

input {
  tcp {
    host => "127.0.0.1"
    port => "4560"
    mode => "server"
    type = json
  }
  stdin {}
}
filter {

}
output {
  stdout {
    codec => rubydebug
  }
  elasticsearch {
    hosts => ["127.0.0.1:9200"]
    action => "index"
	 codec => rubydebug
    index => "log4j2-%{+YYYY.MM.dd}"
  }
}

注意:這里配置 tcp 的 host 和 port 要跟上邊 log4j2-spring.xml 中配置一致,否則無法進行日志收集, output 下的 elasticsearch 的 host 配置要跟上邊啟動的本地 Elasticsearch 配置一致,index 指定為固定的 log4j2-yyyy.MM.dd 格式,方便在 Kibana 中檢索索引使用。使用該配置文件啟動 Logstash,命令如下:

$ cd <Logstash_path>/bin
$ ./logstash -f ../config/test-log4j2.conf

最后,啟動 Kibana,可以使用默認配置,不過這里我稍微修改一些配置如下:

$ vim <Kibana_path>/config/kibana.conf

server.port: 5601
server.host: "127.0.0.1"
elasticsearch.hosts: ["http://127.0.0.1:9200"]
i18n.locale: "zh-CN"

同理,這里 elasticsearch.hosts 配置要跟上邊一致,同時指定 Kibana 啟動端口為 5601,並且修改顯示默認英文為中文,方便查看,啟動 Kibana 直到日志輸出顯示狀態為 Green 即啟動完畢。

一切都准備完畢,最后啟動 Spring Boot 工程,並觸發 /test/log4j2 接口,制造各類日志,在 Kibana Web 頁面查看是否正確加載過來吧!

瀏覽器訪問 http://127.0.0.1:4560 即可打開 Kibana 頁面,首先我們查看下 Elasticsearch 索引管理里面,是否已存在上邊配置的 log4j2-yyyy.MM.dd 格式索引。

img

OK,顯示已存在,那么接下來我們在 Kibana 索引模式下創建索引模式,輸入 log4j2-* 即可正確匹配到 Elasticsearch 中的指定的索引,接着在時間篩選字段名稱處選擇 @timestamp,方便我們后邊按照時間段篩選數據,創建過程如下:

img

img

img

創建完畢,我們就可以在 Kibana 中篩選並顯示日志了,比如我增加了 message 字段,過濾完后,就顯示出來上邊工程示例代碼中的各種類型日志以及異常日志了,非常直觀方便!

img

4.2、Logback 方式配置

上邊使用 Log4j2 日志框架可以正確輸出日志到 ELK,但是有一個地方需要我們注意,就是啟動 Logstash 時指定 Elasticsearch 的 index 索引為固定值(log4j2-*)了,如果有多個 project 同時往 ELK 中輸出日志,那么使用同一個索引名稱的話,會造成日志混亂,不方便區分排查各個項目的日志,所以,我們希望能夠通過動態輸出索引名稱到 Elasticsearch 中,例如 Project A 輸出到索引 projectA-* 下,Project B 輸出到索引 projectB-* 下,這樣就方便我們在 Kibana 下根據項目匹配對應的索引值了,當然使用 Spring Boot 默認日志框架 Logback 可以很輕松的辦到。

那么改造項目來支持 Logback 日志框架,首先修改 pom.xml 配置文件如下:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
<!--        <exclusions>-->
<!--            <exclusion>-->
<!--                <groupId>org.springframework.boot</groupId>-->
<!--                <artifactId>spring-boot-starter-logging</artifactId>-->
<!--            </exclusion>-->
<!--        </exclusions>-->
    </dependency>


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

注意:這里將移除的 spring-boot-starter-logging 重新添加進來,同時依賴 logstash-logback-encoder 該插件,該插件起到 Socket 通過 TCP 方式向 Logstash 進行日志輸送的作用。

接着增加 logback-spring.xml 配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <property name="LOG_HOME" value="logs/demo.log" />
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>127.0.0.1:4560</destination>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder" >
            <customFields>{"appname": "demo-elk"}</customFields>
        </encoder>
    </appender>

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

注意:這里的 destination 依舊要配置對應上本地 Logstash 配置,着重說下 <customFields>{"appname": "demo-elk"}</customFields> 字段配置,該自定義字段配置, Logstash 收集日志時,每條日志記錄均會帶上該字段,而且在 Logstash 配置文件中可以通過變量的方式獲取到字段,這樣就能達到我們說的動態輸出索引名稱到 Elasticsearch 中的功能了。同樣,application.properties 可以不指定加載 logback-spring.xml 文件,Spring Boot 會默認加載該配置文件。接下來,去 Logstash config 下增加 test-logback.conf 配置文件,配置如下:

input {
  tcp {
    host => "127.0.0.1"
    port => "4560"
    mode => "server"
    type => json
  }
  stdin {}
}
filter {

}
output {
  stdout {
    codec => rubydebug
  }
  elasticsearch {
    hosts => ["127.0.0.1:9200"]
    action => "index"
    codec => rubydebug
    index => "%{[appname]}-%{+YYYY.MM.dd}"
  }
}

注意:這里的 %{[appname]} 就是獲取上邊的 <customFields> 字段中的 json 串 key 值,我們只傳了一個 appname 值,當讓還可以傳遞其他值,例如 IP、Hostname 等關鍵信息,方便在 Kibana 中檢索索引時區分。重啟一下 Logstash 指定該配置文件。

$ cd <Logstash_path>/bin
$ ./logstash -f ../config/test-logback.conf

ElasticsearchKibana 不需要重啟,再次啟動 Spring Boot 工程,去 Kibana 下查看 !查看下 Elasticsearch 索引管理里面,是否已存在上邊配置的 demo-elk-yyyy.MM.dd 格式索引。

img

What? 怎么沒有獲取到傳遞過去的 appname 值呢?原樣配置到 Elasticsearch 索引中去了,但是我在后台 Logstash 控制台日志中可以明顯看到,打印的每條 Json 串中是有該字段的呀!各種搜索,發現大家也是這么配置的呢!即使將 type => json 改為 codec => json 依舊不行!百思不得解的時候,查看了下 logstash-logback-encoder 文檔說明 這里明確指出要使用 codec => json_lines 方式,好吧! test-logbash.conf 配置修改如下並重啟 Logstash

input {
  tcp {
    host => "127.0.0.1"
    port => "4560"
    mode => "server"
    codec => json_lines
  }
  stdin {}
}
filter {

}
output {
  stdout {
    codec => rubydebug
  }
  elasticsearch {
    hosts => ["127.0.0.1:9200"]
    action => "index"
    index => "%{[appname]}-%{+YYYY.MM.dd}"
  }
}

這下妥妥沒有問題了,在去查看下 Elasticsearch 索引管理,這下就有了。

img

那么接着建一個索引模式名稱為 demo-elk-*,查看下日志記錄,是否能夠正常加載的項目日志,也是妥妥沒有問題的。

img

img

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM