log zap 設定流程
generate core
生成一個 logger
Core := zapcore.NewCore(encoder, mainWriter, logLevel)
//
Logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
每個 zapcore.Core 實例都代表了一個日誌寫入管道 要生成一個 logger core, 有三個要素
- level
- encoder
- writer (output)
level
level 從最低到最高分別是
- Debug
- Info
- Warn
- Error
- DPanic
- Panic
- 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))
}
}