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 还有不少强大的功能,此处仅展现最基本的使用,仅供参考。