207 lines
4.8 KiB
Go
207 lines
4.8 KiB
Go
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)
|
||
}
|