Zap日志库实战教程

一、zap日志库介绍

Zap是非常快的、结构化的,分日志级别的Go日志库,同时提供了结构化日志记录和printf风格的日志记录。同时提供了两种类型的日志记录器sugared Logger和logger
Zap是由https://github.com/uber-go/zap Uber开源的高性能日志库。

特点:
- 极致性能
- 结构化日志
- JSON格式输出
- 支持日志切割
- 支持日志分级
- 支持多输出
- 适合微服务和K8S环境
默认logger的优缺点
	优势
		它最大的优点是使用非常简单。我们可以设置任何io.Writer作为日志记录输出并向其发送要写入的日志。

	劣势
        仅限基本的日志级别
        	只有一个Print选项。不支持INFO/DEBUG等多个级别。
        对于错误日志,它有Fatal和Panic
        	Fatal日志通过调用os.Exit(1)来结束程序
        	Panic日志在写入日志消息之后抛出一个panic
        	但是它缺少一个ERROR日志级别,这个级别可以在不抛出panic或退出程序的情况下记录错误
        缺乏日志格式化的能力——例如记录调用者的函数名和行号,格式化日期和时间格式。等等。
        不提供日志切割的能力。

二、安装zap日志库

go get -u go.uber.org/zap

日志切割

go get gopkg.in/natefinch/lumberjack.v2

日志等级

Debug
Info
Warn
Error
DPanic
Panic
Fatal

三、创建Logger

开发环境

logger, _ := zap.NewDevelopment()

生产环境

logger, _ := zap.NewProduction()

三、配置zap logger

第一种、配置Logger
Logger
    通过调用zap.NewProduction()/zap.NewDevelopment()或者zap.Example()创建一个Logger。
    上面的每一个函数都将创建一个logger。唯一的区别在于它将记录的信息不同。例如production logger默认记录调用函数信息、日期和时间等。
    通过Logger调用Info/Error等。
    默认情况下日志都会打印到应用程序的console界面
示例:
var logger *zap.Logger //定义全局logger实例

func main() {
	InitLogger()
  defer logger.Sync() //在程序退出之前,将日志都刷到磁盘上
	simpleHttpGet("www.google.com")
	simpleHttpGet("http://www.google.com")
}

func InitLogger() {
	logger, _ = zap.NewProduction() 
}
//测试
func simpleHttpGet(url string) {
	resp, err := http.Get(url)
	if err != nil {
		logger.Error(
			"Error fetching url..",
			zap.String("url", url),
			zap.Error(err))
	} else {
		logger.Info("Success..",
			zap.String("statusCode", resp.Status),
			zap.String("url", url))
		resp.Body.Close()
	}
}
打印结果,以json形式打印:
	{"level":"error","ts":1572159218.912792,"caller":"zap_demo/temp.go:25","msg":"Error fetching url..","url":"www.sogo.com","error":"Get www.sogo.com: unsupported protocol scheme \"\"","stacktrace":"main.simpleHttpGet\n\t/Users/q1mi/zap_demo/temp.go:25\nmain.main\n\t/Users/q1mi/zap_demo/temp.go:14\nruntime.main\n\t/usr/local/go/src/runtime/proc.go:203"}
{"level":"info","ts":1572159219.1227388,"caller":"zap_demo/temp.go:30","msg":"Success..","statusCode":"200 OK","url":"http://www.sogo.com"}

代码分析
	在上面的代码中,我们首先创建了一个Logger,然后使用Info/ Error等Logger方法记录消息。
日志记录器方法的语法是这样的:
	func (log *Logger) MethodXXX(msg string, fields ...Field) 
其中MethodXXX是一个可变参数函数,可以是Info / Error/ Debug / Panic等。每个方法都接受一个消息字符串和任意数量的zapcore.Field场参数。

	每个zapcore.Field其实就是一组键值对参数。
第二种 Sugared Logger
	大部分的实现基本都相同。
	惟一的区别是,我们通过调用主logger的. Sugar()方法来获取一个SugaredLogger。
	然后使用SugaredLogger以printf格式记录语句
示例
var sugarLogger *zap.SugaredLogger
func main() {
	InitLogger()
	defer sugarLogger.Sync()
	simpleHttpGet("www.google.com")
	simpleHttpGet("http://www.google.com")
}

func InitLogger() {
  logger, _ := zap.NewProduction()
	sugarLogger = logger.Sugar()
}

func simpleHttpGet(url string) {
	sugarLogger.Debugf("Trying to hit GET request for %s", url)
	resp, err := http.Get(url)
	if err != nil {
		sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)
	} else {
		sugarLogger.Infof("Success! statusCode = %s for URL %s", resp.Status, url)
		resp.Body.Close()
	}
}

四、zap日志自定义配置

1、将日志写入文件 的配置
	步骤:
		使用zap.New(…)方法来手动传递所有配置,而不是使用像zap.NewProduction()这样的预置方法来创建logger。
		func New(core zapcore.Core, options ...Option) *Logger
		zapcore.Core需要三个配置——Encoder,WriteSyncer,LogLevel。
		编码器(Encoder): 使用NewJSONEncoder(),并使用预先设置的ProductionEncoderConfig()
			zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
		WriteSyncer: 指定将日志写到哪里, 使用zapcore.AddSync()函数并且将打开的文件句柄传进去
			 file, _ := os.Create("./test.log")
  			 writeSyncer := zapcore.AddSync(file)
  		LogLevel: 哪种级别的日志将被写入
//示例 修改上述部分中的Logger代码,并重写InitLogger()方法。其余方法—main() /SimpleHttpGet()保持不变。
func InitLogger() {
	writeSyncer := getLogWriter()
	encoder := getEncoder()
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)

	logger := zap.New(core)
	sugarLogger = logger.Sugar()
}

func getEncoder() zapcore.Encoder {
	return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
}

func getLogWriter() zapcore.WriteSyncer {
	file, _ := os.Create("./test.log")
	return zapcore.AddSync(file)
}
//打印结果
{"level":"debug","ts":1572160754.994731,"msg":"Trying to hit GET request for www.sogo.com"}
{"level":"error","ts":1572160754.994982,"msg":"Error fetching URL www.sogo.com : Error = Get www.sogo.com: unsupported protocol scheme \"\""}
将json格式修改为普通格式
return zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())

修改日志中打印的时间格式
func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder //修改修改时间编码器
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder //在日志文件中使用大写字母记录日志级别
	return zapcore.NewConsoleEncoder(encoderConfig)
}

添加AddCallerSkip,通过日志获取精确的行数,同时需要在new方法中调用上述的两处修改
logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
将日志输出到多个位置
func getLogWriter() zapcore.WriteSyncer {
	file, _ := os.Create("./test.log")
	// 利用io.MultiWriter支持文件和终端两个输出目标
	ws := io.MultiWriter(file, os.Stdout)
	return zapcore.AddSync(ws)
}
将err日志单独输出到文件
func InitLogger() {
	encoder := getEncoder()
	// test.log记录全量日志
	logF, _ := os.Create("./test.log")
	c1 := zapcore.NewCore(encoder, zapcore.AddSync(logF), zapcore.DebugLevel)
	// test.err.log记录ERROR级别的日志
	errF, _ := os.Create("./test.err.log")
	c2 := zapcore.NewCore(encoder, zapcore.AddSync(errF), zap.ErrorLevel)
	// 使用NewTee将c1和c2合并到core
	core := zapcore.NewTee(c1, c2)
	logger = zap.New(core, zap.AddCaller())
}

五、按日志大小切割日志

安装日志切割库
	go get gopkg.in/natefinch/lumberjack.v2

在zap logger中添加lumberjack
修改WriteSyncer代码。我们将按照下面的代码修改getLogWriter()函数:
	func getLogWriter() zapcore.WriteSyncer {
	lumberJackLogger := &lumberjack.Logger{ //从之前的os.create替换为lumberjack.Logger
		Filename:   "./test.log", //日志文件
		MaxSize:    10, //单位是MB 文件的最大量存储10MB
		MaxBackups: 5, //保留旧日志的最大个数
		MaxAge:     30, //保留旧文件的最大天数
		Compress:   false, //是否压缩日志
	}
	return zapcore.AddSync(lumberJackLogger)
}

同时输出到日志文件和控制台

core := zapcore.NewTee(

    fileCore,
    consoleCore,

)
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐