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

尽管目前的这种方式非常不错,但是细心的开发者可能会发现这种架构很难规模化,如果是针对几十个甚至上百个 log 文件。这是因为 Logstash 对资源的消耗非常大,而目前的这个架构每个 log 文件都需要一个 Logstash 的实例来处理。 在接下的文章 “Elastic:运用 Elastic Stack 分析 Spring boot 微服务日志 (二)” 我们将讲述如果配合 Filebeat 来针对多个 log 文件来进行处理。

猜你喜欢

转载自blog.csdn.net/UbuntuTouch/article/details/106347266