Skip to main content

log zap 設定流程

generate core​

生成一個 logger

Core := zapcore.NewCore(encoder, mainWriter, logLevel)
//

Logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))

每個 zapcore.Core 實例都代表了一個日誌寫入管道 要生成一個 logger core, 有三個要素

  1. level
  2. encoder
  3. writer (output)

level​

level 從最低到最高分別是

  1. Debug
  2. Info
  3. Warn
  4. Error
  5. DPanic
  6. Panic
  7. Fatal

可以直接用

EmailLevel := zapcore.ErrorLevel

或是

var logLevel zapcore.Level
if err := logLevel.UnmarshalText([]byte(logLevelString)); err != nil {
panic(err)
}

設定log level

encoder​

func getEncoder() zapcore.Encoder {
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
FunctionKey: zapcore.OmitKey,
MessageKey: "message",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding, // 範例 : \n
EncodeLevel: zapcore.CapitalLevelEncoder, // 範例 : INFO
EncodeTime: zapcore.RFC3339TimeEncoder, // 範例 : 2021-08-31T15:00:00+08:00
EncodeDuration: zapcore.SecondsDurationEncoder, // 範例 : 0.000000
EncodeCaller: zapcore.ShortCallerEncoder, // 範例 : main.go:14
}
// encoderConfig.EncodeTime = zapcore.RFC3339TimeEncoder
// encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewJSONEncoder(encoderConfig)
}

// SecondsDurationEncoder 使用範例

start := time.Now()
// 假設這裡是執行任務的代碼
duration := time.Since(start)
logger.Info("Task completed", zap.Duration("duration", duration))

writer​

writer 的作用是將 log 寫入到指定的地方

ex : 寫到console.log


mainWriter := getLogWriter(config.Get("log.logDir"))

func getLogWriter(logPath string) zapcore.WriteSyncer {
lumberjackLogger := &lumberjack.Logger{
Filename: logPath,
MaxSize: config.GetInt("log.logMaxSize"), // megabytes
MaxBackups: config.GetInt("log.logMaxBackups"),
MaxAge: config.GetInt("log.logMaxAge"), // days
Compress: true,
}

// MultiWriteSyncer will write logs to both the console and the file
return zapcore.NewMultiWriteSyncer(
zapcore.AddSync(os.Stdout),
zapcore.AddSync(lumberjackLogger),
)
}

// 客製化 writer


EmailCore := zapcore.NewCore(
encoder,
&EmailWriteSyncer{},
EmailLevel,
)



type CustomLogger struct {
*zap.Logger
}

// 新的LogIf方法
func (l *CustomLogger) LogIf(err error) {
if err != nil {
l.Error("Error Occurred:", zap.Error(err))
}
}

// NewCustomLoggerFactory 創建一個新的CustomLogger實例
func NewCustomLoggerFactory(z *zap.Logger) *CustomLogger {
return &CustomLogger{z}
}

var Logger *zap.Logger
var AccessLogger *zap.Logger
var NewCustomLogger *CustomLogger

type EmailWriteSyncer struct{}

func (e *EmailWriteSyncer) Write(p []byte) (n int, err error) {
subject := "ERP SYSTEM Error Log Message" // Email 主旨
body := string(p)

_, sendErr := e.sendEmail(subject, body)
if sendErr != nil {
fmt.Println("Failed to send email:", sendErr)
}

return len(p), nil
}

func (e *EmailWriteSyncer) Sync() error {
return nil // 沒有同步/刷新的需求
}

func (e *EmailWriteSyncer) sendEmail(

subject string,
body string,
) (bool, error) {
// 這裡是寄信的代碼
return true, nil
}

generate logger​

生成一個 logger

Logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))

如果有多個 core 可以用 TEE 來合併

// TEE 會將 log 寫入到兩個 core
Logger = zap.New(zapcore.NewTee(core, EmailCore), zap.AddCaller(), zap.AddCallerSkip(1))

如果想要 紀錄 json 的時候,可以用

func RequestLogger() gin.HandlerFunc {
return func(c *gin.Context) {

// Read and save the request body
bodyBytes, err := io.ReadAll(c.Request.Body)
if err != nil {
global.AccessLog.Error("Failed to read request body", zap.Error(err))
}
// Replace the original body so subsequent handlers can read it
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))

c.Next()

var requestBody map[string]interface{}
if err := json.Unmarshal(bodyBytes, &requestBody); err != nil {
global.AccessLog.Error("JSON parse fail", zap.Error(err))
return
}

global.AccessLog.Info(
"RequestLogger",
zap.String("method", c.Request.Method),
zap.String("uri", c.Request.RequestURI),
zap.String("proto", c.Request.Proto),
zap.Int("status", c.Writer.Status()),
zap.String("user-agent", c.Request.UserAgent()),
zap.String("ip", c.ClientIP()),
zap.Any("header", c.Request.Header),
)

global.AccessLog.Info("requestBody", zap.Any("requestBody", requestBody))

}
}