Files
be.ems/src/framework/logger/writer.go
2024-05-27 10:29:04 +08:00

195 lines
4.9 KiB
Go

package logger
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"time"
)
// 日志器对象
type Logger struct {
env string // 运行环境
filePath string // 文件路径
fileName string // 文件名
level int // 日志等级标识
maxDay int // 保留最长天数
maxSize int64 // 文件最大空间
fileHandle *os.File // 文件实例
logger *log.Logger // 日志实例
logLevelMap map[int]string // 日志等级标识名
logDay int // 日志当前日
}
const (
LOG_LEVEL_SILENT = iota
LOG_LEVEL_INFO
LOG_LEVEL_WARN
LOG_LEVEL_ERROR
)
// NewLogger 实例日志器对象
func NewLogger(env, fileDir, fileName string, level, maxDay, maxSize int) (*Logger, error) {
logFilePath := filepath.Join(fileDir, fileName)
if err := os.MkdirAll(filepath.Dir(logFilePath), 0775); err != nil {
return nil, fmt.Errorf("failed to mkdir logger dir: %v", err)
}
fileHandle, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %v", err)
}
writer := io.Writer(fileHandle)
if env == "local" {
writer = io.MultiWriter(fileHandle, os.Stderr)
}
logger := log.New(writer, "", log.LstdFlags|log.Lshortfile)
logLevelMap := map[int]string{
LOG_LEVEL_INFO: "INFO",
LOG_LEVEL_WARN: "WARN",
LOG_LEVEL_ERROR: "ERROR",
}
stdLogger := &Logger{
env: env,
filePath: fileDir,
fileName: fileName,
level: level,
maxDay: maxDay,
maxSize: int64(maxSize * 1024 * 1024),
fileHandle: fileHandle,
logger: logger,
logLevelMap: logLevelMap,
logDay: time.Now().Day(),
}
go stdLogger.checkFile()
return stdLogger, nil
}
// checkFile 检查文件分割,自定时调用
func (l *Logger) checkFile() {
fileInfo, err := l.fileHandle.Stat()
if err != nil {
l.logger.Printf("Failed to get log file info: %v\n", err)
return
}
currTime := time.Now()
if l.logDay != currTime.Day() {
l.logDay = currTime.Day()
l.rotateFile(currTime.AddDate(0, 0, -1).Format("2006-01-02"))
// 移除超过保存最长天数的文件
l.removeOldFile(currTime.AddDate(0, 0, -l.maxDay))
} else if fileInfo.Size() >= l.maxSize {
l.rotateFile(currTime.Format("2006-01-02_150405"))
} else if time.Since(fileInfo.ModTime()).Hours() > 24 {
l.rotateFile(fileInfo.ModTime().Format("2006-01-02"))
}
time.AfterFunc(1*time.Minute, l.checkFile)
}
// rotateFile 检查文件大小进行分割
func (l *Logger) rotateFile(timeFormat string) {
l.fileHandle.Close()
newFileName := fmt.Sprintf("%s.%s", l.fileName, timeFormat)
newFilePath := filepath.Join(l.filePath, newFileName)
oldfilePath := filepath.Join(l.filePath, l.fileName)
// 重命名
os.Rename(oldfilePath, newFilePath)
// 新文件句柄
fileHandle, err := os.OpenFile(oldfilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
l.logger.Printf("Failed to open log file: %v\n", err)
return
}
l.fileHandle = fileHandle
// 重新设置 logger 的 writer
writer := io.Writer(l.fileHandle)
if l.env == "local" {
writer = io.MultiWriter(l.fileHandle, os.Stderr)
}
l.logger.SetOutput(writer)
}
// RemoveOldFile 删除旧文件
func (l *Logger) removeOldFile(oldFileDate time.Time) {
// 遍历目标文件夹中的文件
files, err := os.ReadDir(l.filePath)
if err != nil {
l.Errorf("logger RemoveOldFile ReadDir err: %v", err.Error())
return
}
for _, file := range files {
// 跳过非指定日志文件名
if !strings.HasPrefix(file.Name(), l.fileName+".") {
continue
}
idx := strings.LastIndex(file.Name(), ".")
if idx == -1 {
continue
}
dateStr := file.Name()[idx+1 : idx+11]
// 解析日期字符串
fileDate, err := time.Parse("2006-01-02", dateStr)
if err != nil {
l.Errorf("logger RemoveOldFile Parse err: %v", err.Error())
continue
}
// 判断文件日期是否在给定日期之前
if fileDate.Before(oldFileDate) {
// 删除旧文件
err := os.Remove(filepath.Join(l.filePath, file.Name()))
if err != nil {
l.Errorf("logger RemoveOldFile Remove err: %v", err.Error())
continue
}
}
}
}
// writeLog 写入chan
func (l *Logger) writeLog(level int, format string, args ...interface{}) {
if level < l.level {
return
}
logMsg := fmt.Sprintf("[%s] %s\n", l.logLevelMap[level], fmt.Sprintf(format, args...))
l.logger.Output(4, logMsg)
}
func (l *Logger) Infof(format string, args ...interface{}) {
l.writeLog(LOG_LEVEL_INFO, format, args...)
}
func (l *Logger) Warnf(format string, args ...interface{}) {
l.writeLog(LOG_LEVEL_WARN, format, args...)
}
func (l *Logger) Errorf(format string, args ...interface{}) {
l.writeLog(LOG_LEVEL_ERROR, format, args...)
}
// Close 日志关闭
func (l *Logger) Close() {
err := l.fileHandle.Close()
if err != nil {
l.logger.Printf("Failed to close log file: %v\n", err)
}
}