golang 脚本实时监控错误日志
对于日志的监控分析, ELK 依然已经成为业界标准了。其中L代表Logstash, 用来采集日志。E代表Elasticsearch,用来存储检索分析。K是Kibana用来显示。这一套日志分析监控系统非常强大。
若我们的需求非常简单,例如,要实时监控MySQL,MongoDB等的日志文件, 实时监控 error log。 若出现了error log, 马上报警。
若是需求简单,没必要安装大块头的ELK 情况下, 我们也可以自己写代码来实现对日志的实时监控。
基本思路如下:
主进程定时采集新增日志,比如每秒采集一次,因为采集的是新增日志,所以即使很大(例如,几GB或者几十GB)也不影响性能。当然,还要记录上次读取的位置。
一个go routinue负责对新增日志的过滤,用来搜索过滤是否有error 日志
一个go routinue负责报警,发邮件发钉钉发微信等
主线程核心代码如下:
这里使用了两个循环。 内层循环, 每次新增的日志信息, 内层循环每次读取一行,使用通道传输给日志过滤的go routinue。
外层循环,负责每秒钟读取一次新增的日志。seek, err := f.Seek(i, 0) 负责每次读取新增的日志,i是上次读取到的日志偏移量。 finfo.Size() < i 判断是负责检测日志是否切割了。 因为每天切割一次日志,生成一个新的日志文件。若当前日志的总大小小于记录的偏移量, 则认为这是一个新日志。
for {
f, err := os.Open("D:\\project\\golang\\MyIncrLog\\1.log")
finfo, err := f.Stat()
//fmt.Println("file size:",finfo.Size()," offset:",i)
if err != nil {
fmt.Println(err)
}
if finfo.Size() < i {
i = 0
}
if err != nil {
fmt.Println(err)
}
seek, err := f.Seek(i, 0)
if err != nil {
fmt.Println(seek, err)
}
rd := bufio.NewReader(f)
for {
line, err := rd.ReadString('\n')
if err != nil || io.EOF == err {
if io.EOF != err {
fmt.Println(err)
}
break
}
in <- line
n := len([]byte(line))
i = i + int64(n)
time.Sleep(time.Millisecond * 100)
}
time.Sleep(time.Second)
f.Close()
}
下面的代码是过滤error 日志的go routiune。这个函数返回两个通道,主进程写数据到in 通达以后,该go routinue马上启动去过滤该日志,并根据结果情况,确定是否输入到out 通道中。
func Match() (chan<- string, <-chan string) {
in := make(chan string)
out := make(chan string)
go func() {
for {
select {
case s:=<-in:
if strings.Contains(s, "[Error]") {
out <- s
}
}
}
}()
return in, out
}
下面的代码是负责接收out 通道里面的值, 然后调用send函数去处理。send函数里面,我们可以根据我们的需要去报警, 发邮件发钉钉发微信等等都可以。
go func(s send.Sender, out <-chan string) {
for {
err := s.Send(<-out)
if err != nil {
return
}
}
}(send.Faker{}, out)
func (f Faker) Send(msg string) error {
fmt.Print(msg)
// 发邮件,发微信,发钉钉
return nil
}
运行效果如下。只需要把日志地址和过滤关键词做成配置文件, 就可以对我们所需要的日志进行监控, 结果可以按照我们的需要发邮件发微信发钉钉等。
