文章目录
1. Beats介绍
Beats是 Elastic 公司开发提供的开源数据收集器集合,只需要将其端(Agent)安装到服务器上,通过简单的配置,就可以将指定的数据发送到 Elasticsearch。Elastic 提供了多种类的 Beats 用来收集不同的数据。其列表如下
数据类型 | Beats 名称 | 资源 |
---|---|---|
审计数据 | Auditbeat | https://www.elastic.co/cn/products/beats/auditbeat |
日志文件 | Filebeat | https://www.elastic.co/cn/products/beats/filebeat |
云端数据 | Functionbeat | https://www.elastic.co/cn/products/beats/functionbeat |
运行时间监控 | Heartbeat | https://www.elastic.co/cn/products/beats/heartbeat |
指标 | Metricbeat | https://www.elastic.co/cn/products/beats/metricbeat |
网络数据 | Packetbeat | https://www.elastic.co/cn/products/beats/packetbeat |
Windows 事件日志 | Winlogbeat | https://www.elastic.co/cn/products/beats/winlogbeat |
Tips:Elastic 公司基于 Go 开发了一个公共组件——libbeat
,该组件提供所有 Beats 使用的基础 API,通过这个 API 可以发送数据到 Elasticsearch、配置输入源(input)参数,实现日志打印等等功能,开发人员可以基于该组件开发符合自己需求的 Beat 数据采集器。
2. 第三方开源 Beats
得益于 Elastic 公司的开源组件libbeat
,开发者们开发了众多的第三方 Beats,用于在特定领域环境下的数据指标收集,该列表包括了许多常用的环境和组件,例如:Kafka、Nginx、Prometheus等等,其详细信息见链接:https://www.elastic.co/guide/en/beats/libbeat/7.0/community-beats.html
3. 开发 Beats
开发语言为 GO,开发基础组件为
libbeat
,需要用到其它开发组件Python、virtualenv
从整体上来讲,一个 Beat 应该包含以下两部分:
- 一个数据收集组件,用来收集数据
- 一个数据推送组件(publisher),用来将收集到的数据推送到指定的输出源(output)
需要注意的是,publisher
已经由libbreat
封装好了,开发者仅需要关心,如何定义事件(也就是收集数据),并将该事件信息发送到 publisher
去。libbeat
提供了一些通用服务,例如:配置管理、日志打印、守护进程(daemonzing)、Windows 服务处理、数据处理等模块。
事件(event)是一个 类似JSON 的对象,用来承载用于发送到publisher
的收集数据,最小的事件信息,应该包括@timestamp
和type
字段,除了这些字段外,还可以包括一些附加字段。下面是一个事件的典型示例:
{
"@timestamp": "2016-07-13T21:33:58.355Z",
"beat": {
"hostname": "mar.local",
"name": "mar.local"
},
"directory": false,
"filename": "winlogbeat.yml",
"filesize": 2895,
"modtime": "2016-07-13T20:56:21.000Z",
"path": "./vendor/github.com/elastic/beats/winlogbeat/winlogbeat.yml",
"type": "lsbeat"
}
{
"@timestamp": "2016-07-13T21:33:58.354Z",
"beat": {
"hostname": "mar.local",
"name": "mar.local"
},
"directory": true,
"filename": "system",
"filesize": 238,
"modtime": "2016-07-13T20:56:21.000Z",
"path": "./vendor/github.com/elastic/beats/winlogbeat/tests/system",
"type": "lsbeat"
}
3.1. 创建项目
- 第一步:拉取 beats 代码
mkdir -p ${GOPATH}/src/github.com/elastic
git clone https://github.com/elastic/beats ${GOPATH}/src/github.com/elastic/beats
git checkout 7.0
- 第二步:建立自己的 beat 项目
mkdir ${GOPATH}/src/github.com/{user}
cd ${GOPATH}/src/github.com/{user}
- 第三步:生成通用代码
python $GOPATH/src/github.com/elastic/beats/script/generate.py
Beat Name [Examplebeat]: Countbeat
Your Github Name [your-github-name]: {username}
Beat Path [github.com/{github id}/{beat name}]:
Firstname Lastname: {Full Name}
- 第四步:拉取依赖数据
cd ${GOPATH}/src/github.com/{user}/countbeat
make setup
注意:执行到这一步,我们自定义的 Beat 已经包含了基本的配置文件countbeat.yml
和模板文件,从某种层度上来说,这个 beat 已建立成功,可以编译执行了。如果想要完善这个 beat 的功能,则需要添加用户自定义的逻辑,以及一些需要额外添加的配置参数信息。
- 第五步:编译文件,生成一个二进制可执行文件 countbeat
mage build
- 第六步:执行
./countbeat -e -d "*"
3.2. Beater接口
每个 beat 都需要实现 libbeat中定义的 Beater 接口
// Beater is the interface that must be implemented by every Beat. A Beater
// provides the main Run-loop and a Stop method to break the Run-loop.
// Instantiation and Configuration is normally provided by a Beat-`Creator`.
//
// Once the beat is fully configured, the Run() method is invoked. The
// Run()-method implements the beat its run-loop. Once the Run()-method returns,
// the beat shuts down.
//
// The Stop() method is invoked the first time (and only the first time) a
// shutdown signal is received. The Stop()-method normally will stop the Run()-loop,
// such that the beat can gracefully shutdown.
type Beater interface {
// The main event loop. This method should block until signalled to stop by an
// invocation of the Stop() method.
Run(b *Beat) error
// Stop is invoked to signal that the Run method should finish its execution.
// It will be invoked at most once.
Stop()
}
- 定义一个 Beat 对象,实现两个方法
Run()
和Stop()
type Countbeat struct {
done chan struct{}
config config.Config
client publisher.Client
...
}
func (bt *Countbeat) Run(b *beat.Beat) error {
...
}
func (bt *Countbeat) Stop() {
...
}
注意: 默认情况下,一个 Beat 对象需要包含以下信息:
done
:Channel used by the Run() method to stop when the Stop() method is called.config
:Configuration options for the Beatclient
:Publisher that takes care of sending the events to the defined output.
注意:每个 Beat 需要实现以下方法:
New
:创建 Beat对象Run
:核心代码,用来收集数据,发送数据Stop
:停止进程
3.2.1. New 函数
New()
函数接收 Beat 的配置参数,创建一个 Beat 对象,New()
函数位于 beater/countbeat.go
文件中。
func New(b *beat.Beat, cfg *common.Config) (beat.Beater, error) {
config := config.DefaultConfig
if err := cfg.Unpack(&config); err != nil {
return nil, fmt.Errorf("Error reading config file: %v", err)
}
bt := &Countbeat{
done: make(chan struct{}),
config: config,
}
return bt, nil
}
- Beat生成器生成的默认代码中,一个最简单的 config 对象位于
config/config.go
package config
import "time"
type Config struct {
Period time.Duration `config:"period"`
}
var DefaultConfig = Config{
Period: 1 * time.Second,
}
- 配置文件位于
countbeat.yml
countbeat:
# Defines how often an event is sent to the output
period: 10s
- 如果想要添加自定义参数,那么需要修改结构体(
config/config.go
)信息,同时在(_meta/beat.yml
)添加对应的参数信息
type Config struct {
Period time.Duration `config:"period"`
Path string `config:"path"`
}
var DefaultConfig = Config{
Period: 1 * time.Second,
Path: ".",
}
countbeat:
period: 10s
path: "."
- 修改完配置信息后执行
make update
让这些配置生效
3.2.2. Run方法
- 主要的处理逻辑
func (bt *Countbeat) Run(b *beat.Beat) error {
logp.Info("countbeat is running! Hit CTRL-C to stop it.")
bt.client = b.Publisher.Connect()
ticker := time.NewTicker(bt.config.Period)
counter := 1
for {
select {
case <-bt.done:
return nil
case <-ticker.C:
}
// 创建一个 event 对象
event := common.MapStr{
// 两必备字段,@timestamp 和 type
"@timestamp": common.Time(time.Now()),
"type": b.Name,
"counter": counter,
}
// 发送数据
bt.client.PublishEvent(event)
logp.Info("Event sent")
counter++
}
}
- 当给事件(envent)中添加字段信息(除去@timestamp 和 type)时,必须同时更新
_meta/fields.yml
配置文件信息
- key: countbeat
title: countbeat
description:
fields:
- name: counter
type: long
required: true
description: >
PLEASE UPDATE DOCUMENTATION
- 修改完配置信息后执行
make update
让这些配置生效
3.2.3. Stop 方法
- 用来停止服务
func (bt *Countbeat) Stop() {
bt.client.Close()
close(bt.done)
}
3.2.4. 主函数
package main
import (
"os"
"github.com/elastic/beats/libbeat/beat"
"github.com/elastic/beats/libbeat/cmd"
"github.com/elastic/beats/libbeat/cmd/instance"
"github.com/kimjmin/countbeat/beater"
)
var RootCmd = cmd.GenRootCmdWithSettings(beater.New, instance.Settings{Name: "countbeat"})
func main() {
if err := RootCmd.Execute(); err != nil {
os.Exit(1)
}
}
3.3. 事件命名规约
- 字段名规约
- 所有字段必须是小写字符
- 复合单次使用下划线连接
- 组参数使用
.
体现组信息,例如:cpu.load和 cpu.system - 避免在字段名中出现重复的命名空间,例如:使用 cpu.load而不是 cpu.cpu_load
- 假如释义不明,选择全称,避免使用简写
- 字段排序遵循从通用到特殊的规律
- 如果两个字段一样,但是单位不同,那么就移除那个不常用的字段,例如:timeout.sec和 timeout.min
- 如果字段名和嵌套命名空间匹配,那么字段名添加.value后缀
- 如果是单一字段,那么不要使用
.
,这代表是一个组参数
10.注意字段名的单数复数形式
3.4. 标准化命名
参数(推荐) | 参数(不推荐) |
---|---|
avg | average |
connection | conn |
count | |
day | days, d |
max | maximum |
min | minimum |
pct | percentage |
request | req |
sec | seconds, second, s |
ms | millisecond, millis |
mb | megabytes |
msg | message |
ns | nanoseconds |
norm | normalized |
us | microseconds |