log/file_writer.go

85 lines
1.7 KiB
Go
Raw Permalink Normal View History

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
)
// 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))
}
// Run 充当 Flush 的角色,由外层的 200ms Ticker 定时调用
func (f *FileWriter) Run() {
if f.bufWriter != nil {
_ = f.bufWriter.Flush()
}
}
// 处理文件轮转的内部方法
func (f *FileWriter) rotateFile(nowSplit string) {
if f.bufWriter != nil {
_ = f.bufWriter.Flush()
}
if f.fp != nil {
_ = f.fp.Close()
}
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)
}
}
func (f *FileWriter) Close() {
if f.bufWriter != nil {
_ = f.bufWriter.Flush()
f.bufWriter = nil
}
if f.fp != nil {
_ = f.fp.Close()
f.fp = nil
}
}