122 lines
3.0 KiB
Go
122 lines
3.0 KiB
Go
package utils
|
||
|
||
import (
|
||
"log"
|
||
"os"
|
||
"path/filepath"
|
||
"time"
|
||
)
|
||
|
||
// DailyFileWriter 日志文件写入器
|
||
type DailyFileWriter struct {
|
||
Dir string
|
||
Freq string
|
||
Prefix string
|
||
File *os.File
|
||
Current string
|
||
Logger *log.Logger
|
||
}
|
||
|
||
// NewDailyFileWriter 创建每日日志写入器
|
||
func NewDailyFileWriter(dir, freq string, logger *log.Logger, prefix string) *DailyFileWriter {
|
||
writer := &DailyFileWriter{
|
||
Dir: dir,
|
||
Freq: freq,
|
||
Prefix: prefix,
|
||
Logger: logger,
|
||
}
|
||
writer.rotate()
|
||
return writer
|
||
}
|
||
|
||
func (w *DailyFileWriter) getLogFileName() string {
|
||
baseName := w.Prefix + "-"
|
||
|
||
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"
|
||
}
|
||
}
|
||
|
||
func (w *DailyFileWriter) rotate() {
|
||
filename := w.getLogFileName()
|
||
filePath := filepath.Join(w.Dir, filename)
|
||
|
||
// 如果已经打开了相同的文件,则不需要重新打开
|
||
if w.Current == filePath {
|
||
return
|
||
}
|
||
|
||
// 关闭当前文件(如果存在)
|
||
if w.File != nil {
|
||
w.File.Close()
|
||
}
|
||
|
||
// 确保目录存在(双重保障)
|
||
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)
|
||
}
|
||
}
|
||
|
||
// 打开文件(如果文件不存在则创建)
|
||
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
||
if err != nil {
|
||
// 如果打开文件失败,尝试使用系统临时目录作为备选
|
||
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()
|
||
}
|
||
|
||
return w.File.Write(p)
|
||
}
|
||
|
||
// Close 关闭日志文件
|
||
func (w *DailyFileWriter) Close() error {
|
||
if w.File != nil {
|
||
return w.File.Close()
|
||
}
|
||
return nil
|
||
}
|