众所周知,在一个项目中,日志是非常非常重要的,因为有时候我们找错误,发现问题,处理问题等等,其中最重要的一点就是依靠日志,所以一般一个项目完善不完善,从日志的设置上就可以大概有所判断,日志好不好,我想大家心知肚明.好了,废话不多说,我们来看下Go语言中的日志情况.(本文在翻译zap日志库中结合自己写的代码测试而出)
Go语言提供的默认日志包是https://golang.org/pkg/log/,默认的使用Go Logger,下面 我们就来实现一个Go Logger
初始化一个日志记录器
func InitLoggerUtil() {
locationlFile, _ := os.OpenFile("/Users/csq/Desktop/csq.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0744)
log.SetOutput(locationlFile)
}
参数: 1. os.O_CREATE|os.O_APPEND|os.O_RDWR 这个我们可以进去看看,会发现:
2.perm:0744 它代表的是一个写文件的权限定义
大概去源码看了下,这里就简单说一下,后面会专门讲一下这个.主要看我的部分截图:
读,写,执行代表的数字,权限是他们的相加
一般都文件属性标识如下:
-rwxrwxrwx
第1位:文件属性,一般常用的是"-",表示是普通文件;"d"表示是一个目录。
第2~4位:文件所有者的权限rwx (可读/可写/可执行)。
第5~7位:文件所属用户组的权限rwx (可读/可写/可执行)。
第8~10位:其他人的权限rwx (可读/可写/可执行)。
在golang中,可以使用os.FileMode(perm).String()来查看权限标识:
os.FileMode(0777).String() //返回 -rwxrwxrwx
os.FileMode(0666).String() //返回 -rw-rw-rw-
例子:
0777表示:创建了一个普通文件,所有人拥有所有的读、写、执行权限
0666表示:创建了一个普通文件,所有人拥有对该文件的读、写权限,但是都不可执行
好啦,回到我们刚才的代码中来
创建一个方法:
func GetIrisDemo(url string) {
resp, err := http.Get(url)
if err != nil {
log.Printf("Error fetching url %s : %s", url, err.Error())
} else {
log.Printf("Status Code for %s : %s", url, resp.Status)
resp.Body.Close()
}
}
main方法调用:
func main() {
InitLoggerUtil()
GetIrisDemo("http://localhost:8848/api/user/list")//正确的接口
GetIrisDemo("localhost:8848/api/user/list")//错误接口
}
在执行这main方法之前 我们打开上个项目的 iris框架搭建的demo,然后启动之后 我们去对应位置会发现:
桌面多了一个我们之前命名的文件,里面追加了两个日志记录.
demo整体:
import (
"go.uber.org/zap"
"log"
"net/http"
"os"
)
var logger *zap.Logger
func main() {
InitLoggerUtil()
GetIrisDemo("http://localhost:8848/api/user/list")//正确的接口
GetIrisDemo("localhost:8848/api/user/list")//错误接口
}
func InitLoggerUtil() {
locationlFile, _ := os.OpenFile("/Users/csq/Desktop/csq.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0744)
log.SetOutput(locationlFile)
}
func GetIrisDemo(url string) {
resp, err := http.Get(url)
if err != nil {
log.Printf("Error fetching url %s : %s", url, err.Error())
} else {
log.Printf("Status Code for %s : %s", url, resp.Status)
resp.Body.Close()
}
}
为什么Go Logger在项目中有时候用的少?
1. 其实Go Logger的优点在于它的使用是非常之简单,只要你向里面写,它就会被写入到指定的日志文件中去.
2.但是它的日志级别只有一个Print,这显然是不够的,因为我们项目中常用的是INFO
/DEBUG
等多个级别.
3.对于错误日志的处理,有Fatal
和Panic,一般
Fatal日志通过调用os.Exit(1)
来结束程序,而Panic日志在写入日志消息之后抛出 一个panic,最重要的ERROR日志级别,恰恰是在不抛出panic或退出程序的情况下记录错误,而它没有.
4.不提供日志切割的能力.
所以一般我们使用Zap ,Zap是非常快的、结构化的,分日志级别的Go日志库。
安装
运行下面的命令安装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等。
- 默认情况下日志都会打印到应用程序的console界面。
接下来,我们使用zap日志来修改我们的代码:
package main
import (
"go.uber.org/zap"
"net/http"
)
var logger *zap.Logger
func main(){
InitLoggerUtil()
defer logger.Sync()
GetIrisDemo("http://localhost:8848/api/user/list")
GetIrisDemo("localhost:8848/api/user/list")
}
func InitLoggerUtil() {
logger, _ = zap.NewProduction()
}
func GetIrisDemo(url string) {
resp, err := http.Get(url)
if err != nil {
logger.Error(
"<---错误输出--->",
zap.String("url", url),
zap.Error(err))
} else {
logger.Info("<---成功输出--->",
zap.String("code", resp.Status),
zap.String("url", url))
resp.Body.Close()
}
}
查看控制台:
Sugared Logger
现在让我们使用Sugared Logger来实现相同的功能。
- 大部分的实现基本都相同。
- 惟一的区别是,我们通过调用主logger的
. Sugar()
方法来获取一个SugaredLogger
。 - 然后使用
SugaredLogger
以printf
格式记录语句
package main
import (
"go.uber.org/zap"
"net/http"
)
var logger *zap.Logger
var sugarLogger *zap.SugaredLogger
func main(){
InitLoggerUtil()
//defer logger.Sync()
defer sugarLogger.Sync()
GetIrisDemo("http://localhost:8848/api/user/list")
GetIrisDemo("localhost:8848/api/user/list")
}
func InitLoggerUtil() {
logger, _ := zap.NewProduction()
sugarLogger = logger.Sugar()
}
func GetIrisDemo(url string) {
resp, err := http.Get(url)
if err != nil {
sugarLogger.Errorf(
"<---错误输出--->",
zap.String("url", url),
zap.Error(err))
} else {
sugarLogger.Info("<---成功输出--->",
zap.String("code", resp.Status),
zap.String("url", url))
resp.Body.Close()
}
}
查看控制台:
定制logger
将日志写入文件而不是终端
我们要做的第一个更改是把日志写入文件,而不是打印到应用程序控制台。
- 我们将使用
zap.New(…)
方法来手动传递所有配置,而不是使用像zap.NewProduction()
这样的预置方法来创建logger。
func New(core zapcore.Core, options ...Option) *Logger
zapcore.Core
需要三个配置——Encoder
,WriteSyncer
,LogLevel
。
1.Encoder:编码器(如何写入日志)。我们将使用开箱即用的NewJSONEncoder()
,并使用预先设置的ProductionEncoderConfig()
。
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
2.WriterSyncer :指定日志将写到哪里去。我们使用zapcore.AddSync()
函数并且将打开的文件句柄传进去。
3.Log Level:哪种级别的日志将被写入。
我们将修改上述部分中的Logger代码
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"net/http"
"os"
)
var logger *zap.Logger
var sugarLogger *zap.SugaredLogger
func main(){
InitLoggerUtil()
//defer logger.Sync()
defer sugarLogger.Sync()
GetIrisDemo("http://localhost:8848/api/user/list")
GetIrisDemo("localhost:8848/api/user/list")
}
func InitLoggerUtil() {
//logger, _ := zap.NewProduction()
//sugarLogger = logger.Sugar()
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("/Users/csq/Desktop/csq02.log")
return zapcore.AddSync(file)
}
func GetIrisDemo(url string) {
resp, err := http.Get(url)
if err != nil {
sugarLogger.Errorf(
"<---错误输出--->",
zap.String("url", url),
zap.Error(err))
} else {
sugarLogger.Info("<---成功输出--->",
zap.String("code", resp.Status),
zap.String("url", url))
resp.Body.Close()
}
}
去对应位置查看,可以发现我们之前打印在控制台的日志都写到具体的文件里面去了.
从日志内容可以看出,这是一个json形势的,要是改为普通的Log Encoder,只需要将上述代码中的
return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
修改为:
return
zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())就ok 了.
未完待续....