前言 链接到标题
本文学习了如何使用 uber 开源的高性能日志库 zap ,以下为完整程序,稍做完善,可用于生产环境。
// 作者: pj-x86
// 日期: 2020-01-02
// 功能: 本程序基于 zap 和 lumberjack 实现以下日志基本功能:
// 1. 支持日志输出到文件
// 2. 支持滚动日志输出,自动根据文件大小、日期进行文件切割、归档等
// 3. 支持多种日志级别,如 debug/info/error 等
// 4. 支持结构化日志输出,如 JSON 日志格式
// 5. 支持打印文件名、行号等信息
// 6. 支持不同环境采用不同的日志输出格式
// 7. 支持开发环境同时输出日志到文件和控制台,方便调试;其他环境,只输出到文件
package main
import (
"net/http"
"os"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
func getEncoder(env string) zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder //采用 ISO8601 时间格式
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder //日志级别名称全大写
if env == "production" || env == "prod" { //生产环境使用对日志监控系统友好的 json 格式输出
return zapcore.NewJSONEncoder(encoderConfig)
}
//其他开发测试环境,使用对人类友好的控制台格式输出
return zapcore.NewConsoleEncoder(encoderConfig)
}
func getLogWriter(fileName string) zapcore.WriteSyncer {
//file, _ := os.Create("./test.log")
//return zapcore.AddSync(file)
//启用滚动日志,日志自动切割及归档
lumberJackLogger := &lumberjack.Logger{
Filename: fileName,
MaxSize: 300, // megabytes
MaxBackups: 999,
MaxAge: 365, // days
LocalTime: true, // 采用当地时间
Compress: false,
}
return zapcore.AddSync(lumberJackLogger)
}
func initLogger(env string, fileName string, atomLevel zap.AtomicLevel) *zap.Logger {
var allCore []zapcore.Core
encoder := getEncoder(env)
fileWriteSyncer := getLogWriter(fileName) //输出到文件
consoleWriteSyncer := zapcore.Lock(os.Stdout) //输出到控制台
if env == "dev" { //开发环境同时输出日志到控制台和文件,便于快速调试
allCore = append(allCore, zapcore.NewCore(encoder, consoleWriteSyncer, atomLevel))
}
allCore = append(allCore, zapcore.NewCore(encoder, fileWriteSyncer, atomLevel))
//core := zapcore.NewCore(encoder, fileWriteSyncer, atomLevel) //第三个参数确定日志输出级别
core := zapcore.NewTee(allCore...)
return zap.New(core, zap.AddCaller()) //AddCaller 打印文件名、行号
}
func simpleHttpGet(url string, sugarLogger *zap.SugaredLogger) {
sugarLogger.Debugf("Trying to send GET request for %s", url)
resp, err := http.Get(url)
if err != nil {
//sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)
sugarLogger.Errorw("Error fetching URL", "url", url, "error", err)
} else {
sugarLogger.Infof("Success! statusCode = %s for URL %s", resp.Status, url)
resp.Body.Close()
}
}
func main() {
//日志对象全部定义在 main 函数内部,不作为全局变量,采用显示依赖注入,不使用隐式依赖,类似 go-kit 的设计理念
var logger *zap.Logger
var sugarLogger *zap.SugaredLogger
var atomLevel zap.AtomicLevel //日志输出级别,可动态修改
var env string = "test" //dev-开发, test-测试, prod-生产
atomLevel = zap.NewAtomicLevelAt(zap.InfoLevel) //默认 Info 日志级别
logger = initLogger(env, "./test.log", atomLevel) //默认创建性能最佳的 Logger 对象,对性能有要求的场景下可以使用它,仅支持结构化日志输出
//一般场景下使用性能不错并且易用性也不错的 SugaredLogger 对象,同时支持结构化和 printf 风格的日志输出
sugarLogger = logger.Sugar()
defer sugarLogger.Sync()
go func() {
for i := 0; i < 100000; i++ {
time.Sleep(1 * time.Second)
simpleHttpGet("www.aaa.com", sugarLogger)
simpleHttpGet("http://www.baidu.com", sugarLogger)
}
}()
/* 启用动态修改日志级别的 HTTP 接口
* 使用方法:
* curl http://localhost:8080/log/level #获取当前日志级别
* curl -XPUT --data '{"level":"debug"}' http://localhost:8080/log/level #修改日志级别为 debug
* 支持的日志级别: debug, info, warn, error, fatal, panic
*/
http.HandleFunc("/log/level", atomLevel.ServeHTTP)
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}