diff --git a/CHANGELOG.md b/CHANGELOG.md index fbc579c..b86c94d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # 更新日志 +24w08a +--- +- CHANGE: 同步更新logger模块,与golang-temp项目定义的开发规范保持一致 +- ADD: 新增日志翻转功能 + v1.1.1 --- - CHANGE: 修改部分代码,与golang-temp项目定义的开发规范保持一致 diff --git a/DEV-VERSION b/DEV-VERSION index f77aa1b..6ffcd27 100644 --- a/DEV-VERSION +++ b/DEV-VERSION @@ -1 +1 @@ -24w07b \ No newline at end of file +24w08a \ No newline at end of file diff --git a/config/config.go b/config/config.go index ae3089a..3de2c1e 100644 --- a/config/config.go +++ b/config/config.go @@ -11,6 +11,7 @@ type Config struct { Host string `yaml:"host"` SizeLimit int `yaml:"sizelimit"` LogFilePath string `yaml:"logfilepath"` + MaxLogSize int `yaml:"maxlogsize"` CORSOrigin bool `yaml:"CorsAllowOrigins"` Auth bool `yaml:"auth"` AuthToken string `yaml:"authtoken"` diff --git a/config/config.yaml b/config/config.yaml index 798b1e8..bba4552 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -2,6 +2,7 @@ port: 8080 host: "127.0.0.1" sizelimit: 131072000 # 125MB logfilepath: "/data/ghproxy/log/ghproxy.log" +maxlogsize: 25 #MB CorsAllowOrigins: true auth: false authtoken: "test" diff --git a/logger/logger.go b/logger/logger.go index ae567f2..b6e7057 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -1,30 +1,40 @@ -// logger/logger.go package logger import ( + "archive/tar" + "compress/gzip" "fmt" + "io" "log" "os" + "path/filepath" + "sync" "time" ) var ( - logFile *os.File - logger *log.Logger - logChannel = make(chan string, 100) // 创建一个缓冲通道 - quitChannel = make(chan struct{}) // 用于通知退出 + logw = Logw + logFile *os.File + logger *log.Logger + logChannel = make(chan string, 100) + quitChannel = make(chan struct{}) + logFileMutex sync.Mutex // 保护 logFile 的互斥锁 ) // Init 初始化日志记录器,接受日志文件路径作为参数 -func Init(logFilePath string) error { +func Init(logFilePath string, maxLogsize int) error { + logFileMutex.Lock() + defer logFileMutex.Unlock() + var err error logFile, err = os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) if err != nil { return err } - logger = log.New(logFile, "", 0) // 不使用默认前缀 + logger = log.New(logFile, "", 0) - go logWorker() // 启动 goroutine 处理日志 + go logWorker() + go monitorLogSize(logFilePath, maxLogsize) return nil } @@ -34,30 +44,116 @@ func logWorker() { select { case msg := <-logChannel: timestamp := time.Now().Format("02/Jan/2006:15:04:05 -0700") - logger.Println(timestamp + " - " + msg) // 写入日志 + logger.Println(timestamp + " - " + msg) case <-quitChannel: - return // 退出 goroutine + return } } } // Log 直接记录日志的函数 func Log(customMessage string) { - logChannel <- customMessage // 将日志消息发送到通道 + logChannel <- customMessage } // Logw 用于格式化日志记录 func Logw(format string, args ...interface{}) { - message := fmt.Sprintf(format, args...) // 格式化消息 - Log(message) // 记录日志 + message := fmt.Sprintf(format, args...) + Log(message) } // Close 关闭日志文件 func Close() { + logFileMutex.Lock() + defer logFileMutex.Unlock() + if logFile != nil { - quitChannel <- struct{}{} // 通知日志 goroutine 退出 + quitChannel <- struct{}{} if err := logFile.Close(); err != nil { - Log("Error closing log file: " + err.Error()) // 记录关闭日志时的错误 + fmt.Printf("Error closing log file: %v", err) } } } + +func monitorLogSize(logFilePath string, maxLogsize int) { + var maxLogsizeBytes int64 = int64(maxLogsize) * 1024 * 1024 // 最大日志文件大小,单位为MB + for { + time.Sleep(600 * time.Second) // 每10分钟检查一次 + logFileMutex.Lock() + info, err := logFile.Stat() + logFileMutex.Unlock() + + if err == nil && info.Size() > maxLogsizeBytes { + if err := rotateLogFile(logFilePath); err != nil { + logw("Log Rotation Failed: %s", err) + } + } + } +} + +func rotateLogFile(logFilePath string) error { + logFileMutex.Lock() + defer logFileMutex.Unlock() + + if logFile != nil { + if err := logFile.Close(); err != nil { + logw("Error closing log file for rotation: %v", err) + } + } + + // 打开当前日志文件 + logFile, err := os.Open(logFilePath) + if err != nil { + return fmt.Errorf("failed to open log file: %s, error: %w", logFilePath, err) + } + defer logFile.Close() + + newLogFilePath := logFilePath + "-" + time.Now().Format("20060102-150405") + ".tar.gz" + outFile, err := os.Create(newLogFilePath) + if err != nil { + return fmt.Errorf("failed to create gz file: %s, error: %w", newLogFilePath, err) + } + defer outFile.Close() + + gzWriter, err := gzip.NewWriterLevel(outFile, gzip.BestCompression) + if err != nil { + return fmt.Errorf("failed to create gz writer: %w", err) + } + defer gzWriter.Close() + + tarWriter := tar.NewWriter(gzWriter) + defer tarWriter.Close() + + logFileStat, err := logFile.Stat() + if err != nil { + return fmt.Errorf("failed to stat log file: %s, error: %w", logFilePath, err) + } + + logFileHeader := &tar.Header{ + Name: filepath.Base(logFilePath), + Size: logFileStat.Size(), + Mode: 0644, + ModTime: logFileStat.ModTime(), + } + + if err := tarWriter.WriteHeader(logFileHeader); err != nil { + return fmt.Errorf("failed to write log file header: %s, error: %w", logFilePath, err) + } + + if _, err := io.Copy(tarWriter, logFile); err != nil { + return fmt.Errorf("failed to copy log file: %s, error: %w", logFilePath, err) + } + + if err := os.Truncate(logFilePath, 0); err != nil { + return fmt.Errorf("failed to truncate log file: %s, error: %w", logFilePath, err) + } + + // 重新打开日志文件 + logFile, err = os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + return fmt.Errorf("failed to reopen log file: %s, error: %w", logFilePath, err) + } + logger.SetOutput(logFile) + + return nil +} diff --git a/main.go b/main.go index 7277589..18c8429 100644 --- a/main.go +++ b/main.go @@ -42,7 +42,7 @@ func loadConfig() { func setupLogger() { // 初始化日志模块 var err error - err = logger.Init(cfg.LogFilePath) // 传递日志文件路径 + err = logger.Init(cfg.LogFilePath, cfg.MaxLogSize) // 传递日志文件路径 if err != nil { log.Fatalf("Failed to initialize logger: %v", err) }