前言 链接到标题

本文学习了如何使用 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)
	}

}

参考资料 链接到标题

  1. 官方库 api 说明
  2. 在Go语言项目中使用Zap日志库
  3. 玩转高性能日志库ZAP (1)
  4. Uber日志zap同时输出到Kafka,Console,文件