uniugm/admin/lib/xlog/handler/handler_file_rotate_daymax.go
2025-04-18 17:17:23 +08:00

203 lines
4.6 KiB
Go
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 handler
import (
"fmt"
"os"
"time"
"admin/lib/xlog/handler/logsyscall"
)
type RotatingDayMaxFileHandler struct {
baseName string
outPath string
fd *os.File
rotateInfo struct {
maxBytes int // 单个日志文件最大长度
maxBackupCount int // 最大归档文件数量
day time.Time // 记录跨天
}
}
// NewRotatingDayMaxFileHandler 每天00点滚动当超过大小也滚动
func NewRotatingDayMaxFileHandler(outPath, baseName string, maxBytes int, backupCount int) (*RotatingDayMaxFileHandler, error) {
if outPath == "" {
outPath = "log/"
}
h := new(RotatingDayMaxFileHandler)
h.baseName = baseName
h.outPath = outPath
h.rotateInfo.day = time.Now()
h.rotateInfo.maxBytes = maxBytes
h.rotateInfo.maxBackupCount = backupCount
fd, err := openFile(h.outPath, h.baseName, false)
if err != nil {
panic(err)
} else {
h.fd = fd
}
go h.rotateHandler()
return h, nil
}
func (h *RotatingDayMaxFileHandler) Write(p []byte) (n int, err error) {
return h.fd.Write(p)
}
func (h *RotatingDayMaxFileHandler) Close() error {
if h.fd != nil {
return h.fd.Close()
}
return nil
}
func (h *RotatingDayMaxFileHandler) rotateHandler() {
for {
time.Sleep(time.Second)
h.tryRotate()
}
}
func (h *RotatingDayMaxFileHandler) tryRotate() {
// 校验时间是否触发归档
now := time.Now()
if now.Day() != h.rotateInfo.day.Day() {
h.rotateDay()
h.rotateInfo.day = now
return
}
// 校验文件大小是否触发归档
size, err := calcFileSize(h.fd)
if err != nil {
outErrorLog("stat xlog file(%v) error:%v/home/likun/work/idlerpg-server/internal/utils/xlog", h.baseName, err)
return
}
if h.rotateInfo.maxBytes > 0 && h.rotateInfo.maxBytes <= size {
h.rotateSize()
return
}
}
// rotateAt 到点触发归档
func (h *RotatingDayMaxFileHandler) rotateDay() {
// 创建新的一天的日志文件
newFd, err := openFile(h.outPath, h.baseName, false)
if err != nil {
outErrorLog("new day rotate file, but open new file(%v) error:%v", h.baseName, err)
return
}
// 用新的一天的日志文件描述符接管当前使用的
oldFd := h.fd
h.fd = newFd
oldFd.Close()
}
// rotateSize 文件过大触发归档
func (h *RotatingDayMaxFileHandler) rotateSize() {
// 锁定文件,使触发归档的别的进程也锁住
lockFile(h.fd)
// 重新打开文件判断大小,防止文件被别的归档进程改名
curSize := calcFileNameSize(h.fd.Name())
if curSize < h.rotateInfo.maxBytes {
// 别的进程归档过了
unlockFile(h.fd)
return
}
// 滚动copy归档的文件那么归档的1号文件空出来了
baseFileName := h.fd.Name()
for i := h.rotateInfo.maxBackupCount; i > 0; i-- {
sfn := fmt.Sprintf("%s.%d", baseFileName, i)
dfn := fmt.Sprintf("%s.%d", baseFileName, i+1)
os.Rename(sfn, dfn)
}
// 将当前文件内容拷贝到归档1号文件
dfn := fmt.Sprintf("%s.1", baseFileName)
os.Rename(baseFileName, dfn)
// 重新创建当前日志文件得到新的描述符
newFd, err := os.OpenFile(baseFileName, os.O_CREATE|os.O_APPEND|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
outErrorLog("rotate xlog file size, open file(%v) error:%v", baseFileName, err)
unlockFile(h.fd)
} else {
oldFd := h.fd
h.fd = newFd
unlockFile(h.fd)
oldFd.Close()
}
}
func calcFileSize(fd *os.File) (int, error) {
st, err := fd.Stat()
return int(st.Size()), err
}
func calcFileNameSize(fileName string) int {
fd, err := os.OpenFile(fileName, os.O_RDWR|os.O_APPEND, 0777)
if err != nil {
return 0
}
defer fd.Close()
st, err := fd.Stat()
if err != nil {
return 0
}
return int(st.Size())
}
var fileTimeStampFormat = "20060102"
func openFile(path, baseName string, isTrunc bool) (*os.File, error) {
os.MkdirAll(path, 0777)
timeNow := time.Now()
t := timeNow.Format(fileTimeStampFormat)
fullFileName := baseName + "-" + t + ".log"
if path != "" {
if path[len(path)-1] != '/' {
path += "/"
}
}
fullPathFileName := path + fullFileName
var fd *os.File
var err error
flag := os.O_CREATE | os.O_APPEND | os.O_RDWR
if isTrunc {
flag |= os.O_TRUNC
}
fd, err = os.OpenFile(fullPathFileName, flag, 0777)
if err != nil {
return fd, fmt.Errorf("open xlog file(%v) error:%v", fullPathFileName, err)
}
return fd, nil
}
func lockFile(fd *os.File) {
err := logsyscall.Flock(int(fd.Fd()), logsyscall.LOCK_EX)
if err != nil {
outErrorLog("lock file(%v) error:%v", fd.Name(), err)
}
}
func unlockFile(fd *os.File) {
err := logsyscall.Flock(int(fd.Fd()), logsyscall.LOCK_UN)
if err != nil {
outErrorLog("unlock file(%v) error:%v", fd.Name(), err)
}
}
func outErrorLog(format string, values ...interface{}) {
fmt.Fprintf(os.Stderr, format+"\n", values...)
}