package simple_zap import ( "context" "fmt" "go.uber.org/zap" "go.uber.org/zap/zapcore" "log" "os" "sync" "time" ) const loggerCtxKey = "baozhida" var ( Logger *zap.Logger mutex sync.RWMutex ) func init() { // 确保日志目录存在 if err := os.MkdirAll("./log", 0755); err != nil { log.Fatalf("Failed to create log directory: %v", err) } // 设置不同级别的日志文件 levelFiles, err := setupLevelBasedLogFiles() if err != nil { log.Fatalf("Failed to setup level-based log files: %v", err) } // 配置zap的各个级别输出 var cores []zapcore.Core encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder for level, file := range levelFiles { core := zapcore.NewCore( zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(file), level, // 指定日志级别 ) cores = append(cores, core) } // 组合所有核心为一个TeeCore,这样所有级别的日志都会被正确路由 core := zapcore.NewTee(cores...) Logger = zap.New(core) startDailyLogFileSwitcher() } func setupLevelBasedLogFiles() (map[zapcore.Level]*os.File, error) { levelFiles := make(map[zapcore.Level]*os.File) levels := []zapcore.Level{zap.DebugLevel, zap.InfoLevel, zap.WarnLevel, zap.ErrorLevel, zap.DPanicLevel, zap.PanicLevel, zap.FatalLevel} for _, level := range levels { today := time.Now().Format("2006-01-02") logFile := fmt.Sprintf("./log/%s-%s.log", level.String(), today) file, err := os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { return nil, fmt.Errorf("failed to open log file for level %s: %w", level.String(), err) } levelFiles[level] = file } return levelFiles, nil } // startDailyLogFileSwitcher 启动一个定时任务,每天定时切换日志文件。 func startDailyLogFileSwitcher() { ticker := time.NewTicker(24 * time.Hour) go func() { for range ticker.C { switchLogFile() } }() } // switchLogFile 根据文件大小或日期切换到新的日志文件。 func switchLogFile() { mutex.Lock() defer mutex.Unlock() // 获取当前日志文件信息 currentLogFile := fmt.Sprintf("./log/app-%s.log", time.Now().Format("2006-01-02")) info, err := os.Stat(currentLogFile) if err != nil { Logger.Fatal("Error getting current log file info", zap.Error(err)) } // 如果文件大小超过1GB,或者日期已改变 if info.Size() >= 1<<30 || time.Now().Format("2006-01-02") != info.ModTime().Format("2006-01-02") { // 获取第二天的日期 tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02") newLogFile := fmt.Sprintf("./log/app-%s.log", tomorrow) // 关闭当前日志文件 Logger.Sync() // 重新打开新的日志文件 newFile, err := os.OpenFile(newLogFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { Logger.Fatal("Error opening new log file", zap.Error(err)) } // 更新zap的输出目标 Logger = zap.New(zapcore.NewCore( zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), zapcore.AddSync(newFile), zap.DebugLevel, )) // 记录日志,表明日志文件已切换 Logger.Info("Switched to new log file", zap.String("file", newLogFile)) } } // NewCtx 给 ctx 注入一个 logger, logger 中包含Field(内含日志打印的 k-v对) func NewCtx(ctx context.Context, fields ...zapcore.Field) context.Context { return context.WithValue(ctx, loggerCtxKey, Logger.With(fields...)) } // WithCtx 尝试从 context 中获取带有 traceId Field的 logger func WithCtx(ctx context.Context) *zap.Logger { if ctx == nil { return Logger } ctxLogger, ok := ctx.Value(loggerCtxKey).(*zap.Logger) if ok { return ctxLogger } return Logger }