一、默認版log庫
1.配置日志輸出文件
func SetupLogger() {
logFileLocation, _ := os.OpenFile("/Users/q1mi/test.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0744)
log.SetOutput(logFileLocation)
}
2.使用logger
func simpleHttpGet(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()
}
}
- 三個打印函數Print 、Panic 、Fatal
- 對每一類接口其提供了3中調用方式,分別是 "Xxxx 、 Xxxxln 、Xxxxf",基本和fmt中的相關函數類似
- log.Fatal 接口,打印輸出后,接着調用系統的 os.exit(1) 接口
- log.Panic接口,該函數把日志內容刷到標准錯誤后,調用 panic 函數
log包存在的劣勢
- 僅限基本的日志級別
- 但是它缺少一個ERROR日志級別,這個級別可以在不拋出panic或退出程序的情況下記錄錯誤
- 但是它缺少一個ERROR日志級別,這個級別可以在不拋出panic或退出程序的情況下記錄錯誤
- 不提供日志切割的能力,如日志文件大小,存儲管理
二、go.uber.org/zap 又快又好用的日志包
安裝 go get -u go.uber.org/zap
1)配置Zap Logger
Zap提供了兩種類型的日志記錄器 SugaredLogger 和 Logger
相對來說,SugaredLogger比 Logger封裝更高級,支持結構化和printf風格,
但是Logger,適用於每一微秒和每一次內存分配都很重要的上下文中,因為只支持強類型的結構化日志記錄,比SugaredLogger更快,內存分配次數也更少,
2)簡單調用
- zap.NewProduction()、zap.NewDevelopment()、zap.Example()創建一個Logger
- 上面每一個函數都將創建一個logger。唯一的區別在於它將記錄的信息不同。例如production logger默認記錄調用函數信息、日期和時間等
- Logger調用Info/Error等,默認日志打印到console
var logger *zap.Logger
func main() {
InitLogger()
defer logger.Sync() //zap底層設置了緩存,sync將緩存同步到文件中
simpleHttpGet("www.google.com")
simpleHttpGet("http://www.google.com")
}
func InitLogger() {
logger, _ = zap.NewProduction()
sugarLogger = logger.Sugar() //升級為sugar
}
func simpleHttpGet(url string) {
resp, err := http.Get(url)
if err != nil {
logger.Error(
"Error fetching url..",
zap.String("url", url), // 輸出 url: XXXX,
zap.Error(err))
} else {
logger.Info("Success..",
zap.String("statusCode", resp.Status), //zap.type() 強類型,寫入日志,zap.StringP,輸出記錄指針,zap.StringL 輸出記錄字符串數組
zap.String("url", url))
resp.Body.Close()
}
}
輸出結果 默認有caller
{"level":"error","ts":1572159149.923002,"caller":"logic/temp2.go:27","msg":"Error fetching URL www.sogo.com : Error = Get www.sogo.com: unsupported protocol scheme \"\"","stacktrace":"main.simpleHttpGet\n\t/Users/q1mi/zap_demo/logic/temp2.go:27\nmain.main\n\t/Users/q1mi/zap_demo/logic/temp2.go:14\nruntime.main\n\t/usr/local/go/src/runtime/proc.go:203"}
{"level":"info","ts":1572159150.192585,"caller":"logic/temp2.go:29","msg":"Success! statusCode = 200 OK for URL http://www.sogo.com"}
3)定制化調用
func New(core zapcore.Core, options ...Option) *Logger
其中zapcore.Core需要三個配置——Encoder,WriteSyncer,LogLevel
-
Encoder 編碼器(如何寫入日志),使用json編碼NewsJSONEncoder(),再使用預先設置的ProductionEncoderConfig()。
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
-
WriterSyncer :指定日志將寫到哪里去。我們使用zapcore.AddSync()函數並且將打開的文件句柄傳進去
file, _ := os.Create("./test.log")
writeSyncer := zapcore.AddSync(file)
- Log Level:哪種級別的日志將被寫入
func InitLogger() {
file, _ := os.Create("./test.log")
writeSyncer := zapcore.AddSync(file)
encoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) //json輸出格式
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
logger := zap.New(core)
sugarLogger = logger.Sugar()
}
輸出結果,無caller,需額外配置
{"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 \"\""}
{"level":"debug","ts":1572160754.994996,"msg":"Trying to hit GET request for http://www.sogo.com"}
{"level":"info","ts":1572160757.3755069,"msg":"Success! statusCode = 200 OK for URL http://www.sogo.com"}
添加調用詳細信息,即那行代碼出錯
logger := zap.New(core, zap.AddCaller())
修改時間格式
大寫記錄日志級別
通過修改core
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
encoder := zapcore.NewConsoleEncoder(encoderConfig)
019-10-27T15:33:29.855+0800 DEBUG logic/temp2.go:47 Trying to hit GET request for www.sogo.com
2019-10-27T15:33:29.855+0800 ERROR logic/temp2.go:50 Error fetching URL www.sogo.com : Error = Get www.sogo.com: unsupported protocol scheme ""
2019-10-27T15:33:29.856+0800 DEBUG logic/temp2.go:47 Trying to hit GET request for http://www.sogo.com
2019-10-27T15:33:30.125+0800 INFO logic/temp2.go:52 Success! statusCode = 200 OK for URL http://www.sogo.com
三、使用Lumberjack進行日志切割歸檔
go get -u github.com/natefinch/lumberjack
- 使用lumberjack 修改WriterSyncer
lumberJackLogger := &lumberjack.Logger{
Filename: "./test.log",
MaxSize: 10, //日志文件最大存儲大小(MB)
MaxBackups: 5, //保留舊文件最大個數
MaxAge: 30, //保留舊文件最大天數
Compress: false, //是否壓縮
}
WriterSyncer := zapcore.AddSync(lumberJackLogger)
最終完整zap/lumberjack代碼示例如下:
package main
import (
"net/http"
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var sugarLogger *zap.SugaredLogger
func main() {
InitLogger()
defer sugarLogger.Sync()
simpleHttpGet("www.sogo.com")
simpleHttpGet("http://www.sogo.com")
}
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)
}
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./test.log",
MaxSize: 1,
MaxBackups: 5,
MaxAge: 30,
Compress: false,
}
return zapcore.AddSync(lumberJackLogger)
}
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()
}
}