This commit is contained in:
lpf
2026-02-13 13:50:09 +08:00
parent f88a78ef8b
commit 085c265319
15 changed files with 1485 additions and 179 deletions

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
@@ -37,7 +38,11 @@ var (
)
type Logger struct {
file *os.File
file *os.File
filePath string
maxSizeBytes int64
maxAgeDays int
fileMu sync.Mutex
}
type LogEntry struct {
@@ -68,9 +73,25 @@ func GetLevel() LogLevel {
}
func EnableFileLogging(filePath string) error {
return EnableFileLoggingWithRotation(filePath, 20, 3)
}
func EnableFileLoggingWithRotation(filePath string, maxSizeMB, maxAgeDays int) error {
mu.Lock()
defer mu.Unlock()
if maxSizeMB <= 0 {
maxSizeMB = 20
}
if maxAgeDays <= 0 {
maxAgeDays = 3
}
dir := filepath.Dir(filePath)
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("failed to create log directory: %w", err)
}
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return fmt.Errorf("failed to open log file: %w", err)
@@ -81,6 +102,12 @@ func EnableFileLogging(filePath string) error {
}
logger.file = file
logger.filePath = filePath
logger.maxSizeBytes = int64(maxSizeMB) * 1024 * 1024
logger.maxAgeDays = maxAgeDays
if err := logger.cleanupOldLogFiles(); err != nil {
log.Println("Failed to clean up old log files:", err)
}
log.Println("File logging enabled:", filePath)
return nil
}
@@ -92,6 +119,9 @@ func DisableFileLogging() {
if logger.file != nil {
logger.file.Close()
logger.file = nil
logger.filePath = ""
logger.maxSizeBytes = 0
logger.maxAgeDays = 0
log.Println("File logging disabled")
}
}
@@ -119,7 +149,9 @@ func logMessage(level LogLevel, component string, message string, fields map[str
if logger.file != nil {
jsonData, err := json.Marshal(entry)
if err == nil {
logger.file.WriteString(string(jsonData) + "\n")
if err := logger.writeLine(append(jsonData, '\n')); err != nil {
log.Println("Failed to write file log:", err)
}
}
}
@@ -143,6 +175,88 @@ func logMessage(level LogLevel, component string, message string, fields map[str
}
}
func (l *Logger) writeLine(line []byte) error {
l.fileMu.Lock()
defer l.fileMu.Unlock()
if l.file == nil {
return nil
}
if l.maxSizeBytes > 0 {
if err := l.rotateIfNeeded(int64(len(line))); err != nil {
return err
}
}
_, err := l.file.Write(line)
return err
}
func (l *Logger) rotateIfNeeded(nextWrite int64) error {
info, err := l.file.Stat()
if err != nil {
return err
}
if info.Size()+nextWrite <= l.maxSizeBytes {
return nil
}
if err := l.file.Close(); err != nil {
return err
}
backupPath := fmt.Sprintf("%s.%s", l.filePath, time.Now().UTC().Format("20060102-150405"))
if err := os.Rename(l.filePath, backupPath); err != nil {
return err
}
file, err := os.OpenFile(l.filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return err
}
l.file = file
return l.cleanupOldLogFiles()
}
func (l *Logger) cleanupOldLogFiles() error {
if l.maxAgeDays <= 0 || l.filePath == "" {
return nil
}
dir := filepath.Dir(l.filePath)
base := filepath.Base(l.filePath)
entries, err := os.ReadDir(dir)
if err != nil {
return err
}
cutoff := time.Now().AddDate(0, 0, -l.maxAgeDays)
for _, entry := range entries {
if entry.IsDir() {
continue
}
name := entry.Name()
// Only delete rotated files like clawgo.log.20260213-120000
if !strings.HasPrefix(name, base+".") {
continue
}
info, err := entry.Info()
if err != nil {
continue
}
if info.ModTime().Before(cutoff) {
_ = os.Remove(filepath.Join(dir, name))
}
}
return nil
}
func formatComponent(component string) string {
if component == "" {
return ""