【导读】go服务端应用日志分析有什么好实践?本文中作者介绍了基于ELK体系和Zap日志库的日志采集和分析实践。
Zap 是 Uber 开源的一款高性能日志工具。
本篇文章实现的 ELK 架构如下图,通过定制化 Zap 实现多输出源,同时将日志输出到 Console (Standard IO) 与 MQ 中,再配置 Logstash Input 使其读取 MQ 中的日志并写入 ES 中,最后在 Kibana 中展示。
本文使用 Redis 作为 MQ 实现,可替换为其他 MQ。
笔者理解的 zap logger 组件如下图,通过创建不同的 zapcore 可以实现多格式,多级别的日志输出。
我们首先创建 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"
这个 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 进入。
输入正则使匹配 logstash
选择 timestamp 作为 filter
创建后回到 Discover 页面
调用 logger 输出日志,可以在 Kibana 上看到。
Zap 和 Elk 还有很多强大的功能,此处仅展示最基本的使用,仅供参考。
转自:F1renze
juejin.cn/post/6844904039793033223
- EOF -
Go 开发大全
参与维护一个非常全面的Go开源技术资源库。日常分享 Go, 云原生、k8s、Docker和微服务方面的技术文章和行业动态。
关注后获取
回复 Go 获取6万star的Go资源库
分享、点赞和在看
支持我们分享更多好文章,谢谢!