在我們的很多 Java 應用開發中,我們有通常很多的日誌信息。這些日誌信息對我們的開發非常有用。它可以幫我們對我們的應用的運行情況進行分析,特別是在應用運行出現錯的時候。在傳統的開發中,我們可能會使用一些編輯器來查看這些日誌。這對於一個這樣的應用是可行的,但是假設我們有多個正在運行的應用程序,並且所有這些應用程序都生成日誌。 如果必須手動分析日誌,則需要遍歷所有日誌文件。 這些可能會變成數百個。在今天的教程中,我們將介紹如何使用 Elastic Stack 來查詢分析我們的日誌。
我們的整個系統如上圖所示:
- 我們有一個 Spring boot 的應用,它會生成日誌信息
- 使用 Logstash 把日誌信息導入到 Elasticsearch 中
- Elasticsearch 被用於存儲及分析日誌信息
- Kibana 將被使用爲可視化及查詢日誌工具
安裝
Elasticsearch
我們可參考我之前的文章“如何在Linux,MacOS及Windows上進行安裝Elasticsearch”來安裝我們的Elasticsearch。我們可以不需要修改任何的配置文件,並在本機上運行。
Kibana
我們可以參考我之前的文章“如何在Linux,MacOS及Windows上安裝Elastic棧中的Kibana”來進行我們的安裝。並在本機上運行。如果 Elasticsearch 及 Kibana 都運行正常的話,那麼我們可以看到 Kibana 的界面:
Logstash
我們可以參考我之前的文章“如何安裝Elastic棧中的Logstash”來安裝 Logstash。我們先不運行Logstash。
這樣我們就基本安裝好了所需要的 Elastic Stack 組件。接下來,我們將創建一個簡單的 Spring boot 應用。
創建 Spring boot 應用
我們使用 Eclipse 來創建一個 Spring boot 的應用:
點擊 Finish 按鈕。這樣就完成了一個最基本的 Maven 應用的框架。我們接着修改項目中的 pom.xml 文件:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.liuxg</groupId>
<artifactId>boot-elastic</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-elastic</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在上面我們加入了項目所必須的一些 dependence。
接下來,我們創建一個 SpringBootApplication 的 class。
點擊 Finish 按鈕。這樣就創建了我們的 ElasticSpringBootApplication class。我們進一步修改這個 class:
ElasticSpringBootApplication.java
ackage com.liuxg;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ElasticSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(ElasticSpringBootApplication.class, args);
}
}
接下來定義控制器以公開REST API。 我們將利用這些調用將內容寫入日誌文件。我們創建另外一個叫做 ElasticController 的 class。我們可以仿照上面的方法來創建這個 class:
ElasticController.java
package com.liuxg;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
class ElasticController {
private static final Logger LOG = Logger.getLogger(ElasticController.class.getName());
@Autowired
RestTemplate restTemplete;
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
@RequestMapping(value = "/elk")
public String helloWorld() {
String response = "Welcome to JAVA " + new Date();
LOG.log(Level.INFO, response);
return response;
}
@RequestMapping(value = "/exception")
public String exception() {
String response = "";
try {
throw new Exception("Opps Exception raised....");
} catch (Exception e) {
e.printStackTrace();
LOG.error(e);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String stackTrace = sw.toString();
LOG.error("Exception - " + stackTrace);
response = stackTrace;
}
return response;
}
}
在上面 我們定義了一個叫做 /elk 和 /exception 兩個 REST 接口。它們分別生產相應的 Log 到文件裏去。我們接下來定義我們的l og 文件地址:
點擊 Finish 按鈕。我們接着把文件的內容寫爲:
logging.file=/Users/liuxg/tmp/spring-boot-elastic.log
注意:這個 log 文件的路徑依賴於你自己想要的路徑不同而不同。上面是在我自己電腦上的路徑。
保存上面的 application.propertiese 文件。我們接下來運行這個應用:
我們在 Eclipse 的 console 裏可以看到如下的信息:
也就是我們的微服務運行於 localhost:8080 端口:
我們測試 REST AP 的兩個接口:
我們接下來查看在我們上面配置的 log 文件裏的內容:
$ pwd
/Users/liuxg/tmp
liuxg:tmp liuxg$ vi spring-boot-elastic.log
在上面,我們可以看到已經生產了許多的 log 了。其中 exception 所生成的日誌裏面含有 "at" 的字樣。在我們上傳這個日誌信息的時候,我們需要把這個日誌當做一個整體作爲一個文檔進行上傳,而不是每一行含有 at 的語句分別當做一個文檔上傳。
配置 Logstash
上面我已經生產了相應的日誌文件了。接下來,我們將配置 Logstash。如果你想了解更多關於 Logstash 方面的知識,請參閱我之前的文章“Logstash:Data轉換,分析,提取,豐富及核心操作” 及文章“如何安裝Elastic棧中的Logstash”。
我們在 Logstash 的安裝目錄下,創建如下的 配置文件:
logstash.conf
input {
file {
type => "java"
path => "/Users/liuxg/tmp/spring-boot-elastic.log"
codec => multiline {
pattern => "^%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME}.*"
negate => "true"
what => "previous"
}
start_position => "beginning"
sincedb_path => "/dev/null"
}
}
filter {
#If log line contains tab character followed by 'at' then we will tag that entry as stacktrace
if [message] =~ "\tat" {
grok {
match => ["message", "^(\tat)"]
add_tag => ["stacktrace"]
}
}
}
output {
stdout {
codec => rubydebug
}
# Sending properly parsed log events to elasticsearch
elasticsearch {
hosts => ["localhost:9200"]
}
}
在上面我們通過:
codec => multiline {
pattern => "^%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME}.*"
negate => "true"
what => "previous"
}
把多行的含有 at 的 stack trace 的日誌變爲一個文檔,而不是多個文檔。關於這個 multiline 的介紹可以參閱我之前的文章“Beats:使用Filebeat傳送多行日誌”。雖然那個是針對 Filebeat 的,但是基本原理是完全一樣的。
在 filter 的部分:
filter {
#If log line contains tab character followed by 'at' then we will tag that entry as stacktrace
if [message] =~ "\tat" {
grok {
match => ["message", "^(\tat)"]
add_tag => ["stacktrace"]
}
}
}
如果有一行的日誌含有一個 tab (\t) 及 at 字符爲首,那麼這個信息將被標記爲 stacktrace。這個便於我們以後在 Kibana 中進行搜索。
最後,在 output 的部分,我們需要填入相應的 elasticsearch 的地址。
我們可以使用如下的命令來測試我們的 logstash.conf 是否正確:
bin/logstash --config.test_and_exit -f logstash.conf
上面顯示是正確的。
我們可以接着使用如下的命令來啓動 logstash:
sudo ./bin/logstash -f logstash.conf
這個時候,我們可以在運行 Logstash 的屏幕上看到很多被處理的 log 信息。
我們打開 Kibana,並運行如下的命令:
GET _cat/indices
我們可以看到有一個叫做 logstash 爲開頭的日誌文件出現了。我們可以通過如下的命令來檢查它的日誌的內容:
GET logstash/_search
我們也可以通過 Discover 來對我們的日誌進行搜索。爲此,我們需要創建一個index pattern:
點擊 Create index pattern:
點擊 Next step:
點擊 Create index pattern。然後,我們點擊 Discover:
在這裏我們可以看到我們在之前生成的一些日誌:
我們也可以繼續在瀏覽器中使用接口 /elk 及 /exception 來生產相應的 log 並在 Kibana 中進行查看:
如果大家對這個測試應用感興趣,請在地址下載:https://github.com/liu-xiao-guo/spring-boot-elastic-logs