基於 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,LevelEnabler
與 Encoder
不變,通過 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(p []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