1. 程式人生 > 其它 >Golang Zap日誌

Golang Zap日誌

Zap日誌解析

Config.yaml

zap:
  level: 'info' #日誌級別
  format: 'console' #輸出的級別,有console和json
  prefix: '[catering]' #輸出的字首,[catering]xxxxxxxxxxx
  director: 'log' #存放的資料夾
  show-line: true #是否顯示行號
  encode-level: 'LowercaseColorLevelEncoder' #編碼級別,目前支援四種LowercaseLevelEncoder,LowercaseColorLevelEncoder,CapitalLevelEncoder,CapitalColorLevelEncoder
  stacktrace-key: 'stacktrace' #棧名
  log-in-console: true #輸出控制檯

配置檔案

package config

type Zap struct {
	Level         string `mapstructure:"level" json:"level" yaml:"level"`                           // 級別
	Format        string `mapstructure:"format" json:"format" yaml:"format"`                        // 輸出
	Prefix        string `mapstructure:"prefix" json:"prefix" yaml:"prefix"`                        // 日誌字首
	Director      string `mapstructure:"director" json:"director"  yaml:"director"`                 // 日誌資料夾
	ShowLine      bool   `mapstructure:"show-line" json:"showLine" yaml:"showLine"`                 // 顯示行
	EncodeLevel   string `mapstructure:"encode-level" json:"encodeLevel" yaml:"encode-level"`       // 編碼級
	StacktraceKey string `mapstructure:"stacktrace-key" json:"stacktraceKey" yaml:"stacktrace-key"` // 棧名
	LogInConsole  bool   `mapstructure:"log-in-console" json:"logInConsole" yaml:"log-in-console"`  // 輸出控制檯
}

初始化

func InitZap() {
	cfg := global.Config.Zap //獲取上述的配置檔案
    
    // 判斷是否有Director資料夾
	if ok, _ := pkg.PathExists(cfg.Director); !ok { 
		fmt.Printf("create %v directory\n", cfg.Director)
		_ = os.Mkdir(cfg.Director, os.ModePerm)
	}
    
    // zap.LevelEnablerFunc(func(lev zapcore.Level) bool 用來劃分不同級別的輸出
    // 根據不同的級別輸出到不同的日誌檔案
    
	// 除錯級別
	debugPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
		return lev == zap.DebugLevel
	})
	// 日誌級別
	infoPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
		return lev == zap.InfoLevel
	})
	// 警告級別
	warnPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
		return lev == zap.WarnLevel
	})
	// 錯誤級別
	errorPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
		return lev >= zap.ErrorLevel
	})

	cores := [...]zapcore.Core{
		getEncoderCore(fmt.Sprintf("./%s/server_debug.log", cfg.Director), debugPriority),
		getEncoderCore(fmt.Sprintf("./%s/server_info.log", cfg.Director), infoPriority),
		getEncoderCore(fmt.Sprintf("./%s/server_warn.log", cfg.Director), warnPriority),
		getEncoderCore(fmt.Sprintf("./%s/server_error.log", cfg.Director), errorPriority),
	}
    
    //zapcore.NewTee(cores ...zapcore.Core) zapcore.Core
    //NewTee建立一個Core,將日誌條目複製到兩個或更多的底層Core中
    
	logger := zap.New(zapcore.NewTee(cores[:]...))

    //用檔名、行號和zap呼叫者的函式名註釋每條訊息
	if cfg.ShowLine {
		logger = logger.WithOptions(zap.AddCaller())
	}

    //把初始化好的logger賦值到全域性日誌變數中
	global.Log = logger
}

getEncoderCode函式

// getEncoderCore 獲取Encoder的zapcore.Core
func getEncoderCore(fileName string, level zapcore.LevelEnabler) (core zapcore.Core) {
	writer := pkg.GetWriteSyncer(fileName) // 使用file-rotatelogs進行日誌分割
	return zapcore.NewCore(getEncoder(), writer, level)
}

getEncoder函式

// getEncoder 獲取zapcore.Encoder
func getEncoder() zapcore.Encoder {
    //獲取配置檔案的輸出格式,json or console
	cfg := global.Config.Zap
	if cfg.Format == "json" {
		return zapcore.NewJSONEncoder(getEncoderConfig())
	}
	return zapcore.NewConsoleEncoder(getEncoderConfig())
}

getEncoderConfig函式

獲取自定義的編碼器的配置

// getEncoderConfig 獲取zapcore.EncoderConfig
func getEncoderConfig() (config zapcore.EncoderConfig) {
	cfg := global.Config.Zap
	config = zapcore.EncoderConfig{
		MessageKey:     "message",
		LevelKey:       "level",
		TimeKey:        "time",
		NameKey:        "logger",
		CallerKey:      "caller",
		StacktraceKey:  cfg.StacktraceKey, //棧名
		LineEnding:     zapcore.DefaultLineEnding, //預設的結尾\n
		EncodeLevel:    zapcore.LowercaseLevelEncoder, //小寫字母輸出
		EncodeTime:     CustomTimeEncoder, //自定義時間格式
		EncodeDuration: zapcore.SecondsDurationEncoder, //編碼間隔 s
		EncodeCaller:   zapcore.FullCallerEncoder, //控制列印的檔案位置是絕對路徑,ShortCallerEncoder 是相對路徑
	}
    //根據配置檔案重新配置編碼顏色和字型
	switch {
	case cfg.EncodeLevel == "LowercaseLevelEncoder": // 小寫編碼器(預設)
		config.EncodeLevel = zapcore.LowercaseLevelEncoder
	case cfg.EncodeLevel == "LowercaseColorLevelEncoder": // 小寫編碼器帶顏色
		config.EncodeLevel = zapcore.LowercaseColorLevelEncoder
	case cfg.EncodeLevel == "CapitalLevelEncoder": // 大寫編碼器
		config.EncodeLevel = zapcore.CapitalLevelEncoder
	case cfg.EncodeLevel == "CapitalColorLevelEncoder": // 大寫編碼器帶顏色
		config.EncodeLevel = zapcore.CapitalColorLevelEncoder
	default:
		config.EncodeLevel = zapcore.LowercaseLevelEncoder
	}
	return config
}

CustomTimeEncoder函式

用於自定義日誌字首的輸出格式

// 自定義日誌輸出時間格式
// 輸出格式為 prfix + 具體的時間日期
func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	cfg := global.Config.Zap
	enc.AppendString(t.Format(cfg.Prefix + "2006-01-02 15:04:05.000"))
}

pkg.GetWriteSyncer函式

用於切割檔案,防止日誌檔案過大

func GetWriteSyncer(file string) zapcore.WriteSyncer {
	lumberJackLogger := &lumberjack.Logger{
		Filename:   file, // 日誌檔案的位置
		MaxSize:    10,   // 在進行切割之前,日誌檔案的最大大小(以MB為單位)
		MaxBackups: 200,  // 保留舊檔案的最大個數
		MaxAge:     30,   // 保留舊檔案的最大天數
		Compress:   true, // 是否壓縮/歸檔舊檔案
	}

	if global.Config.Zap.LogInConsole {
		return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger))
	}
	return zapcore.AddSync(lumberJackLogger)
}

用法

虛擬碼,切勿直接貼上複製

package global

var log *zap.Logger
package main

import (
	"catering/initialize"
    "catering/global"
)

func main() {
    initialize.InitZap()
    global.log.Info("test")
}

列印日誌示例

log
├── server_error.log
├── server_info.log

server_info.log

[catering]2022-03-17 17:36:42.200	[34minfo[0m	E:/GoPath/project/go-catering/admin/initialize/router.go:32	use middleware logger
[catering]2022-03-17 17:36:42.201	[34minfo[0m	E:/GoPath/project/go-catering/admin/initialize/router.go:36	use middleware cors
[catering]2022-03-17 17:36:42.201	[34minfo[0m	E:/GoPath/project/go-catering/admin/initialize/router.go:38	register swagger handler
[catering]2022-03-17 17:36:42.225	[34minfo[0m	E:/GoPath/project/go-catering/admin/initialize/router.go:104	router register success
[catering]2022-03-17 17:36:42.228	[34minfo[0m	E:/GoPath/project/go-catering/admin/core/server.go:27	server run success on 	{"address": ":8888"}

server_error.log

[catering]2022-03-09 21:35:33.812	[31merror[0m	D:/goProject/src/catering/admin/pkg/app/request.go:13	Id.Required.	{"message": "Id Can not be empty"}
[catering]2022-03-09 21:35:33.813	[31merror[0m	D:/goProject/src/catering/admin/pkg/app/request.go:13	LevelName.Required.	{"message": "LevelName Can not be empty"}
[catering]2022-03-09 21:35:33.813	[31merror[0m	D:/goProject/src/catering/admin/pkg/app/request.go:13	UpNeedIntegration.Required.	{"message": "UpNeedIntegration Can not be empty"}