[Go] 基於 Zap 與 ELK 的日誌分析實踐

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

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

本文使用 Redis 做爲 MQ 實現,可替換爲其餘 MQ。redis

image.png

定製化 Zap

筆者理解的 zap logger 組件以下圖,經過建立不一樣的 zapcore 能夠實現多格式,多級別的日誌輸出。docker

image.png

咱們首先建立 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,LevelEnablerEncoder 不變,經過 zapcore.AddSync 能夠將一個實現 io.Writer 接口的對象轉爲 zap 須要的 WriteSyncermarkdown

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 修改配置。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 進入。

image.png

輸入正則使匹配 logstash

image.png

image.png

選擇 timestamp 做爲 filter

image.png

建立後回到 Discover 頁面

image.png

調用 logger 輸出日誌,能夠在 Kibana 上看到。

image.png

總結

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

相關文章
相關標籤/搜索