2026-05-02 23:39:10 +08:00
|
|
|
package log
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// FileWriter 文件写入器
|
|
|
|
|
type FileWriter struct {
|
|
|
|
|
fileName string
|
|
|
|
|
lastSplit string
|
|
|
|
|
splitTag string
|
|
|
|
|
fp *os.File
|
|
|
|
|
bufWriter *bufio.Writer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
files = make(map[string]*FileWriter)
|
|
|
|
|
filesLock sync.RWMutex
|
|
|
|
|
)
|
|
|
|
|
|
2026-05-05 13:23:25 +08:00
|
|
|
// Write 由外层的 writerRunner 单协程调用,绝对并发安全,无需加锁
|
|
|
|
|
func (f *FileWriter) Write(tm time.Time, data []byte) {
|
|
|
|
|
nowSplit := tm.Format(f.splitTag)
|
|
|
|
|
|
|
|
|
|
// 1. 文件切割逻辑 (按天/按小时流转)
|
|
|
|
|
if f.lastSplit != nowSplit || f.fp == nil {
|
|
|
|
|
f.rotateFile(nowSplit)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 直接写内存缓冲区
|
|
|
|
|
if f.bufWriter != nil {
|
|
|
|
|
_, err := f.bufWriter.Write(data)
|
|
|
|
|
if err == nil {
|
|
|
|
|
f.bufWriter.WriteByte('\n') // 追加换行
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 降级:如果文件句柄异常,打印到控制台避免丢失
|
|
|
|
|
fmt.Println(string(data))
|
2026-05-02 23:39:10 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-05 13:23:25 +08:00
|
|
|
// Run 充当 Flush 的角色,由外层的 200ms Ticker 定时调用
|
2026-05-02 23:39:10 +08:00
|
|
|
func (f *FileWriter) Run() {
|
2026-05-05 13:23:25 +08:00
|
|
|
if f.bufWriter != nil {
|
|
|
|
|
_ = f.bufWriter.Flush()
|
2026-05-02 23:39:10 +08:00
|
|
|
}
|
2026-05-05 13:23:25 +08:00
|
|
|
}
|
2026-05-02 23:39:10 +08:00
|
|
|
|
2026-05-05 13:23:25 +08:00
|
|
|
// 处理文件轮转的内部方法
|
|
|
|
|
func (f *FileWriter) rotateFile(nowSplit string) {
|
|
|
|
|
if f.bufWriter != nil {
|
|
|
|
|
_ = f.bufWriter.Flush()
|
|
|
|
|
}
|
|
|
|
|
if f.fp != nil {
|
|
|
|
|
_ = f.fp.Close()
|
|
|
|
|
}
|
2026-05-02 23:39:10 +08:00
|
|
|
|
2026-05-05 13:23:25 +08:00
|
|
|
var err error
|
|
|
|
|
f.fp, err = os.OpenFile(f.fileName+"."+nowSplit, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
|
|
|
|
if err == nil {
|
|
|
|
|
// 分配 64KB 缓冲区
|
|
|
|
|
f.bufWriter = bufio.NewWriterSize(f.fp, 64*1024)
|
|
|
|
|
f.lastSplit = nowSplit
|
|
|
|
|
} else {
|
|
|
|
|
f.bufWriter = nil
|
|
|
|
|
fmt.Printf("failed to open log file: %s.%s, error: %v\n", f.fileName, nowSplit, err)
|
2026-05-02 23:39:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *FileWriter) Close() {
|
|
|
|
|
if f.bufWriter != nil {
|
|
|
|
|
_ = f.bufWriter.Flush()
|
|
|
|
|
f.bufWriter = nil
|
|
|
|
|
}
|
|
|
|
|
if f.fp != nil {
|
|
|
|
|
_ = f.fp.Close()
|
|
|
|
|
f.fp = nil
|
|
|
|
|
}
|
|
|
|
|
}
|