日志报警:通过钉钉实时通报业务错误
摘要
线上服务产生报错日志,可以第一时间通知到相关的人员可以更快得修复问题,提高服务的可用性。前一篇文件已经实现把所有日志文件收集起来,于是在此只需要获取这份数据,按需要来报警。前言
千辛万苦把所有的日志收集起来,为的就是此时。前面已经实现了把日志收集到 kafka
:使用 filebeat 收集日志到 kafka。
写了一个小小的项目 gladmo/alerting 来实消费 kafka 中的日志文件,匹配中需要的字段时行报警。
当前项目的一些小功能:
- 可为不同项目设置不同报警方式
- 有其它渠道报警的,可自行编写,扩展性高
- 聚合同类型(每 2s/每 20 个)报警
配置说明
配置示例:
[kafka]
host = ["you-kafka-host:9092"]
group = "log-collect-group"
topic = ["log-collect"]
[template]
[template.common]
AccessToken = "47556dea1ece1536fcb069a44a391a443333ff2bb22ed6c120f2c8ff137a594a"
AtMobiles = ["1588888888"]
Fields = ["test.hao.0 as test", "haha"]
[alerting]
[alerting.log]
type = "dingding"
Template = "template.common"
[alerting.alerting]
type = "dingding"
Template = "template.common"
其中 [kafka]
块配置了 kafka
的基础信息,需要的有服务器的地址,消费组,需要消费的 topic
列表。
[template]
块可以创建一些公用的配置,在 [alerting]
块使用 Template = "template.common"
引入。
template
中可以配置 AccessToken
、AtMobiles
、Fields
字段,分别是钉钉的机器人 token
、需要@的用户手机号、需要报警的其它字段。当前报警会报警 json 中的 ts
level
msg
title
字段,如有其它字段需要报警,可使用 Fields
字段进行配置。
[alerting]
块配置了需要报警的哪些服务, 比如 [alerting.log]
中 log
为项目名,一般对应被收集日志文件的父目录名。
项目结构简介
├── Dockerfile
├── LICENSE
├── README.md
├── alerting.go
├── config.toml // 项目配置文件
├── conf // 配置文件处理
├── consumer // 消费日志
├── inbox // 消息处理
├── log // 打印日志
├── notice // 通知渠道及规则
│ ├── channels // 通知方式,目前只有钉钉
│ │ └── dingding // 钉钉通知方式实现
│ └── rule // 配置规则处理
└── vendor // 依赖
项目中需要的配置在 config.toml
中进行配置,如需要通知其它渠道的,可以自行在 notice/channels
中添加实现,基础的数据类型已经在 BaseInfo
结构体中有定义,可以拿到要通知的消息以及一些附属字段。同时欢迎 PR。
核心代码简介
inbox/inbox.go:
func Dispatch() {
// handle message
for line := range ch {
// Get project name from log path.
project := getProjectName(line.LogPath)
// get noticer by project name
projectRule, err := rule.GetNoticer(project)
if err != nil {
// todo new default noticer, notice warn and error.
log.Logger.Info(err.Error())
continue
}
if _, ok := projectRule.Levels[line.Level]; !ok {
continue
}
// New alerting
alert := ¬ice.Alerting{
Project: project,
Message: line.Message,
Level: line.Level,
Count: 1,
Noticer: projectRule.Noticer,
}
notice.Push(alert)
}
}
// getProjectName 输入 /usr/share/filebeat/logs/log.log 返回 logs
func getProjectName(p string) (project string) {
return filepath.Base(filepath.Dir(p))
}
这里的 ch
这个全局 channel
是由 consumer
负责把 kafka
中的数据消费整理后写到这里的。
随后根据日志文件的数据,获取项目名,使用项目名从配置文件中获取项目的报警配置。如未找到,则忽略此条消息。这里返回的主要结构是一个实现了 Noticer
的一个结构体,当前只实现了钉钉,有其它报警渠道的可以实现这个 interface
便可以配置到配置文件中去,实现其它的报警渠道就是这么简单。
当前消息的报警级别不在配置的报警级别中的,忽略此条消息。注:未配置报警级别的默认 error
。
鉴权完毕后,组装一个 notice.Alerting
的结构体 push
到一个聚合队列中,在这里实现 2s 或 20个一组进行报警。
效果一览
小结
能主动推送日志产生的问题,这感觉太好了,我要把更多的项目接入到这里面来。
接入更多的项目,增加更多的功能。