安装Zap日志库
go get -u go.uber.org/zap
配置Zap Logger
Zap提供了两种类型的日志记录器—Sugared Logger
和Logger
。
在性能很好但不是很关键的上下文中,使用SugaredLogger
。它比其他结构化日志记录包快4-10倍,并且支持结构化和 printf 风格的日志记录。
在每一微秒和每一次内存分配都很重要的上下文中,使用Logger
。它甚至比SugaredLogger
更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。
Logger
- 通过调用
zap.NewProduction()
/zap.NewDevelopment()
或者zap.Example()
创建一个Logger。 - 上面的每一个函数都将创建一个logger。唯一的区别在于它将记录的信息不同。例如production logger默认记录调用函数信息、日期和时间等。
- 通过Logger调用Info/Error等。
- 默认情况下日志都会打印到应用程序的终端界面。
package main
import (
"go.uber.org/zap"
"net/http"
)
var logger *zap.Logger
func main() {
InitLogger()
// Sync 调用底层Core的 Sync 方法,刷新所有缓冲的日志条目。应用程序应注意在退出之前调用 Sync。
defer logger.Sync()
// 向指定 URL 发送 GET 请求,根据成功或出错结果打印日志信息
simpleHttpGet("www.baidu.com")
simpleHttpGet("http://www.baidu.com")
}
func InitLogger() {
// 通过 zap.NewProduction() 创建一个 logger
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()
}
}
可以看到打印了该日志的级别,花费时间,调用位置,以及我们自定义输出的错误信息。
日志记录器方法的语法:
func (log *Logger) MethodXXX(msg string, fields ...Field)
其中MethodXXX
是一个可变参数函数,可以是Info / Error/ Debug / Panic等。每个方法都接受一个消息字符串和任意数量的zapcore.Field
参数。
每个zapcore.Field
其实就是一组键值对参数。
Sugared Logger
Sugar 包装了 Logger 以提供更符合人们使用习惯但速度稍慢的 API。对 Logger 进行加糖非常便宜,因此单个应用程序同时使用 Loggers 和 SugaredLoggers 是合理的,在性能敏感代码的边界上在它们之间进行转换。
package main
import (
"go.uber.org/zap"、
"net/http"
)
var sugarLogger *zap.SugaredLogger
func main() {
InitLogger()
// Sync 调用底层Core的 Sync 方法,刷新所有缓冲的日志条目。应用程序应注意在退出之前调用 Sync。
defer sugarLogger.Sync()
// 向指定 URL 发送 GET 请求,根据成功或出错结果打印日志信息
simpleHttpGet("www.baidu.com")
simpleHttpGet("http://www.baidu.com")
}
func InitLogger() {
logger, _ := zap.NewProduction()
sugarLogger = logger.Sugar()
}
func simpleHttpGet(url string) {
resp, err := http.Get(url)
if err != nil {
sugarLogger.Errorf(
"Error fetching url..",
zap.String("url", url),
zap.Error(err))
} else {
sugarLogger.Infof("Success..",
zap.String("statusCode", resp.Status),
zap.String("url", url))
resp.Body.Close()
}
}
定制logger
接下来我们将不使用默认的 zap 配置,而是使用自定义的配置。这主要靠0
func New(core zapcore.Core, options ...Option) *Logger
方法来实现。
New
从提供的 zapcore 构造一个新的 Logger 。如果传递的 zapcore.Core 为 nil,则回退到使用无操作实现。
logger := zap.New(core)
我们现在要替换掉之前的 InitLogger
函数
func InitLogger() {
writeSyncer := getLogWriter()
encoder := getEncoder()
// 需传入Encoder、WriterSyncer、Log Level
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
// 使用zap.New(…)方法来手动传递所有配置
logger := zap.New(core)
sugarLogger = logger.Sugar()
}
// 使用JSON格式写入日志
func getEncoder() zapcore.Encoder {
return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
}
// 将日志写到 test.log 文件中
func getLogWriter() zapcore.WriteSyncer {
file, _ := os.Create("./test.log")
return zapcore.AddSync(file)
}
更换写入日志的格式
将 JSON Encoder 更改为普通的 Log Encoder
// 使用不同格式写入日志
func getEncoder() zapcore.Encoder {
//return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
return zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())
}
可以看到日志文件中的格式发生了改变,不再是之前的 JSON 格式了
更加详细的配置
我们将要增添如下的配置
- 将时间编码成人能看懂的形式
- 在日志文件中记录日志级别
// 自定义日志格式
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
// ISO8601TimeEncoder 序列化时间。以毫秒为精度的 ISO8601 格式字符串的时间。
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
// CapitalLevelEncoder 将Level序列化为全大写字符串。例如, InfoLevel被序列化为“INFO”。
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
}
日志增添调用函数的信息
// 添加将调用函数信息记录到日志中的功能。
logger := zap.New(core, zap.AddCaller())
完整代码
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"net/http"
"os"
)
var sugarLogger *zap.SugaredLogger
func main() {
InitLogger()
// Sync 调用底层Core的 Sync 方法,刷新所有缓冲的日志条目。应用程序应注意在退出之前调用 Sync。
defer sugarLogger.Sync()
// 向指定 URL 发送 GET 请求,根据成功或出错结果打印日志信息
simpleHttpGet("www.baidu.com")
simpleHttpGet("http://www.baidu.com")
}
func InitLogger() {
// 写入位置
writeSyncer := getLogWriter()
// 编码格式
encoder := getEncoder()
// 需传入Encoder、WriterSyncer、Log Level
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
// 使用zap.New(…)方法来手动传递所有配置
// 增加 Caller 信息
logger := zap.New(core, zap.AddCaller())
sugarLogger = logger.Sugar()
}
// 自定义日志格式
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
// ISO8601TimeEncoder 序列化时间。以毫秒为精度的 ISO8601 格式字符串的时间。
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
// CapitalLevelEncoder 将Level序列化为全大写字符串。例如, InfoLevel被序列化为“INFO”。
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
}
// 将日志写到 test.log 文件中
func getLogWriter() zapcore.WriteSyncer {
file, _ := os.Create("./test.log")
return zapcore.AddSync(file)
}
func simpleHttpGet(url string) {
resp, err := http.Get(url)
if err != nil {
sugarLogger.Errorf(
"Error fetching url..",
zap.String("url", url),
zap.Error(err))
} else {
sugarLogger.Infof("Success..",
zap.String("statusCode", resp.Status),
zap.String("url", url))
resp.Body.Close()
}
}
切割日志
执行下面的命令安装Lumberjack
go get -u github.com/natefinch/lumberjack
如果只用一个文件记录日志,那么这个文件会越来越大,最后也不方便定位错误。所以我们需要分割日志,比如按照时间来分割,一个日志文件记录一天的信息。
// 在zap中加入Lumberjack支持
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./test.log", // 日志文件的位置
MaxSize: 1, // 以 MB 为单位
MaxBackups: 5, // 在进行切割之前,日志文件的最大大小(以MB为单位)
MaxAge: 30, // 保留旧文件的最大天数
Compress: false, // 是否压缩/归档旧文件
}
return zapcore.AddSync(lumberJackLogger)
}
完整代码
package main
import (
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"net/http"
)
var (
sugarLogger *zap.SugaredLogger
)
func main() {
InitLogger()
defer sugarLogger.Sync() // 刷新流,写日志到文件中
// 循环写入日志,这里为了达到切割要求循环记录了 100000 次
for i := 0; i < 100000; i++ {
sugarLogger.Infof("this is a test")
}
}
func InitLogger() {
writeSyncer := getLogWriter()
encoder := getEncoder()
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
logger := zap.New(core, zap.AddCaller())
sugarLogger = logger.Sugar()
}
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
}
// 在zap中加入Lumberjack支持
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./test.log",
MaxSize: 1, // 以 MB 为单位
MaxBackups: 5, // 在进行切割之前,日志文件的最大大小(以MB为单位)
MaxAge: 30, // 保留旧文件的最大天数
Compress: false, // 是否压缩/归档旧文件
}
return zapcore.AddSync(lumberJackLogger)
}