package logger import ( "fmt" "io" "log" "os" "path/filepath" "strings" "time" ) // 日志器对象 type Logger struct { env string // 运行环境 filePath string // 文件路径 fileName string // 文件名 level int // 日志等级标识 maxDay int // 保留最长天数 maxSize int64 // 文件最大空间 fileHandle *os.File // 文件实例 logger *log.Logger // 日志实例 logLevelMap map[int]string // 日志等级标识名 logDay int // 日志当前日 } const ( LOG_LEVEL_SILENT = iota LOG_LEVEL_INFO LOG_LEVEL_WARN LOG_LEVEL_ERROR ) // NewLogger 实例日志器对象 func NewLogger(env, fileDir, fileName string, level, maxDay, maxSize int) (*Logger, error) { logFilePath := filepath.Join(fileDir, fileName) if err := os.MkdirAll(filepath.Dir(logFilePath), 0750); err != nil { return nil, fmt.Errorf("failed to mkdir logger dir: %v", err) } fileHandle, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { return nil, fmt.Errorf("failed to open log file: %v", err) } writer := io.Writer(fileHandle) if env == "local" { writer = io.MultiWriter(fileHandle, os.Stderr) } logger := log.New(writer, "", log.LstdFlags|log.Lshortfile) logLevelMap := map[int]string{ LOG_LEVEL_INFO: "INFO", LOG_LEVEL_WARN: "WARN", LOG_LEVEL_ERROR: "ERROR", } stdLogger := &Logger{ env: env, filePath: fileDir, fileName: fileName, level: level, maxDay: maxDay, maxSize: int64(maxSize * 1024 * 1024), fileHandle: fileHandle, logger: logger, logLevelMap: logLevelMap, logDay: time.Now().Day(), } go stdLogger.checkFile() return stdLogger, nil } // checkFile 检查文件分割,自定时调用 func (l *Logger) checkFile() { fileInfo, err := l.fileHandle.Stat() if err != nil { l.logger.Printf("Failed to get log file info: %v\n", err) return } currTime := time.Now() if l.logDay != currTime.Day() { l.logDay = currTime.Day() l.rotateFile(currTime.AddDate(0, 0, -1).Format("2006-01-02")) // 移除超过保存最长天数的文件 l.removeOldFile(currTime.AddDate(0, 0, -l.maxDay)) } else if fileInfo.Size() >= l.maxSize { l.rotateFile(currTime.Format("2006-01-02_150405")) } else if time.Since(fileInfo.ModTime()).Hours() > 24 { l.rotateFile(fileInfo.ModTime().Format("2006-01-02")) } time.AfterFunc(1*time.Minute, l.checkFile) } // rotateFile 检查文件大小进行分割 func (l *Logger) rotateFile(timeFormat string) { l.fileHandle.Close() newFileName := fmt.Sprintf("%s.%s", l.fileName, timeFormat) newFilePath := filepath.Join(l.filePath, newFileName) oldfilePath := filepath.Join(l.filePath, l.fileName) // 重命名 os.Rename(oldfilePath, newFilePath) // 新文件句柄 fileHandle, err := os.OpenFile(oldfilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { l.logger.Printf("Failed to open log file: %v\n", err) return } l.fileHandle = fileHandle // 重新设置 logger 的 writer writer := io.Writer(l.fileHandle) if l.env == "local" { writer = io.MultiWriter(l.fileHandle, os.Stderr) } l.logger.SetOutput(writer) } // RemoveOldFile 删除旧文件 func (l *Logger) removeOldFile(oldFileDate time.Time) { // 遍历目标文件夹中的文件 files, err := os.ReadDir(l.filePath) if err != nil { l.Errorf("logger RemoveOldFile ReadDir err: %v", err.Error()) return } for _, file := range files { // 跳过非指定日志文件名 if !strings.HasPrefix(file.Name(), l.fileName+".") { continue } idx := strings.LastIndex(file.Name(), ".") if idx == -1 { continue } dateStr := file.Name()[idx+1 : idx+11] // 解析日期字符串 fileDate, err := time.Parse("2006-01-02", dateStr) if err != nil { l.Errorf("logger RemoveOldFile Parse err: %v", err.Error()) continue } // 判断文件日期是否在给定日期之前 if fileDate.Before(oldFileDate) { // 删除旧文件 err := os.Remove(filepath.Join(l.filePath, file.Name())) if err != nil { l.Errorf("logger RemoveOldFile Remove err: %v", err.Error()) continue } } } } // writeLog 写入chan func (l *Logger) writeLog(level int, format string, args ...interface{}) { if level < l.level { return } logMsg := fmt.Sprintf("[%s] %s\n", l.logLevelMap[level], fmt.Sprintf(format, args...)) l.logger.Output(4, logMsg) } func (l *Logger) Infof(format string, args ...interface{}) { l.writeLog(LOG_LEVEL_INFO, format, args...) } func (l *Logger) Warnf(format string, args ...interface{}) { l.writeLog(LOG_LEVEL_WARN, format, args...) } func (l *Logger) Errorf(format string, args ...interface{}) { l.writeLog(LOG_LEVEL_ERROR, format, args...) } // Close 日志关闭 func (l *Logger) Close() { err := l.fileHandle.Close() if err != nil { l.logger.Printf("Failed to close log file: %v\n", err) } }