gologger/file.go

207 lines
4.8 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 gologger
import (
"os"
"path/filepath"
"regexp"
"strconv"
"time"
)
// FileAppender implements the LoggerAppender interface for file output.
// It supports log rotation based on file size and age.
// Example:
// appender := &FileAppender{
// formatter: NewSimpleFormatter(),
// filePath: "app.log",
// EnableRolling: true,
// MaxSize: 1024 * 1024 * 10, // 10MB
// }
// appender.Append(event)
// appender.Close()
type FileAppender struct {
formatter LogFormatter
filePath string
lchan chan LogEvent
file *os.File
stopChan chan struct{}
// 新增滚动相关字段
EnableRolling bool
MaxSize int64 // 文件最大大小(字节)
MaxAge int64 // 文件最大时间(秒)
currentFileSize int64 // 当前文件大小
createdAt int64 // 文件创建时间戳
}
// Close implements LoggerAppender.
func (f *FileAppender) Close() {
//send stop signal
f.stopChan <- struct{}{}
}
func (f *FileAppender) GetName() string {
return "FileAppender:" + f.filePath
}
func (f *FileAppender) start() {
f.lchan = make(chan LogEvent, 10)
f.stopChan = make(chan struct{})
if f.file == nil || int(f.file.Fd()) == -1 {
dirName := filepath.Dir(f.filePath)
_, err := os.Stat(dirName)
if err != nil && os.IsNotExist(err) {
os.MkdirAll(dirName, 0755)
}
f.file, _ = os.OpenFile(f.filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
// 获取当前文件大小
if stat, err := f.file.Stat(); err == nil {
f.currentFileSize = stat.Size()
}
// 记录文件创建时间
f.createdAt = time.Now().Unix()
}
go func() {
defer f.file.Close()
for {
select {
case <-f.stopChan:
return
case logEvent := <-f.lchan:
// 检查日志滚动
if f.EnableRolling {
f.checkAndRoll(logEvent)
}
logMsg := f.formatter(logEvent)
f.file.WriteString(logMsg)
}
}
}()
}
// 新增日志滚动方法
func (f *FileAppender) checkAndRoll(logEvent LogEvent) {
// 按大小滚动
if f.MaxSize > 0 && f.currentFileSize >= f.MaxSize {
f.rollFile(logEvent)
return
}
// 按时间滚动
if f.MaxAge > 0 && time.Now().Unix()-f.createdAt >= f.MaxAge {
f.rollFile(logEvent)
}
}
// 日志文件滚动
func (f *FileAppender) rollFile(logEvent LogEvent) {
// 关闭当前文件
f.file.Close()
// 重命名旧文件
timestamp := time.Now().Format("20060102150405")
newPath := f.filePath + "." + timestamp
os.Rename(f.filePath, newPath)
// 创建新文件
f.file, _ = os.OpenFile(f.filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
// 重置状态
f.currentFileSize = 0
f.createdAt = time.Now().Unix()
// 重新写入当前日志
logMsg := format(logEvent)
f.file.WriteString(logMsg)
}
func (f *FileAppender) Append(logEvent LogEvent) {
f.lchan <- logEvent
}
func makeFileAppender(appenderConfig LogAppenderConfig) *LoggerAppender {
var logfile interface{}
var ok bool
logfile, ok = appenderConfig.Options["file"]
if !ok {
logfile = "default.log"
}
// 新增滚动配置参数
rollingEnabled := false
if enable, ok := appenderConfig.Options["enableRolling"].(bool); ok {
rollingEnabled = enable
}
maxSize := int64(0)
if size, ok := appenderConfig.Options["maxSize"].(int64); ok {
maxSize = size * 1024 * 1024 // 将兆转换为字节
} else if sizeStr, ok := appenderConfig.Options["maxSize"].(string); ok {
// 解析带单位的大小配置支持KB, MB, GB
var re = regexp.MustCompile(`(?i)^(\d+)([kmg]b)?$`)
matches := re.FindStringSubmatch(sizeStr)
if len(matches) > 0 {
num, _ := strconv.ParseInt(matches[1], 10, 64)
unit := ""
if len(matches) > 2 {
unit = matches[2]
}
switch unit {
case "KB", "kb":
maxSize = num * 1024
case "MB", "mb":
maxSize = num * 1024 * 1024
case "GB", "gb":
maxSize = num * 1024 * 1024 * 1024
default:
maxSize = num * 1024 * 1024 // 默认按MB处理
}
}
}
maxAge := int64(0)
if ageStr, ok := appenderConfig.Options["maxAge"].(string); ok {
// 解析带单位的时间配置
re := regexp.MustCompile(`^\d+[hd]?$`)
if re.MatchString(ageStr) {
// 提取数字部分和单位
numStr := ""
unit := ""
for _, c := range ageStr {
if c >= '0' && c <= '9' {
numStr += string(c)
} else {
unit = string(c)
}
}
num, _ := strconv.ParseInt(numStr, 10, 64)
switch unit {
case "h":
maxAge = num * 3600 // 小时转秒
default:
maxAge = num * 86400 // 默认按天转秒
}
}
} else if ageInt, ok := appenderConfig.Options["maxAge"].(int64); ok {
maxAge = ageInt
}
var ret LoggerAppender = &FileAppender{
formatter: SelectFormatter(appenderConfig.Formatter),
filePath: logfile.(string),
EnableRolling: rollingEnabled,
MaxSize: maxSize,
MaxAge: maxAge,
}
ret.(*FileAppender).start()
return &ret
}
func init() {
RegistAppender("file", makeFileAppender)
}