基於 Golang Zap 與 ELK 的日誌分析實踐

【導讀】go 服務端應用日誌分析有什麼好實踐?本文中作者介紹了基於 ELK 體系和 Zap 日誌庫的日誌採集和分析實踐。

Zap 是 Uber 開源的一款高性能日誌工具。

本篇文章實現的 ELK 架構如下圖,通過定製化 Zap 實現多輸出源,同時將日誌輸出到 Console (Standard IO) 與 MQ 中,再配置 Logstash Input 使其讀取 MQ 中的日誌並寫入 ES 中,最後在 Kibana 中展示。

本文使用 Redis 作爲 MQ 實現,可替換爲其他 MQ。

image.png

定製化 Zap

筆者理解的 zap logger 組件如下圖,通過創建不同的 zapcore 可以實現多格式,多級別的日誌輸出。

image.png

我們首先創建 logger,其只有一個輸出到 console 的 zapcore。

func NewLogger() *zap.Logger {
 // 限制日誌輸出級別, >= DebugLevel 會打印所有級別的日誌
 // 生產環境中一般使用 >= ErrorLevel
 lowPriority := zap.LevelEnablerFunc(func(lv zapcore.Level) bool {
  return lv >= zapcore.DebugLevel
 })

 // 使用 JSON 格式日誌
 jsonEnc := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
 stdCore := zapcore.NewCore(jsonEnc, zapcore.Lock(os.Stdout), lowPriority)

 // logger 輸出到 console 且標識調用代碼行
 return zap.New(stdCore).WithOptions(zap.AddCaller())
}

func main() {
 logger := NewLogger()

 logger.Info("test logger info", zap.String("hello""logger"))
}
複製代碼

日誌輸出格式:

{"level":"info","ts":1578372154.69565,"caller":"hello/main.go:28","msg":"test logger info","hello":"logger"}

接下來添加同步日誌到 redis 的 zapcore,LevelEnablerEncoder 不變,通過 zapcore.AddSync 可以將一個實現 io.Writer 接口的對象轉爲 zap 需要的 WriteSyncer

import (
 "os"

 "github.com/go-redis/redis"
 "go.uber.org/zap"
 "go.uber.org/zap/zapcore"
)

func NewRedisWriter(key string, cli *redis.Client) *redisWriter{
 return &redisWriter{
  cli: cli, listKey: key,
 }
}
// 爲 logger 提供寫入 redis 隊列的 io 接口
type redisWriter struct {
 cli *redis.Client
 listKey string
}

func (w *redisWriter) Write([]byte) (int, error) {
 n, err := w.cli.RPush(w.listKey, p).Result()
 return int(n), err
}

func NewLogger(writer *redisWriter) *zap.Logger {
 // 限制日誌輸出級別, >= DebugLevel 會打印所有級別的日誌
 // 生產環境中一般使用 >= ErrorLevel
 lowPriority := zap.LevelEnablerFunc(func(lv zapcore.Level) bool {
  return lv >= zapcore.DebugLevel
 })

 // 使用 JSON 格式日誌
 jsonEnc := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
 stdCore := zapcore.NewCore(jsonEnc, zapcore.Lock(os.Stdout), lowPriority)

 // addSync 將 io.Writer 裝飾爲 WriteSyncer
 // 故只需要一個實現 io.Writer 接口的對象即可
 syncer := zapcore.AddSync(writer)
 redisCore := zapcore.NewCore(jsonEnc, syncer, lowPriority)

 // 集成多個 core
 core := zapcore.NewTee(stdCore, redisCore)

 // logger 輸出到 console 且標識調用代碼行
 return zap.New(core).WithOptions(zap.AddCaller())
}

func main() {
 cli := redis.NewClient(&redis.Options{
  Addr: "127.0.0.1:6379",
 })
 writer := NewRedisWriter("log_list", cli)
 logger := NewLogger(writer)

 logger.Info("test logger info", zap.String("hello""logger"))
}

在 Redis 中查看是否 push:

127.0.0.1:6379> lpop log_list
"{\"level\":\"info\",\"ts\":1578373888.516468,\"caller\":\"hello/main.go:58\",\"msg\":\"test logger info\",\"hello\":\"logger\"}\n"

搭建 elk 環境

這個 repo 包含了一套非常方便搭建的 elk 環境,使用 docker 與 docker-compose 直接啓動即可,並且支持通過 yml 修改配置。

git clone https://github.com/deviantony/docker-elk.git

修改 logstash input ,配置文件位置 docker-elk/logstash/pipeline/logstash.conf

input {
 tcp {
  port =5000
 }
 redis {
  data_type ="list"
  key ="log_list"
  host ="127.0.0.1"
  port =6379
  db =0
  threads =2
 }
}

修改後啓動 elk ,訪問 Kibana (http://localhost:5601),默認用戶名:elastic,密碼:changeme

docker-compose up

首次啓動需要創建 Index Pattern,可以由 Home 右下方 Manage and Administer the Elastic Stack 的 Index Patterns 進入。

image.png

輸入正則使匹配 logstash

image.png

image.png

選擇 timestamp 作爲 filter

image.png

創建後回到 Discover 頁面

image.png

調用 logger 輸出日誌,可以在 Kibana 上看到。

image.png

總結

Zap 和 Elk 還有很多強大的功能,此處僅展示最基本的使用,僅供參考。

轉自:F1renze

juejin.cn/post/6844904039793033223

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/su9tE7sw3CAfXBTrfKngfg