2025-07-03 09:16:16 +08:00
|
|
|
|
package utils
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"log"
|
|
|
|
|
|
"os"
|
2025-07-03 12:49:49 +08:00
|
|
|
|
"path/filepath"
|
2025-07-03 09:16:16 +08:00
|
|
|
|
"time"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-07-03 12:49:49 +08:00
|
|
|
|
// DailyFileWriter 日志文件写入器
|
2025-07-03 09:16:16 +08:00
|
|
|
|
type DailyFileWriter struct {
|
2025-07-03 12:49:49 +08:00
|
|
|
|
Dir string
|
|
|
|
|
|
Freq string
|
|
|
|
|
|
Prefix string
|
|
|
|
|
|
File *os.File
|
|
|
|
|
|
Current string
|
|
|
|
|
|
Logger *log.Logger
|
2025-07-03 09:16:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 12:49:49 +08:00
|
|
|
|
// NewDailyFileWriter 创建每日日志写入器
|
|
|
|
|
|
func NewDailyFileWriter(dir, freq string, logger *log.Logger, prefix string) *DailyFileWriter {
|
|
|
|
|
|
writer := &DailyFileWriter{
|
|
|
|
|
|
Dir: dir,
|
|
|
|
|
|
Freq: freq,
|
|
|
|
|
|
Prefix: prefix,
|
|
|
|
|
|
Logger: logger,
|
2025-07-03 09:16:16 +08:00
|
|
|
|
}
|
2025-07-03 12:49:49 +08:00
|
|
|
|
writer.rotate()
|
|
|
|
|
|
return writer
|
2025-07-03 09:16:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 12:49:49 +08:00
|
|
|
|
func (w *DailyFileWriter) getLogFileName() string {
|
|
|
|
|
|
baseName := w.Prefix + "-"
|
2025-07-03 09:16:16 +08:00
|
|
|
|
|
2025-07-03 12:49:49 +08:00
|
|
|
|
switch w.Freq {
|
|
|
|
|
|
case "daily":
|
|
|
|
|
|
return baseName + time.Now().Format("2006-01-02") + ".log"
|
|
|
|
|
|
case "hourly":
|
|
|
|
|
|
return baseName + time.Now().Format("2006-01-02-15") + ".log"
|
|
|
|
|
|
default: // 默认按分钟(适合开发)
|
|
|
|
|
|
return baseName + time.Now().Format("2006-01-02-15-04") + ".log"
|
2025-07-03 09:16:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 12:49:49 +08:00
|
|
|
|
func (w *DailyFileWriter) rotate() {
|
|
|
|
|
|
filename := w.getLogFileName()
|
|
|
|
|
|
filePath := filepath.Join(w.Dir, filename)
|
2025-07-03 09:16:16 +08:00
|
|
|
|
|
2025-07-03 12:49:49 +08:00
|
|
|
|
// 如果已经打开了相同的文件,则不需要重新打开
|
|
|
|
|
|
if w.Current == filePath {
|
|
|
|
|
|
return
|
2025-07-03 09:16:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 12:49:49 +08:00
|
|
|
|
// 关闭当前文件(如果存在)
|
|
|
|
|
|
if w.File != nil {
|
|
|
|
|
|
w.File.Close()
|
2025-07-03 09:16:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 12:49:49 +08:00
|
|
|
|
// 确保目录存在(双重保障)
|
|
|
|
|
|
if err := os.MkdirAll(w.Dir, 0755); err != nil {
|
|
|
|
|
|
// 如果创建目录失败,尝试使用备用目录
|
|
|
|
|
|
if w.Dir != "logs" {
|
|
|
|
|
|
w.Logger.Printf("⚠️ 无法创建日志目录 %s,尝试使用备用目录: %v", w.Dir, err)
|
|
|
|
|
|
w.Dir = "logs"
|
|
|
|
|
|
if err := os.MkdirAll(w.Dir, 0755); err != nil {
|
|
|
|
|
|
w.Logger.Fatalf("❌ 无法创建备用日志目录: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
w.Logger.Fatalf("❌ 无法创建日志目录: %v", err)
|
|
|
|
|
|
}
|
2025-07-03 09:16:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 12:49:49 +08:00
|
|
|
|
// 打开文件(如果文件不存在则创建)
|
|
|
|
|
|
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
2025-07-03 09:16:16 +08:00
|
|
|
|
if err != nil {
|
2025-07-03 12:49:49 +08:00
|
|
|
|
// 如果打开文件失败,尝试使用系统临时目录作为备选
|
|
|
|
|
|
fallbackPath := filepath.Join(os.TempDir(), "xk-websocket-logs", filename)
|
|
|
|
|
|
w.Logger.Printf("⚠️ 无法打开日志文件 %s,尝试备选路径: %s (%v)", filePath, fallbackPath, err)
|
|
|
|
|
|
|
|
|
|
|
|
// 确保备选目录存在
|
|
|
|
|
|
if err := os.MkdirAll(filepath.Dir(fallbackPath), 0755); err != nil {
|
|
|
|
|
|
w.Logger.Fatalf("❌ 无法创建备选日志目录: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
file, err = os.OpenFile(fallbackPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
w.Logger.Fatalf("❌ 无法打开备选日志文件: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
w.File = file
|
|
|
|
|
|
w.Current = filePath
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Write 实现io.Writer接口
|
|
|
|
|
|
func (w *DailyFileWriter) Write(p []byte) (n int, err error) {
|
|
|
|
|
|
// 检查是否需要轮转日志
|
|
|
|
|
|
needRotate := false
|
|
|
|
|
|
switch w.Freq {
|
|
|
|
|
|
case "daily":
|
|
|
|
|
|
needRotate = time.Now().Day() != time.Now().Add(-time.Second).Day()
|
|
|
|
|
|
case "hourly":
|
|
|
|
|
|
needRotate = time.Now().Hour() != time.Now().Add(-time.Second).Hour()
|
|
|
|
|
|
default: // 每分钟轮转(开发模式)
|
|
|
|
|
|
needRotate = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if needRotate {
|
|
|
|
|
|
w.rotate()
|
2025-07-03 09:16:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 12:49:49 +08:00
|
|
|
|
return w.File.Write(p)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Close 关闭日志文件
|
|
|
|
|
|
func (w *DailyFileWriter) Close() error {
|
|
|
|
|
|
if w.File != nil {
|
|
|
|
|
|
return w.File.Close()
|
2025-07-03 09:16:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|