概要 帶著問題去看教程: 不是用logstash來監聽我們的日誌,我們可以使用logback配置來使用TCP appender通過TCP協議將日誌發送到遠程Logstash實例。 我們可以使用Logstash指向多個日誌文件。 我們可以在logstash配置文件中使用更複雜的過濾器,以根據需要執行更 ...
概要
帶著問題去看教程:
-
不是用logstash來監聽我們的日誌,我們可以使用logback配置來使用TCP appender通過TCP協議將日誌發送到遠程Logstash實例。
-
我們可以使用Logstash指向多個日誌文件。
-
我們可以在logstash配置文件中使用更複雜的過濾器,以根據需要執行更多操作。
-
我們可以使用遠程ELK集群指向我們的日誌文件,或者將日誌推入,這在將所有應用程式部署到雲中時基本上是必需的。
-
在logstash中創建不同的索引模式。
通過使用微服務,我們已經能夠剋服許多遺留問題,並且它允許我們創建穩定的分散式應用程式,並對代碼,團隊規模,維護,發佈周期,雲計算等進行所需的控制。但它也引入了一些挑戰。其他領域,例如分散式日誌管理和查看在許多服務中分佈的完整事務的日誌和一般的分散式調試的能力。
實際上,挑戰在於微服務是相互隔離的,它們不共用公共資料庫和日誌文件。隨著微服務數量的增加以及我們使用自動化持續集成工具實現雲部署,當我們遇到任何問題時,非常有必要對組件進行一些調試。
感謝開源運動。我們已經擁有了一系列工具,如果一起使用可以發揮魔力。一組如此受歡迎的工具是Elastic Search,Logstash和Kibana - 一起稱為ELK Stack 。它們用於實時搜索,分析和可視化日誌數據。
ELK Stack
-
Elasticsearch是一個基於JSON的分散式搜索和分析引擎,專為水平可擴展性,最高可靠性和易管理性而設計。
-
Logstash是一個動態數據收集管道,具有可擴展的插件生態系統和強大的Elasticsearch協同作用。
-
Kibana通過UI 提供數據可視化。
ELK Stack架構
Logstash根據我們設置的過濾條件處理應用程式日誌文件,並將這些日誌發送到Elasticsearch。通過Kibana,我們可以在需要時查看和分析這些日誌。
ELK配置
所有這三個工具都基於JVM,在開始安裝之前,請驗證JDK是否已正確配置。檢查標準JDK 1.8安裝,JAVA_HOME
並且PATH
已經完成設置。
Elasticsearch
-
從此下載頁面下載最新版本的Elasticsearch 並將其解壓縮到任何文件夾中。
-
bin\elasticsearch.bat
從命令提示符運行。 -
預設情況下,它將從http:// localhost:9200
Kibana
-
從下載頁面下載最新的發行版並解壓縮到任何文件夾中。
-
config/kibana.yml
在編輯器中打開並設置elasticsearch.url
為指向您的Elasticsearch實例。在我們的例子中,我們將使用本地實例取消註釋elasticsearch.url: "http://localhost:9200"
-
bin\kibana.bat
從命令提示符運行。 -
成功啟動後,Kibana將啟動預設埠
5601
,Kibana UI將在http:// localhost:5601上提供
Logstash
-
從下載頁面下載最新的發行版並解壓縮到任何文件夾中。
-
logstash.conf
根據配置說明創建一個文件。在實際演示時間內,我們將再次進行精確配置。現在運行
bin/logstash -f logstash.conf
以啟動logstash
ELK堆棧未啟動並正在運行。現在我們需要創建一些微服務並指向API日誌路徑的logstash。
創建微服務
創建Spring Boot項目
讓我們使用spring boot創建一個應用程式,以縮短開發時間。請按照這些步驟啟動此服務。
添加REST端點
添加一個RestController
類會暴露一些端點一樣/elk
,/elkdemo
,/exception
。實際上我們只會測試幾個日誌語句,因此可以根據您的選擇隨意添加/修改日誌。
package com.example.howtodoinjava.elkexamplespringboot;
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.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ElkExampleSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(ElkExampleSpringBootApplication. class , args);
}
}
@RestController
class ELKController {
private static final Logger LOG = Logger.getLogger(ELKController. class .getName());
@Autowired
RestTemplate restTemplete;
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
@RequestMapping (value = "/elkdemo" )
public String helloWorld() {
String response = "Hello user ! " + new Date();
LOG.log(Level.INFO, "/elkdemo - > " + response);
return response;
}
@RequestMapping (value = "/elk" )
public String helloWorld1() {
String response = restTemplete.exchange( "http://localhost:8080/elkdemo" , HttpMethod.GET, null , new ParameterizedTypeReference() {
}).getBody();
LOG.log(Level.INFO, "/elk - > " + response);
try {
String exceptionrsp = restTemplete.exchange( "http://localhost:8080/exception" , HttpMethod.GET, null , new ParameterizedTypeReference() {
}).getBody();
LOG.log(Level.INFO, "/elk trying to print exception - > " + exceptionrsp);
response = response + " === " + exceptionrsp;
} catch (Exception e) {
// exception should not reach here. Really bad practice :)
}
return response;
}
@RequestMapping (value = "/exception" )
public String exception() {
String rsp = "" ;
try {
int i = 1 / 0 ;
// should get exception
} catch (Exception e) {
e.printStackTrace();
LOG.error(e);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String sStackTrace = sw.toString(); // stack trace as a string
LOG.error( "Exception As String :: - > " +sStackTrace);
rsp = sStackTrace;
}
return rsp;
}
}
|
配置Spring引導記錄
application.properties
在resources
文件夾下打開並添加以下配置條目。
logging.file=elk-example.log
spring.application.name = elk-example
|
驗證微服務生成的日誌
通過瀏覽http:// localhost:8080 / elk,mvn clean install
使用命令java -jar target\elk-example-spring-boot-0.0.1-SNAPSHOT.jar
和測試來執行最終的maven構建並啟動應用程式。
不要害怕看到屏幕上的大堆棧跟蹤,因為有意識地看看ELK如何處理異常消息。
轉到應用程式根目錄並驗證是否elk-example.log
已創建日誌文件,並對端點執行幾次訪問,並驗證日誌文件中是否添加了日誌。
Logstash配置
我們需要創建一個logstash配置文件,以便它監聽日誌文件並將日誌消息推送到彈性搜索。以下是示例中使用的logstash 配置,請根據您的設置更改日誌路徑。
input {
file {
type => "java"
path => "F:/Study/eclipse_workspace_mars/elk-example-spring-boot/elk-example.log"
codec => multiline {
pattern => "^%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME}.*"
negate => "true"
what => "previous"
}
}
}
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" ]
}
}
grok {
match => [ "message" ,
"(?<timestamp>%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME}) %{LOGLEVEL:level} %{NUMBER:pid} --- \[(?<thread>[A-Za-z0-9-]+)\] [A-Za-z0-9.]*\.(?<class>[A-Za-z0-9#_]+)\s*:\s+(?<logmessage>.*)" ,
"message" ,
"(?<timestamp>%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME}) %{LOGLEVEL:level} %{NUMBER:pid} --- .+? :\s+(?<logmessage>.*)"
]
}
date {
match => [ "timestamp" , "yyyy-MM-dd HH:mm:ss.SSS" ]
}
}
output {
stdout {
codec => rubydebug
}
# Sending properly parsed log events to elasticsearch
elasticsearch {
hosts => [ "localhost:9200" ]
}
}
|
Kibana配置
在查看Kibana中的日誌之前,我們需要配置索引模式。我們可以配置logstash-*
為預設配置。我們總是可以在logstash端更改此索引模式併在Kibana中進行配置。為簡單起見,我們將使用預設配置。
索引模式管理頁面如下所示。通過這種配置,我們將Kibana指向您選擇的Elasticsearch索引。Logstash使用名稱模式創建索引。logstash-YYYY.MM.DD
我們可以在Kibana控制台http:// localhost:5601 / app / kibana中執行所有這些配置,然後轉到左側面板中的Management鏈接。
驗證ELK
現在,當所有組件都啟動並運行時,讓我們驗證整個生態系統。
轉到應用程式並測試端點幾次以便生成日誌,然後轉到Kibana控制台,看看日誌是否正確堆疊在Kibana中,還有許多額外的功能,比如我們可以過濾,查看內置的不同圖表等。
以下是Kibana中生成的日誌的視圖。