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

208 lines
4.2 KiB
Go

package handler
import (
"bufio"
"fmt"
"io"
"os"
"strings"
"time"
)
const (
kRollPerSeconds = 60 * 60 * 24 // one day
)
var (
pid = os.Getpid()
host = "unknowhost"
)
func init() {
h, err := os.Hostname()
if err == nil {
host = shortHostname(h)
}
}
func shortHostname(hostname string) string {
if i := strings.Index(hostname, "."); i >= 0 {
return hostname[:i]
}
return hostname
}
func logFileName(basename string) string {
now := time.Now()
name := fmt.Sprintf("%s.%04d%02d%02d-%02d%02d%02d.%s.%d.xlog",
basename,
now.Year(),
now.Month(),
now.Day(),
now.Hour(),
now.Minute(),
now.Second(),
host,
pid,
)
return name
}
type RotatingFileAtDayHandler struct {
baseName string
rollSize int
flushInterval int64
checkEveryN int
syncFlush bool
count int
startofPeriod int64
lastRoll int64
lastFlush int64
file *appendFile
}
// baseName 日志文件的基本名包含全路径 如 "/tmp/test"
// rollSize 每写入rollSize字节日志滚动文件
// flushInterval 刷新文件写入缓存的间隔
// checkEveryN 每写入checkEveryN次 检查文件回滚和缓存刷新
// syncFlush == true flushInterval和checkEveryN 失效
func NewRotatingFileAtDayHandler(baseName string, rollSize int,
flushInterval int64, checkEveryN int, syncFlush bool) *RotatingFileAtDayHandler {
hander := &RotatingFileAtDayHandler{
baseName: baseName,
rollSize: rollSize,
flushInterval: flushInterval,
checkEveryN: checkEveryN,
syncFlush: syncFlush,
count: 0,
startofPeriod: 0,
lastRoll: 0,
lastFlush: 0,
}
hander.rollFile()
return hander
}
func NewDefaultRotatingFileAtDayHandler(baseName string,
rollSize int) *RotatingFileAtDayHandler {
//checkEveryN 开发期间默认设置为1 上线后调高已提高处理性能
return NewRotatingFileAtDayHandler(baseName, rollSize, 3, 1, true)
}
func (self *RotatingFileAtDayHandler) Write(b []byte) (int, error) {
n, err := self.file.append(b)
if err != nil {
return n, err
}
if self.file.writtenBytes() > self.rollSize {
self.rollFile()
} else {
self.count++
if self.count >= self.checkEveryN || self.syncFlush {
self.count = 0
now := time.Now().Unix()
thisPeriod := now / kRollPerSeconds * kRollPerSeconds
if thisPeriod != self.startofPeriod {
self.rollFile()
} else if now-self.lastFlush > self.flushInterval || self.syncFlush {
self.lastFlush = now
err = self.file.flush()
}
}
}
return n, err
}
func (self *RotatingFileAtDayHandler) Rotate() {
}
func (self *RotatingFileAtDayHandler) flush() error {
return self.file.flush()
}
func (self *RotatingFileAtDayHandler) rollFile() bool {
fileName := logFileName(self.baseName)
t := time.Now().Unix()
start := t / kRollPerSeconds * kRollPerSeconds
//滚动时间间隔最小为1秒
if t > self.lastRoll {
self.lastRoll = t
self.lastFlush = t
self.startofPeriod = start
if self.file != nil {
self.file.close()
}
tmpFile, _ := newAppendFile(fileName)
self.file = tmpFile
return true
}
return false
}
func (self *RotatingFileAtDayHandler) Close() error {
return self.file.close()
}
type appendFile struct {
file *os.File
writer *bufio.Writer
writtenBytes_ int
}
func newAppendFile(filename string) (*appendFile, error) {
file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
if err != nil {
return nil, err
}
return &appendFile{
file: file,
writer: bufio.NewWriterSize(file, 64*1024),
writtenBytes_: 0,
}, nil
}
func (self *appendFile) append(b []byte) (int, error) {
length := len(b)
remain := length
offset := 0
var err error
for remain > 0 {
x, err := self.writer.Write(b[offset:])
if err != nil {
if err != io.ErrShortWrite {
break
}
}
offset = offset + x
remain = length - offset
}
self.writtenBytes_ = self.writtenBytes_ + length
return offset, err
}
func (self *appendFile) writtenBytes() int {
return self.writtenBytes_
}
func (self *appendFile) flush() error {
return self.writer.Flush()
}
func (self *appendFile) close() error {
err := self.writer.Flush()
for err != nil {
if err == io.ErrShortWrite {
err = self.writer.Flush()
} else {
break
}
}
if err != nil {
return err
}
return self.file.Close()
}