3. 日志滚动
实际原理就是通过自定义 WriteSyncer
接口来自定义日志写入逻辑。
package main
import (
"fmt"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
"path/filepath"
"sync"
"time"
)
type DailyWriteSyncer struct {
mu sync.Mutex
currentTime string
file *os.File
logDir string
baseName string
maxKeepTime int
}
// NewDailyWriteSyncer 创建按天分割的 WriteSyncer
func NewDailyWriteSyncer(logDir, baseName string, maxKeepTime int) *DailyWriteSyncer {
return &DailyWriteSyncer{
logDir: logDir,
baseName: baseName,
maxKeepTime: maxKeepTime,
}
}
// getCurrentDay 获取当前日期(格式:2006-01-02)
func (d *DailyWriteSyncer) getCurrentDay() string {
return time.Now().Format("2006-01-02")
}
// openFile 打开或创建当天的日志文件
func (d *DailyWriteSyncer) openFile() error {
currentDay := d.getCurrentDay()
logPath := filepath.Join(d.logDir, fmt.Sprintf("%s-%s.log", d.baseName, currentDay))
if err := os.MkdirAll(d.logDir, 0755); err != nil {
return fmt.Errorf("failed to create log directory: %v", err)
}
file, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
if err != nil {
return fmt.Errorf("failed to open log file: %v", err)
}
if d.file != nil {
d.file.Close()
}
d.file = file
d.currentTime = currentDay
if d.maxKeepTime > 0 {
go d.cleanExpiredLogs()
}
return nil
}
// Write 实现 zapcore.WriteSyncer 接口
func (d *DailyWriteSyncer) Write(p []byte) (n int, err error) {
d.mu.Lock()
defer d.mu.Unlock()
currentDay := d.getCurrentDay()
if d.currentTime != currentDay || d.file == nil {
if err := d.openFile(); err != nil {
return 0, err
}
}
return d.file.Write(p)
}
// Sync 实现 zapcore.WriteSyncer 接口
func (d *DailyWriteSyncer) Sync() error {
d.mu.Lock()
defer d.mu.Unlock()
if d.file != nil {
return d.file.Sync()
}
return nil
}
// cleanExpiredLogs 清理过期日志
func (d *DailyWriteSyncer) cleanExpiredLogs() error {
d.mu.Lock()
defer d.mu.Unlock()
day := time.Now().AddDate(0, 0, -d.maxKeepTime)
expireDay := day.Format("2006-01-02")
files, _ := os.ReadDir(d.logDir)
for _, file := range files {
if file.IsDir() {
continue
}
name := file.Name()
if len(name) < len(d.baseName)+11 {
continue
}
timePart := name[len(d.baseName)+1 : len(d.baseName)+11]
if timePart < expireDay {
os.Remove(filepath.Join(d.logDir, name))
}
}
return nil
}
func main() {
encoderCfg := zapcore.EncoderConfig{
MessageKey: "message",
LevelKey: "level",
TimeKey: "time",
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeLevel: zapcore.LowercaseLevelEncoder,
}
autoLevel := zap.NewAtomicLevel()
jsonEncoder := zapcore.NewJSONEncoder(encoderCfg)
dailyWs := NewDailyWriteSyncer("./logs", "app", 1)
core := zapcore.NewCore(jsonEncoder, zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(dailyWs)), autoLevel)
logger := zap.New(core, zap.AddCaller())
defer logger.Sync()
i := 0
for {
logger.Info("This is a log message", zap.Int("count", i))
time.Sleep(3 * time.Second)
i++
if i == 20 {
autoLevel.SetLevel(zap.ErrorLevel)
}
}
}
最后更新于