Elastic:運用 Elastic Stack 分析 Spring boot 微服務日誌

在我們的很多 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章