1. 什么是logStash
ELK(Elasticsearch+Logstash+Kibana)
到底Logstash是什么呢?官方说明
官方文字说明:Logstash 是开源的服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到您最喜欢的“存储库”中。
通俗说明:Logstash是一款强大的数据处理工具,常用作日志处理。
到目前为止,Logstash已经有超过200个可用的插件,以及创建和贡献自己的灵活性。社区生态非常完善,对于我们可以放心的使用。
2. 为什么使用logStash
通常当系统发生故障时,工程师需要登录到各个服务器上,使用 grep / sed / awk 等 Linux 脚本工具去日志里查找故障原因。在没有日志系统的情况下,首先需要定位处理请求的服务器,如果这台服务器部署了多个实例,则需要去每个应用实例的日志目录下去找日志文件。每个应用实例还会设置日志滚动策略(如:每天生成一个文件),还有日志压缩归档策略等。
这样一系列流程下来,对于我们排查故障以及及时找到故障原因,造成了比较大的麻烦。因此,如果我们能把这些日志集中管理,并提供集中检索功能,不仅可以提高诊断的效率,同时对系统情况有个全面的理解,避免事后救火的被动。
所以日志集中管理功能就可以使用ELK技术栈进行实现。Elasticsearch只有数据存储和分析的能力,Kibana就是可视化管理平台。还缺少数据收集和整理的角色,这个功能就是Logstash负责的。
3.Logstash工作原理
- Data Source
Logstash 支持的数据源有很多。例如对于日志功能来说只能能有日志记录和日志传递功能的日志都支持,Spring Boot中默认推荐logback支持日志输出功能(输出到数据库、数据出到文件)。
- Logstash Pipeline
整个整体就是Logstash的功能。
在Logstash中包含非常重要的三个功能:
- Input
输入源,一般配置为自己监听的主机及端口。DataSource向指定的ip及端口输出日志,Input 输入源监听到数据信息就可以进行收集。
- Filter
过滤功能,对收集到的信息进行过滤(额外处理),也可以省略这个配置(不做处理)
- Output
把收集到的信息发送给谁。在ELK技术栈中都是输出给Elasticsearch,后面数据检索和数据分析的过程就给Elasticsearch了。
最终效果:通过整体步骤就可以把原来一行日志信息转换为Elasticsearch支持的Document形式(键值对形式)的数据进行存储。
4.docker安装logStash
- 拉取logStash镜像
docker pull logstash:7.7.0
- 启动容器
docker run -it -p 4560:4560 --name logstash -d logstash:7.7.0
- 修改配置
进入容器
docker exec -it logstash /bin/bash
修改配置文件
vi /usr/share/logstash/config/logstash.yml
把ip修改成elasticsearch访问地址IP
- 修改输入输出配置
继续在容器命令行输入
vi /usr/share/logstash/pipeline/logstash.conf
配置解释说明:
input:接收日志输入配置
tcp: 协议
mode: logstash服务
port:端口,自己指定。默认4560
output:日志处理输出
elasticsearch: 交给es处理
action:es中index命令。也就是新增命令。
hosts:es的主机
index:存储日志的索引。如果不存在可以自动创建。默认的type名称为doc
- 重启容器
退出容器命令行,进入到Linux终端,重启logstash容器。
docker restart logstash
5.使用logback想logStash中输出日志
添加logStash依赖
<!-- logstash依赖-->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
导入logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--该日志将日志级别不同的log信息保存到不同的文件中 -->
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<springProperty scope="context" name="springAppName"
source="spring.application.name" />
<!-- 日志在工程中的输出位置 -->
<property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}" />
<!-- 控制台的日志输出样式 -->
<property name="CONSOLE_LOG_PATTERN"
value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<!-- 日志输出编码 -->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- logstash远程日志配置-->
<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>192.168.8.128:4560</destination>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="console" />
<appender-ref ref="logstash" />
</root>
</configuration>
启动项目后,在kibanna中查看日志信息是否写入到elasticsearch中
6.在Kibanna中查看日志信息
输入: GET test_log/_search 查看全部。
7.搭建日志系统
给定简单需求:
搭建日志系统,提供查询Elasticsearch中日志信息的接口。(默认查询进15分钟内的日志信息)
7.1 修改pom.xml文件
<dependencies>
<!-- springDateElasticsearch依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- test依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- logstash依赖-->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
7.2 修改application.yml配置文件
spring:
elasticsearch:
rest:
uris: http://192.168.8.128:9200
7.3 创建实体
根据kibana中查看到日志信息可以得出看出,除了message是类类型,里面包含一些其他属性外,其他的属性都是简单类型属性。
注意@version和@timestamp在Spring data Elasticsearch 3.x中要使用@JsonProperty进行接收。
@version和@timestamp在spring data elasticsearch 4.x中可以直接使用@Field的name属性进行映射。
时间类型要使用format属性进行转换。不要使用custom方式。
创建LogPojo实体类
@Data
@Document(indexName = "test_log")
public class LogPojo {
@Id
private String id;
@Field(type = FieldType.Keyword)
private String host;
@Field(type = FieldType.Long)
private Long port;
@Field(type = FieldType.Text)
private String message;
@Field(type = FieldType.Date,name = "@timestamp",format = DateFormat.date_time)
private Date timestamp;
@Field(type = FieldType.Text,name = "@version")
private String version;
// 不与ES中的属性对应。当自己业务中需要日志内容时
private MessagePojo messagePojo;
}
创建MessagePojo实体类(用于细化message字段的日志信息)
@Data
public class MessagePojo {
@JsonProperty("@timestamp")
private Date timestamp;
@JsonProperty("@version")
private String version;
private String message;
private String logger_name;
private String thread_name;
private String level;
private String level_value;
}
7.4 新建service及实现类
public interface DemoService {
List<LogPojo> demo();
}
@Service
@Slf4j
public class DemoServiceImpl implements DemoService {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Override
public List<LogPojo> demo() {
// 获取当前时间
Calendar instance = Calendar.getInstance();
instance.add(Calendar.MINUTE,-15);
// 查询近15分钟的日志信息
Query query = new NativeSearchQuery(QueryBuilders.rangeQuery("@timestamp").gte(instance));
// 分页
query.setPageable(PageRequest.of(1,30));
SearchHits<LogPojo> search = elasticsearchRestTemplate.search(query, LogPojo.class);
log.error("查询结果总条数为: "+ search.getTotalHits());
List<SearchHit<LogPojo>> searchHits = search.getSearchHits();
List<LogPojo> list = new ArrayList<>();
searchHits.forEach(hits->{
LogPojo logPojo = hits.getContent();
String message = logPojo.getMessage();
ObjectMapper objectMapper = new ObjectMapper();
MessagePojo mp = null;
try {
mp = objectMapper.readValue(message, MessagePojo.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
logPojo.setMessagePojo(mp);
list.add(logPojo);
});
return list;
}
}
7.5 新建控制器
@RestController
public class DemoController {
@Autowired
private DemoService demoService;
/**
* 获取es中最近15分钟的日志
* @return
*/
@RequestMapping("/")
public List<LogPojo> demo(){
return demoService.demo();
}
}
7.6测试结果
在浏览器输入: http://127.0.0.1:8080/
会看见下面的结果。