目录
日志介绍
我们都知道哈,项目在开发阶段,出现问题,我们一般会存日志来进行分析问题,这是非常有效的,上线后更需要依赖日志了,所以查看日志是非常重要的。
那么我们需要怎么样的日志呢?
其实在我们的go语言里面的提供了很多的日志库,但是我们需要一个好的日志能够提供以下几个条件:
- 能打印最基本的信息,例如调用的文件,函数名称,行号,日志时间等等。
- 支持不同的日志级别,例如: info 、 debug 、 error 等等
- 能够将记录的日志保存在文件里面,并且可以根据
时间
或者文件大小
来切割日志文件
而zap就完全满足了,他非常的高效,并且是结构化的,分级的go日志库。
为什么选择zap日志
这个问题好哈!两个点:
- 结构化日志记录并且是printf风格的日志记录
- 真的 非常的快 ,非常的高效,啊哈哈
根据Uber-go Zap的文档,它的性能比类似的结构化日志包更好——也比标准库更快。 以下是Zap发布的基准测试信息
- 记录一条消息和10个字段:
- 记录一个静态字符串,没有任何上下文或printf风格的模板:
- 完虐呀家人们!!!
zap的安装
- 直接用工具导入,或者doc命令导入都可以哦!
go get -u go.uber.org/zap
zap的基本配置
-
Zap提供了两种类型的日志记录器—
Sugared Logger
和Logger
。 -
在性能很好但不是很关键的上下文中,使用
SugaredLogger
。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。 -
在每一微秒和每一次内存分配都很重要的上下文中,使用
Logger
。它甚至比SugaredLogger
更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。
Logger
- 可以通过调用
zap.NewProduction()
/zap.NewDevelopment()
来创建又给Logger
- 两种方式都可以,区别就是打印出来的格式不一样
NewDevelopment 是以 空格分开
的形式展示
NewProduction 使用的是 json格式
,键值对的形式
展示出来
- 注意:默认情况下日志都会打印到应用程序的console界面。
SugaredLogger
- 这个就直接使用logger.Sugar()即可,啥使用都管用
- 他们基本上相同,唯一的不同的就是SugaredLogger可以用printf格式记录语句
例如:
sugarLogger.Infof("Success! statusCode = %s for URL %s", resp.Status, url)
自定义logger
那么不想打印在终端怎么办呢,那我们只有自定义配置了
- 那我们只能使用
zap.New(…)
方法来手动传递所有配置
- 我们可以看到需要一个zapcore.Core的参数,所以我们再进去看看
- 我们可以看到这个是一个接口,里面有个newCore的方法可以创建一个core。
- 源码里面可以明显的看到哈,只要我们给三个参数,就可以得到一个logger了,那么这三个参数分别表示上面呢?
Encoder
: 编码器(如何写入日志)。我们将使用开箱即用的NewJSONEncoder(),并使用预先设置的ProductionEncoderConfig()。
// core 三个参数之 编码
func getEncoder() zapcore.Encoder {
return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
}
WriteSyncer
: 指定日志将写到哪里去。但是打开的类型不一样,文件打开的是io.writer类型,而我们需要的是WriteSyncer,所以我们使用zapcore.AddSync()函数来进行一个转换。
// core 三个参数之 路径
func getLogWriter() zapcore.WriteSyncer {
file,_ := os.Create("E:/test.log")
return zapcore.AddSync(file)
}
LevelEnabler
: 这个就是我们所需要打印的日志等级设置了,通过它来动态的保存日志,比如上线后我们error以下的日志就不打印了!
- 我们通过 zapcore.***Level 来设置,里面都是封装好的日志等级
- 可以看下zapcore的源码哦
- 非常的ok!然后我们就可以创建一个logger了
var logger *zap.Logger
var sugarLogger *zap.SugaredLogger
func InitLogger() {
encoder := getEncoder()
writerSyncer := getLogWriter()
core := zapcore.NewCore(encoder,writerSyncer,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("E:/test.log")
return zapcore.AddSync(file)
}
- 我们来跑一个例子:
- 好用!!!只有存在文件里面才方便我们往后的查看呢!!!
将JSON Encoder更改为普通的Log Encoder
- 我们采用编码格式的时候,采用的json格式满,可以有的人习惯看空格呀,怎么办,那就换一个呗,
- 人家zap也是提供了的
// core 三个参数之 编码
func getEncoder() zapcore.Encoder {
return zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())
}
- 非常的ok哈!!!
- 但是这个时间,,,还是有点不敬人意哈,所以我们好需要调整以下
编码配置优化
- 那么我们就需要对
encoderConfig
进行一个自定义配置了
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
}
- 修改时间编码器
- 在日志文件中使用大写字母记录日志级别
- 是不是感觉又好很多了!
- 那么我们怎么来获取 调用的文件,函数名称,行号呢?
- 也很简单哈,我们再new一个zap 的时候加个
zap.AddCaller()
即可!
logger := zap.New(core, zap.AddCaller())
- 来看看效果吧!
- 这样子,我们基本上就可以进行一个很好的使用体验了哈!!!
- 你以为没了? 不,还有最重要的一点,
文件的切割
,但是很可惜,zap没有这玩意,所以我们只有采用第三方库来实现拉!
使用Lumberjack进行日志切割归档
注意:Zap本身不支持切割归档日志文件
- 为了实现切割功能呢,我们采用第三方库
Lumberjack
Lumberjack的安装
- 老规矩哈,要是有依赖漏了就 go mod tidy 一下哈!!!
go get -u github.com/natefinch/lumberjack
zap logger中加入Lumberjack
- 要在zap中加入Lumberjack支持,我们需要修改WriteSyncer代码。我们将按照下面的代码修改getLogWriter()函数:
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./test.log",
MaxSize: 10,
MaxBackups: 5,
MaxAge: 30,
Compress: false,
}
return zapcore.AddSync(lumberJackLogger)
}
- 分别表示上面意思呢?
Lumberjack Logger采用以下属性作为输入:
属性 | 含义 |
---|---|
Filename | 日志文件的位置,也就是路径 |
MaxSize | 在进行切割之前,日志文件的最大大小(以MB为单位) |
MaxBackups | 保留旧文件的最大个数 |
MaxAges | 保留旧文件的最大天数 |
Compress | 是否压缩/归档旧文件 |
到这里我们的代码就完成了!!!
来看看总代码:
package main
import (
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"net/http"
)
var logger *zap.Logger
var sugarLogger *zap.SugaredLogger
func InitLogger() {
encoder := getEncoder()
writerSyncer := getLogWriter()
core := zapcore.NewCore(encoder,writerSyncer,zapcore.DebugLevel)
logger = zap.New(core,zap.AddCaller())
sugarLogger = logger.Sugar()
}
// core 三个参数之 编码
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
}
// core 三大核心之 路径
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "E:/test.log",
MaxSize: 10,
MaxBackups: 5,
MaxAge: 30,
Compress: false,
}
return zapcore.AddSync(lumberJackLogger)
}
func main() {
InitLogger()
defer logger.Sync()
simpleHttpGet("www.baidu.com")
simpleHttpGet("http://www.baidu.com")
}
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()
}
}
- 这里我们设置的是
日志文件每 5MB 会切割并且在当前目录下最多保存 5 个备份
,并且会将旧文档保存30天。
到这里,我们就完成了zap日志程序集成到项目中了,还是很方便简单的哈!
至于测试数据,大家可以跑几个 goroutine来试试,把MaxSize调小一点,即可看到切分的效果哦!!