引子
最近笔者在项目中用到了opentsdb,用于存放采集数据指标,并提供实时的图表、曲线展示功能。
opentsdb提供了自己的指标收集工具tcollector,故事就要从这里说起。
tcollector解决不了的事
笔者编写了一个抓包程序,用于实时分析MySQL中的请求情况,每10秒会输出一行记录到日志文件中,数据内容包括:
- 时间点
- 查询次数,查询响应时长,查询错误次数,查询影响的行数
- 插入次数,插入响应时长,插入错误次数,插入影响的行数
- ......
总之,指标我放到一个日志文件了,如果用tcollector去做的话,要采集这些指标就非常困难了。
主要的实现思路和要解决的问题有:
- 文件变化通知
- 新的日志文件的发现和通知
- 每个文件上次处理的位置的记录
- 接到文件和目录变化通知后,需要根据文件上次处理的位置来读取新的数据,并输出到opentsdb
- 解决tcollector重启问题,需要持久化每个文件上次处理的位置记录信息
笔者开始确实写了一个版本出来,测试跑起来也没问题,但是批量上线后发现有大量的无法工作,笔者有些气馁了。因为大家都知道,上面的东西都是logstash的强项。
于是决定用logstash来解决这个问题。
logstash解决问题
缺省情况下logstash是没有opentsdb的插件的,需要下载插件并进行安装,我们可以在github上找到logstash-output-opentsdb插件,并按照官方知道的安装方法,把logstash-output-opentsdb插件进行安装。因为logstash的每个官方插件的README文件都介绍了具体安装步骤,笔者这里就不赘述了。
logstash-output-opentsdb插件存在的问题
安装好了插件,笔者于是写好配置文件,启动logstash。
output {
opentsdb {
host => "127.0.0.1"
port => 4242
metrics => [
"%{metris_name}",
"%{value}",
"ip",
"%{ip}",
"port",
"%{port}"
]
}
}
数据果然进到logstash之中了,一起看起来都很简单顺畅,然而,大家可能也想到了一个问题,数据的时间戳呢???,我们并没有输入时间戳,那么插件是否帮我们正确的完成了呢?
这里就不得不检查一下插件的代码,这里我只展示关键的部分:
# The first part of the message
message = ['put',
event.sprintf(name),
event.sprintf("%{+%s}"),
event.sprintf(value),
].join(" ")
从上面的代码我们可以看出,该插件在每次输出metrics到opentsdb时,实时生成时间戳。这显然无法达到笔者的目的,而且在多数据日志抓取中,都会存在日志文件中已经有自己的时间戳,这时候希望输出到opentsdb的数据的时间戳是该日志中的时间戳。否则,一旦因为某些原因logstash重启,需要处理历史日志数据的时候,真实生成的时间戳数据就会与我们期望的大相近庭了。
改造logstash-output-opentsdb插件
从前面的代码看起来,我们除了改造以外,别无他法。
首先,logstash本身自带了@timestamp,而且是一个通用的变量;
其次,logstash提供了各种版本可以修改@timestamp的值;
所以,笔者决定在输出的时候,直接输出@timestamp的值作为opentsdb指标的时间戳。
改动的代码很简单,如下所示:
# The first part of the message
message = ['put',
event.sprintf(name),
event.get("@timestamp").to_i.to_s,
event.sprintf(value),
].join(" ")
当然我们还需要捕获日志中的时间,方法很多,这里列举一个例子:
grok {
match => [ "message", "^%{NUMBER:@timestamp}" ]
}
上述改造后的logstash-output-opentsdbb插件,笔者已经在生产系统使用,同时上传到了笔者的github中,如果不想改造的话,可以直接下载使用。