Zap 是 Uber 開源的一款高性能日誌工具。git
本篇文章實現的 ELK 架構以下圖,經過定製化 Zap 實現多輸出源,同時將日誌輸出到 Console (Standard IO) 與 MQ 中,再配置 Logstash Input 使其讀取 MQ 中的日誌並寫入 ES 中,最後在 Kibana 中展現。github
本文使用 Redis 做爲 MQ 實現,可替換爲其餘 MQ。redis
筆者理解的 zap logger 組件以下圖,經過建立不一樣的 zapcore 能夠實現多格式,多級別的日誌輸出。docker
咱們首先建立 logger,其只有一個輸出到 console 的 zapcore。json
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")) } 複製代碼
日誌輸出格式:bash
{"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
。markdown
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" 複製代碼
這個 repo 包含了一套很是方便搭建的 elk 環境,使用 docker 與 docker-compose 直接啓動便可,而且支持經過 yml 修改配置。tcp
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 進入。
輸入正則使匹配 logstash
選擇 timestamp 做爲 filter
建立後回到 Discover 頁面
調用 logger 輸出日誌,能夠在 Kibana 上看到。
Zap 和 Elk 還有不少強大的功能,此處僅展現最基本的使用,僅供參考。