轉載自: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 環境搭建
我們可以去 官網 分別下載系統對應最新版 Elasticsearch
、Logstash
、Kibana
,截止目前已更新到 7.1.0
版本,可通過以下鏈接獲取安裝包並提供默認配置啟動步驟。
這里我先按照默認配置啟動 Elasticsearch
服務,啟動完畢,本地可以通過 http://127.0.0.1:9200
地址訪問服務是否啟動正常。注意:先不啟動 Logstash
和 Kibana
,因為他們的配置需要更改,下邊會講到。
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
的吞吐量是 Log4j1
和 Logback
的 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 頁面展示出來。需要指定 host
、port
、protocol
,這里就配置成本地 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
支持配置完畢,接下來,我們需要配置 Logstash
和 Kibana
,在 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
格式索引。
OK,顯示已存在,那么接下來我們在 Kibana
索引模式下創建索引模式,輸入 log4j2-*
即可正確匹配到 Elasticsearch
中的指定的索引,接着在時間篩選字段名稱處選擇 @timestamp
,方便我們后邊按照時間段篩選數據,創建過程如下:
創建完畢,我們就可以在 Kibana
中篩選並顯示日志了,比如我增加了 message
字段,過濾完后,就顯示出來上邊工程示例代碼中的各種類型日志以及異常日志了,非常直觀方便!
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
Elasticsearch
和 Kibana
不需要重啟,再次啟動 Spring Boot 工程,去 Kibana 下查看 !查看下 Elasticsearch
索引管理里面,是否已存在上邊配置的 demo-elk-yyyy.MM.dd
格式索引。
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
索引管理,這下就有了。
那么接着建一個索引模式名稱為 demo-elk-*
,查看下日志記錄,是否能夠正常加載的項目日志,也是妥妥沒有問題的。
參考資料