项目场景
本人在springcloud项目中引入了logstash的依赖然后通过logback往logstash里写: 如下
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>5.3</version>
</dependency>
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<!--可以访问的logstash日志收集端口-->
<destination>logstash的ip:4560</destination>
<!-- 此处配置logstash的索引名称,在logstash.conf中配置了使用appname作为索引名称的一部分 -->
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"appname":"back"}</customFields>
</encoder>
</appender>
<!--root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
不能设置为INHERITED或者同义词NULL。默认是DEBUG
可以包含零个或多个元素,标识这个appender将会添加到这个logger-->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="APPLICATION" />
<appender-ref ref="LOGSTASH"/>
</root>
问题描述
突然有一天晚上离奇的反馈说所有的接口均出现各种超时现象, 如下(部分超时截图)
接着我打开云厂商管理页面查看CPU和内存和连接数和慢sql指标均正常,就特别离谱,我不知道是不是见鬼了。
唯一有个不正常的就是我搭建的ELK里面的es挂了,紧接着我联系云厂商的客服询问的同时把这个es重新启动,超时立马就没有了。
过了一段时间收到云厂商的回复,告知说这段时间云厂商服务器的网络设备有异常,已紧急修复。
看他回复的修复时间正好是我es的重启时间,我不相信什么巧合,我觉得这可能就是logstash等待es连接超时导致的问题,但是这次既然已经甩锅给云厂商那我就顺着台阶下了哈哈哈。
这个小疑问也只有我自己知道。
解决方案:
这个事情结束时候,我知道还是得有个后续的处理措施。
这边准备把收集日志跟项目做一下解耦, 不采取logback发送日志给logstash的方式。
而且公司给的服务器也抠门,一个2核4G的机器来做一个日志服务器,平时运行一个logstah和es内存就跑满了,这边准备淘汰掉logstash,采用filebeat部署在每台应用服务器上,使用filebeat直接读取日志文件的方式以此达到与java应用之间的一个解耦。
这边采用的也是多个java项目传输到多个es的索引中, filebeat.yml如下:
# filebeat读取文件的配置,可以配置多个
filebeat.inputs:
- type: log
enabled: true
paths:
- /data/api/logs/api.log
fields:
appname: api
fields_under_root: true
# 这里是为了保证多行日志插入到es里面是算一条,是按照时间前缀来将多条整合成一条,可以看下打印的异常栈,异常栈是打印多行,但实际上本身是一行
# 是按照年-月-日的形式进行分割的,也就是上一步骤中的日志形式,这里需要根据自身实际的日志来进行配置。
multiline.type: pattern
multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'
#multiline.pattern: '^\20'
multiline.negate: true
multiline.match: after
- type: log
enabled: true
paths:
- /data/back/logs/back.log
fields:
appname: back
fields_under_root: true
# 这里是为了保证多行日志插入到es里面是算一条,是按照时间前缀来将多条整合成一条,可以看下打印的异常栈,异常栈是打印多行,但实际上本身是一行
# 是按照年-月-日的形式进行分割的,也就是上一步骤中的日志形式,这里需要根据自身实际的日志来进行配置。
multiline.type: pattern
multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'
#multiline.pattern: '^\20'
multiline.negate: true
multiline.match: after
- type: filestream
# Change to true to enable this input configuration.
enabled: false
# Paths that should be crawled and fetched. Glob based paths.
paths:
- /var/log/*.log
# ============================== Filebeat modules ==============================
filebeat.config.modules:
# Glob pattern for configuration loading
path: ${path.config}/modules.d/*.yml
# Set to true to enable config reloading
reload.enabled: false
setup.template.settings:
index.number_of_shards: 1
#index.codec: best_compression
#_source.enabled: false
# This requires a Kibana endpoint configuration.
setup.kibana:
#根据读取文件时传入的字段决定插入哪个索引
# ---------------------------- Elasticsearch Output ----------------------------
output.elasticsearch:
# Array of hosts to connect to.
hosts: ["es的ip:9200"]
indices:
- index: "api-%{+yyyy.MM.dd}"
when.contains:
appname: "api"
- index: "back-%{+yyyy.MM.dd}"
when.contains:
appname: "back"
processors:
- script:
lang: javascript
id: my_filter
tag: enable
source: >
function process(event){
var str= event.Get("message");
var time=str.split(" ").slice(0,2).join(" ");
event.Put("start_time",time);
var hostname = event.Get("agent.hostname");
event.Put("hostname", hostname);
}
- timestamp:
field: start_time
timezone: Asia/Shanghai
layouts:
- '2006-01-02 15:04:05'
- '2006-01-02 15:04:05.999'
test:
- '2019-06-22 16:33:51'
- '2019-11-18 04:59:51.123'
- drop_fields:
fields: ["start_time","input", "agent", "ecs", "log", "host"]
# fields: [start_time]
在配置中还把日志进行了一些处理再传送给es。
比如将java日志的开头中的时间戳传入到es里面的timestamp字段,如果不这样的话不方便查询(因为filebeat会把日志的所有内容都推送给es,而时间则是插入es的时间而不是日志的时间)。
还有比如把一些filebeat自动生成的字段给删掉,避免占用过大的存储空间。
经过了这些数据处理就不用再使用logstash进行了一道中间的传输。
关键配置是
processors:
- script:
lang: javascript
id: my_filter
tag: enable
source: >
function process(event){
var str= event.Get("message");
var time=str.split(" ").slice(0,2).join(" ");
event.Put("start_time",time);
var hostname = event.Get("agent.hostname");
event.Put("hostname", hostname);
}
- timestamp:
field: start_time
timezone: Asia/Shanghai
layouts:
- '2006-01-02 15:04:05'
- '2006-01-02 15:04:05.999'
test:
- '2019-06-22 16:33:51'
- '2019-11-18 04:59:51.123'
- drop_fields:
fields: ["start_time","input", "agent", "ecs", "log", "host"]
# fields: [start_time]