package gologger import ( "os" "path/filepath" "regexp" "strconv" "time" ) type FileAppender struct { formatter LogFormatter filePath string lchan chan LogEvent file *os.File stopChan chan struct{} // 新增滚动相关字段 EnableRolling bool MaxSize int64 // 文件最大大小(字节) MaxAge int64 // 文件最大时间(秒) currentFileSize int64 // 当前文件大小 createdAt int64 // 文件创建时间戳 } // Close implements LoggerAppender. func (f *FileAppender) Close() { //send stop signal f.stopChan <- struct{}{} } func (f *FileAppender) GetName() string { return "FileAppender:" + f.filePath } func (f *FileAppender) start() { f.lchan = make(chan LogEvent, 10) f.stopChan = make(chan struct{}) if f.file == nil || int(f.file.Fd()) == -1 { dirName := filepath.Dir(f.filePath) _, err := os.Stat(dirName) if err != nil && os.IsNotExist(err) { os.MkdirAll(dirName, 0755) } f.file, _ = os.OpenFile(f.filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) // 获取当前文件大小 if stat, err := f.file.Stat(); err == nil { f.currentFileSize = stat.Size() } // 记录文件创建时间 f.createdAt = time.Now().Unix() } go func() { defer f.file.Close() for { select { case <-f.stopChan: return case logEvent := <-f.lchan: // 检查日志滚动 if f.EnableRolling { f.checkAndRoll(logEvent) } logMsg := f.formatter(logEvent) f.file.WriteString(logMsg) } } }() } // 新增日志滚动方法 func (f *FileAppender) checkAndRoll(logEvent LogEvent) { // 按大小滚动 if f.MaxSize > 0 && f.currentFileSize >= f.MaxSize { f.rollFile(logEvent) return } // 按时间滚动 if f.MaxAge > 0 && time.Now().Unix()-f.createdAt >= f.MaxAge { f.rollFile(logEvent) } } // 日志文件滚动 func (f *FileAppender) rollFile(logEvent LogEvent) { // 关闭当前文件 f.file.Close() // 重命名旧文件 timestamp := time.Now().Format("20060102150405") newPath := f.filePath + "." + timestamp os.Rename(f.filePath, newPath) // 创建新文件 f.file, _ = os.OpenFile(f.filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) // 重置状态 f.currentFileSize = 0 f.createdAt = time.Now().Unix() // 重新写入当前日志 logMsg := format(logEvent) f.file.WriteString(logMsg) } func (f *FileAppender) Append(logEvent LogEvent) { f.lchan <- logEvent } func makeFileAppender(appenderConfig LogAppenderConfig) *LoggerAppender { var logfile interface{} var ok bool logfile, ok = appenderConfig.Options["file"] if !ok { logfile = "default.log" } // 新增滚动配置参数 rollingEnabled := false if enable, ok := appenderConfig.Options["enableRolling"].(bool); ok { rollingEnabled = enable } maxSize := int64(0) if size, ok := appenderConfig.Options["maxSize"].(int64); ok { maxSize = size * 1024 * 1024 // 将兆转换为字节 } else if sizeStr, ok := appenderConfig.Options["maxSize"].(string); ok { // 解析带单位的大小配置(支持KB, MB, GB) var re = regexp.MustCompile(`(?i)^(\d+)([kmg]b)?$`) matches := re.FindStringSubmatch(sizeStr) if len(matches) > 0 { num, _ := strconv.ParseInt(matches[1], 10, 64) unit := "" if len(matches) > 2 { unit = matches[2] } switch unit { case "KB", "kb": maxSize = num * 1024 case "MB", "mb": maxSize = num * 1024 * 1024 case "GB", "gb": maxSize = num * 1024 * 1024 * 1024 default: maxSize = num * 1024 * 1024 // 默认按MB处理 } } } maxAge := int64(0) if ageStr, ok := appenderConfig.Options["maxAge"].(string); ok { // 解析带单位的时间配置 re := regexp.MustCompile(`^\d+[hd]?$`) if re.MatchString(ageStr) { // 提取数字部分和单位 numStr := "" unit := "" for _, c := range ageStr { if c >= '0' && c <= '9' { numStr += string(c) } else { unit = string(c) } } num, _ := strconv.ParseInt(numStr, 10, 64) switch unit { case "h": maxAge = num * 3600 // 小时转秒 default: maxAge = num * 86400 // 默认按天转秒 } } } else if ageInt, ok := appenderConfig.Options["maxAge"].(int64); ok { maxAge = ageInt } var ret LoggerAppender = &FileAppender{ formatter: SelectFormatter(appenderConfig.Formatter), filePath: logfile.(string), EnableRolling: rollingEnabled, MaxSize: maxSize, MaxAge: maxAge, } ret.(*FileAppender).start() return &ret } func init() { RegistAppender("file", makeFileAppender) }