zap 使用
uber 開源昆庇,zap 可以在控制面板、文檔甚至發(fā)送數(shù)據(jù)到其他系統(tǒng)中闸溃,以此來記錄日志整吆。我們可以指定日志的級別,支持 json 結(jié)構(gòu)化辉川,方便查詢表蝙。
和 logrus 類似,簡單來講乓旗,日志有兩個概念:字段和消息府蛇。字段用來結(jié)構(gòu)化輸出錯誤相關(guān)的上下文環(huán)境,而消息簡明扼要的闡述錯誤本身寸齐。
開發(fā)模式下是普通文本的結(jié)構(gòu):
package main
import (
"go.uber.org/zap"
"time"
)
func () {
logger, _ := zap.NewDevelopment()
defer logger.Sync()
logger.Info("無法獲取網(wǎng)址",
zap.String("url", "http://www.baidu.com"),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}
|
自定義配置:
func main() {
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder, // ISO8601 UTC 時間格式
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder, // 全路徑編碼器
}
// 設(shè)置日志級別
atom := zap.NewAtomicLevelAt(zap.DebugLevel)
config := zap.Config{
Level: atom, // 日志級別
Development: true, // 開發(fā)模式欲诺,堆棧跟蹤
Encoding: "json", // 輸出格式 console 或 json
EncoderConfig: encoderConfig, // 編碼器配置
InitialFields: map[string]interface{}{"serviceName": "spikeProxy"}, // 初始化字段,如:添加一個服務(wù)器名稱
OutputPaths: []string{"stdout", "./logs/spikeProxy.log"}, // 輸出到指定文檔 stdout(標準輸出渺鹦,正常顏色) stderr(錯誤輸出,紅色)
ErrorOutputPaths: []string{"stderr"},
}
// 構(gòu)建日志
logger, err := config.Build()
if err != nil {
panic(fmt.Sprintf("log 初始化失敗: %v", err))
}
logger.Info("log 初始化成功")
logger.Info("無法獲取網(wǎng)址",
zap.String("url", "http://www.baidu.com"),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}
lumberjack 歸檔
日志文檔越來越大蛹含,我們根據(jù)大小毅厚、日期進行歸檔。
zap 可以寫入文檔浦箱,但是并沒有歸檔的功能吸耿。借助于 lumberjack 第三方庫,利用 hook 進行歸檔酷窥。
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"time"
"gopkg.in/natefinch/lumberjack.v2"
"os"
)
func main() {
hook := lumberjack.Logger{
Filename: "./logs/spikeProxy1.log", // 日志文檔路徑
MaxSize: 128, // 每個日志文檔保存的最大尺寸 單位:M
MaxBackups: 30, // 日志文檔最多保存多少個備份
MaxAge: 7, // 文檔最多保存多少天
Compress: true, // 是否壓縮
}
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "linenum",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder, // 小寫編碼器
EncodeTime: zapcore.ISO8601TimeEncoder, // ISO8601 UTC 時間格式
EncodeDuration: zapcore.SecondsDurationEncoder, //
EncodeCaller: zapcore.FullCallerEncoder, // 全路徑編碼器
EncodeName: zapcore.FullNameEncoder,
}
// 設(shè)置日志級別
atomicLevel := zap.NewAtomicLevel()
atomicLevel.SetLevel(zap.InfoLevel)
core := zapcore.NewCore(
zapcore.NewJSONEncoder(encoderConfig), // 編碼器配置
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)), // 打印到控制面板和文檔
atomicLevel, // 日志級別
)
// 開啟開發(fā)模式咽安,堆棧跟蹤
caller := zap.AddCaller()
// 開啟文檔及行號
development := zap.Development()
// 設(shè)置初始化字段
filed := zap.Fields(zap.String("serviceName", "serviceName"))
// 構(gòu)造日志
logger := zap.New(core, caller, development, filed)
logger.Info("log 初始化成功")
logger.Info("無法獲取網(wǎng)址",
zap.String("url", "[http://www.baidu.com](http://www.baidu.com/)"),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second))
}
zap 優(yōu)勢及原理
標準 log 沒有日志分級。seelog 可分級蓬推,支持歸檔妆棒,比較靈活,但是利用反射沸伏,效率低糕珊。
避免 GC: 對象復(fù)用
zap 通過 sync.Pool 提供的對象池,復(fù)用了大量可以復(fù)用的對象毅糟,避開了 gc 這個大麻煩红选。
內(nèi)建的 Encoder: 避免反射
標準庫中的 json.Marshaler 提供的是基于類型反射的拼接方式,代價是高昂的姆另。
zap 選擇了自己實現(xiàn) json Encoder喇肋。 通過明確的類型調(diào)用坟乾,直接拼接字符串,最小化性能開銷蝶防。
level handler
level handler 是 zap 提供的一種 level 的處理方式糊渊,通過 http 請求動態(tài)改變?nèi)罩窘M件級別。