01. 日志模块介绍
参考链接🔗:https://www.topgoer.com/项目/log/ZapLogger.html
1.1 介绍
在许多Go语言项目中,我们需要一个好的日志记录器能够提供下面这些功能:
- 能够将事件记录到文件中,而不是应用程序控制台。
- 日志切割-能够根据文件大小、时间或间隔等来切割日志文件。
- 支持不同的日志级别。例如INFO,DEBUG,ERROR等。
- 能够打印基本信息,如调用文件/函数名和行号,日志时间等。
1.2 默认的Go Logger
Go语言提供的默认日志包是 https://golang.org/pkg/log/ 。
实现一个Go语言中的日志记录器非常简单——创建一个新的日志文件,然后设置它为日志的输出位置。
package main
import (
"fmt"
"log"
"os"
"time"
)
func main() {
//创建输出日志文件
logFile, err := os.Create("./" + time.Now().Format("20060102") + ".txt")
if err != nil {
fmt.Println(err)
}
//创建一个Logger
//参数1:日志写入目的地
//参数2:每条日志的前缀
//参数3:日志属性
loger := log.New(logFile, "test_", log.Ldate|log.Ltime|log.Lshortfile)
//Flags返回Logger的输出选项
fmt.Println(loger.Flags())
//SetFlags设置输出选项
loger.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
//返回输出前缀
fmt.Println(loger.Prefix())
//设置输出前缀
loger.SetPrefix("test_")
//输出一条日志
loger.Output(2, "打印一条日志信息")
//格式化输出日志
loger.Printf("第%d行 内容:%s", 11, "我是错误")
//等价于print();os.Exit(1);
loger.Fatal("我是错误")
//等价于print();panic();
loger.Panic("我是错误33333333")
//log的导出函数
//导出函数基于std,std是标准错误输出
//var std = New(os.Stderr, "", LstdFlags)
//获取输出项
fmt.Println(log.Flags())
//获取前缀
fmt.Printf(log.Prefix())
}
1.3 Go Logger的优势和劣势
优势
- 它最大的优点是使用非常简单。
- 我们可以设置任何
io.Writer
作为日志记录输出并向其发送要写入的日志。
劣势
- 仅限基本的日志级别
- 只有一个
Print
选项。不支持INFO
/DEBUG
等多个级别。 - 缺乏日志格式化的能力——例如记录调用者的函数名和行号,格式化日期和时间格式。等等。
- 不提供日志切割的能力。
02. Zap Logger
Zap是非常快的、结构化的,分日志级别的Go日志库。
2.1 安装
go get -u go.uber.org/zap
2.2 配置Zap Logger
Zap提供了两种类型的日志记录器—Sugared Logger和Logger。
- SugaredLogger: 在性能很好但不是很关键的上下文中,使用SugaredLogger。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。
- Logger: 在每一微秒和每一次内存分配都很重要的上下文中,使用Logger。它甚至比SugaredLogger更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。
2.3 Logger
-
创建Logger的方式:
- zap.NewProduction():适合生产环境,提供结构化的 JSON 日志和更高级别的日志记录;默认丢弃调试级别的日志,仅记录info及以上严重程度的消息(error, fatal)。
- zap.NewDevelopment():适合开发环境,提供人类可读的日志和更详细的调试级别输出。
- zap.Example(): 一个简单易懂的示例,帮助学习如何使用
zap
的日志系统。
-
zap.NewProduction的例子:
package main import ( "go.uber.org/zap" "net/http" ) // 第一步:创建一个Logger var logger *zap.Logger func InitLogger() { logger, _ = zap.NewProduction() } // 第二步:通过Logger调用Info/Error等输出日志 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 main() { InitLogger() defer logger.Sync() simpleHttpGet("www.sogou.com") simpleHttpGet("www.baidu.com") }
-
在上面的代码中,我们首先创建了一个Logger,然后使用Info/ Error等Logger方法记录消息。日志记录器方法的语法是这样的:
func (log *Logger) MethodXXX(msg string, fields ...Field)
其中
MethodXXX
是一个可变参数函数,可以是Info / Error/ Debug / Panic
等。每个方法都接受一个消息字符串和任意数量的zapcore.Field
场参数。即:logger.Info()
logger.Error()
logger.Debug()
logger.Panic()
2.4 Sugared Logger
现在让我们使用Sugared Logger来实现相同的功能。
- 大部分的实现基本都相同。
- 唯一的区别是,我们通过调用主logger的. Sugar()方法来获取一个SugaredLogger。
- 然后使用SugaredLogger以printf格式记录语句
下面是修改过后使用SugaredLogger代替Logger的代码:
package main
import (
"net/http"
"go.uber.org/zap"
)
var sugarLogger *zap.SugaredLogger
func main() {
InitLogger()
defer sugarLogger.Sync()
simpleHttpGet("www.5lmh.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()
}
}