goProject/.svn/pristine/88/880f8b2789a7eb735412542d295753784c122a5c.svn-base
2025-01-06 16:21:36 +08:00

204 lines
4.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package impl_localfile
import (
"fmt"
"log"
"os"
"path/filepath"
"strconv"
"sync"
"time"
)
const (
con_FILE_SUFFIX = "txt"
defaultPanicNum = 10 // 默认支持的连续panic次数
defaultPanicIntervals = 5 // 秒
fileMaxSize = 1 << 27 // 128M
)
type fileLog struct {
logPath string
// fileDir + fileName 用于判断是否需要切换日志文件
fileDir string
fileName string
lv levelType
msgChan chan logObject
f *os.File
// 如果出现未知panic并一直发生那么应该要真正的panic
panicNum int
panicTime time.Time
// 当前已经写入的大小
curWriteSize int64
}
func newLog(logPath string, lv levelType) *fileLog {
f := &fileLog{
logPath: logPath,
fileDir: "",
fileName: "",
msgChan: make(chan logObject, 10240),
lv: lv,
f: nil,
}
go f.loop()
return f
}
func (f *fileLog) loop() {
defer func() {
if r := recover(); r != nil {
// 将错误输出,而不是记录到文件,是因为可能导致死循环
fmt.Println("log file lv:", f.lv, " err:", r)
// 超过间隔时间重置
if time.Since(f.panicTime)/time.Second > defaultPanicIntervals {
f.panicNum = 0
f.panicTime = time.Now()
}
// 这里处理连续panic 也是防止循环调用形成死循环
f.panicNum++
if f.panicNum >= defaultPanicNum {
panic(r)
}
}
go f.loop()
}()
for logItem := range f.msgChan {
f.writeLog(logItem)
}
}
func (f *fileLog) writeLog(logObj logObject) {
// 由于不使用直接 logUtil 调用logUtilPlus 存在终端打印的控制,所以 logUtil 只作为纯文件日志组件
// if PrintImportantLog && (logObj.level == warn || logObj.level == _error || logObj.level == fatal) {
// fmt.Println(logObj.logInfo)
// }
// 检查是否需要去切换文件或者创建文件
f.checkFileAndLoop(logObj.level, logObj.ifIncludeHour)
f.curWriteSize += int64(len(logObj.logInfo))
_, _ = f.f.WriteString(logObj.logInfo)
}
// 检查文件是否需要切换
func (f *fileLog) checkFileAndLoop(level levelType, ifIncludeHour bool) {
// 获取当前时间
now := time.Now()
fileAbsoluteDirectory := filepath.Join(f.logPath, strconv.Itoa(now.Year()), strconv.Itoa(int(now.Month())))
fileName := ""
if ifIncludeHour {
fileName = fmt.Sprintf("%s.%s.%s", time.Now().Format("2006-01-02-15"), level, con_FILE_SUFFIX)
} else {
fileName = fmt.Sprintf("%s.%s.%s", time.Now().Format("2006-01-02"), level, con_FILE_SUFFIX)
}
// 说明已经存在, 检查文件大小,并切换
if f.f != nil && f.fileName == fileName && f.fileDir == fileAbsoluteDirectory {
if f.curWriteSize < fileMaxSize {
return
}
// 大小超额切换文件
f.switchFile()
return
}
// 创建文件夹
if f.fileDir != fileAbsoluteDirectory {
if err := os.MkdirAll(fileAbsoluteDirectory, os.ModePerm|os.ModeTemporary); err != nil {
log.Println("make dir all is err :", err)
}
f.fileDir = fileAbsoluteDirectory
}
// 创建文件
if f.fileName != fileName {
f.fileName = fileName
}
// 到这里说明要切换了关闭之前的file
if f.f != nil {
_ = f.f.Close()
}
// 正常切换文件
f.createFile()
}
func (f *fileLog) switchFile() {
// 关闭文件
_ = f.f.Close()
// 重命名
curFileName := filepath.Join(f.fileDir, f.fileName)
newFileName := filepath.Join(f.fileDir, fmt.Sprintf("%s.%s.%s", f.fileName, time.Now().Format("15-04-05.999"), con_FILE_SUFFIX))
err := os.Rename(curFileName, newFileName)
if err != nil {
log.Println(err)
}
// 再次创建文件
f.createFile()
}
func (f *fileLog) createFile() {
// 得到最终的文件绝对路径
fileAbsolutePath := filepath.Join(f.fileDir, f.fileName)
// 打开文件(如果文件存在就以读写模式打开,并追加写入;如果文件不存在就创建,然后以写模式打开。)
file, err := os.OpenFile(fileAbsolutePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm|os.ModeTemporary)
if err != nil {
log.Println("open file is err :", err)
return
}
f.f = file
stat, _ := file.Stat()
f.curWriteSize = stat.Size()
}
func (f *fileLog) SetLogPath(logPath string) {
f.logPath = logPath
}
// Close 最后等待 3s 钟
func (f *fileLog) Close(wg *sync.WaitGroup, waitFinish bool) {
ticker := time.NewTicker(3 * time.Second)
defer func() {
ticker.Stop()
if f.f != nil {
_ = f.f.Close()
}
wg.Done()
}()
for {
if !waitFinish || len(f.msgChan) == 0 {
break
}
select {
case <-ticker.C:
fmt.Println("wait close log file timeout:3s")
return
default:
// 1ms 写入文件500条最多等待 15ms 检查一次就行
time.Sleep(15 * time.Millisecond)
}
}
}