Initial qiwei secondary development handoff
This commit is contained in:
380
logger/logger.go
Normal file
380
logger/logger.go
Normal file
@@ -0,0 +1,380 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LogEntry 定义日志条目结构体,实际实现在operation_logger.go
|
||||
// 这里保留类型定义以保持向后兼容性
|
||||
type LogEntry struct {
|
||||
ID int64 `json:"id"`
|
||||
Time string `json:"time"`
|
||||
Source string `json:"source"`
|
||||
Type string `json:"type"`
|
||||
Content string `json:"content"`
|
||||
Duration int64 `json:"duration"`
|
||||
}
|
||||
|
||||
// LogLevel 定义日志级别
|
||||
type LogLevel int
|
||||
|
||||
const (
|
||||
LevelDebug LogLevel = iota
|
||||
LevelInfo
|
||||
LevelWarning
|
||||
LevelError
|
||||
)
|
||||
|
||||
// Logger 日志器结构体
|
||||
type Logger struct {
|
||||
Logger *log.Logger
|
||||
LogLevel LogLevel
|
||||
LogEnabled bool
|
||||
mu sync.Mutex
|
||||
|
||||
// 日志文件相关字段
|
||||
logFile *os.File
|
||||
logDir string
|
||||
exeName string
|
||||
maxFileSize int64
|
||||
currentSize int64
|
||||
Lock *sync.Mutex
|
||||
}
|
||||
|
||||
// GetLogDir 获取日志目录路径
|
||||
func (l *Logger) GetLogDir() string {
|
||||
return l.logDir
|
||||
}
|
||||
|
||||
// 确保日志文件大小不超过限制
|
||||
func ensureLogFileSize(logDir string, baseLogName string) string {
|
||||
// 尝试最多10次找到一个可用的文件名
|
||||
for i := 0; i < 10; i++ {
|
||||
var logFileName string
|
||||
if i == 0 {
|
||||
logFileName = baseLogName
|
||||
} else {
|
||||
logFileName = fmt.Sprintf("%s_%d.txt", baseLogName[:len(baseLogName)-4], i)
|
||||
}
|
||||
|
||||
logFilePath := filepath.Join(logDir, logFileName)
|
||||
|
||||
// 检查文件是否存在以及大小
|
||||
fileInfo, err := os.Stat(logFilePath)
|
||||
if err != nil || fileInfo.Size() < 5*1024*1024 { // 5MB
|
||||
return logFilePath
|
||||
}
|
||||
}
|
||||
// 如果所有尝试都失败,返回一个默认文件名
|
||||
return filepath.Join(logDir, fmt.Sprintf("%s_default.txt", baseLogName[:len(baseLogName)-4]))
|
||||
}
|
||||
|
||||
// NewLogger 创建一个新的日志器
|
||||
func NewLogger(exeName string, enabled bool, level LogLevel) (*Logger, error) {
|
||||
// 获取程序路径
|
||||
exePath, err := os.Executable()
|
||||
var logDir string
|
||||
if err != nil {
|
||||
// 如果获取程序路径失败,使用临时目录作为备选
|
||||
logDir = filepath.Join(os.TempDir(), fmt.Sprintf("%s_logs", exeName))
|
||||
} else {
|
||||
// 使用程序所在目录的Log子目录
|
||||
exeDir := filepath.Dir(exePath)
|
||||
logDir = filepath.Join(exeDir, "Log")
|
||||
}
|
||||
|
||||
// 创建基础日志目录
|
||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("无法创建日志目录: %v", err)
|
||||
}
|
||||
|
||||
// 获取当前日期,用于创建日期子目录(格式:2006-01-02)
|
||||
today := time.Now().Format("2006-01-02")
|
||||
dateDir := filepath.Join(logDir, today)
|
||||
|
||||
// 创建日期子目录
|
||||
if err := os.MkdirAll(dateDir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("无法创建日期子目录: %v", err)
|
||||
}
|
||||
|
||||
// 生成当前日期时间的日志文件名(格式:appName_YYYYMMDD_HHmmss.txt)
|
||||
timeStr := time.Now().Format("20060102_150405")
|
||||
baseLogName := fmt.Sprintf("%s_%s.txt", exeName, timeStr)
|
||||
logFilePath := filepath.Join(dateDir, baseLogName)
|
||||
|
||||
// 确保新文件不会超过大小限制
|
||||
logFilePath = ensureLogFileSize(dateDir, baseLogName)
|
||||
|
||||
// 打开日志文件
|
||||
logFile, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无法打开日志文件: %v", err)
|
||||
}
|
||||
|
||||
// 获取文件信息以初始化当前大小
|
||||
fileInfo, err := logFile.Stat()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无法获取文件信息: %v", err)
|
||||
}
|
||||
|
||||
// 创建logger
|
||||
logger := log.New(logFile, "", log.LstdFlags)
|
||||
|
||||
// 返回日志器
|
||||
return &Logger{
|
||||
Logger: logger,
|
||||
LogLevel: level,
|
||||
LogEnabled: enabled,
|
||||
logFile: logFile,
|
||||
logDir: logDir,
|
||||
exeName: exeName,
|
||||
maxFileSize: 5 * 1024 * 1024, // 5MB
|
||||
currentSize: fileInfo.Size(),
|
||||
Lock: &sync.Mutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close 关闭日志文件
|
||||
func (l *Logger) Close() {
|
||||
l.Lock.Lock()
|
||||
defer l.Lock.Unlock()
|
||||
|
||||
if l.logFile != nil {
|
||||
l.logFile.Close()
|
||||
l.logFile = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Debug 记录调试日志
|
||||
func (l *Logger) Debug(format string, v ...interface{}) {
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
if l.LogEnabled && l.LogLevel <= LevelDebug {
|
||||
l.log("[调试]", format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Info 记录信息日志
|
||||
func (l *Logger) Info(format string, v ...interface{}) {
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
if l.LogEnabled && l.LogLevel <= LevelInfo {
|
||||
l.log("[信息]", format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warn 记录警告日志
|
||||
func (l *Logger) Warn(format string, v ...interface{}) {
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
if l.LogEnabled && l.LogLevel <= LevelWarning {
|
||||
l.log("[警告]", format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Error 记录错误日志
|
||||
func (l *Logger) Error(format string, v ...interface{}) {
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
if l.LogEnabled && l.LogLevel <= LevelError {
|
||||
l.log("[错误]", format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// log 实际的日志记录函数
|
||||
func (l *Logger) log(level string, format string, v ...interface{}) {
|
||||
if l == nil || l.Lock == nil || l.Logger == nil {
|
||||
return
|
||||
}
|
||||
l.Lock.Lock()
|
||||
defer l.Lock.Unlock()
|
||||
|
||||
// 格式化日志消息
|
||||
message := fmt.Sprintf(format, v...)
|
||||
fullMessage := fmt.Sprintf("%s %s", level, message)
|
||||
|
||||
// 写入日志
|
||||
l.Logger.Println(fullMessage)
|
||||
|
||||
// 更新当前文件大小估计
|
||||
// 注意:这只是估计值,实际大小可能不同
|
||||
l.currentSize += int64(len(fullMessage) + 20) // 20是为时间戳等额外内容预留的空间
|
||||
|
||||
// 检查是否需要创建新文件
|
||||
if l.LogEnabled && l.currentSize >= l.maxFileSize {
|
||||
// 关闭当前文件
|
||||
if l.logFile != nil {
|
||||
l.logFile.Close()
|
||||
}
|
||||
|
||||
// 创建新的日志文件
|
||||
today := time.Now().Format("2006-01-02")
|
||||
dateDir := filepath.Join(l.logDir, today)
|
||||
|
||||
// 创建日期子目录
|
||||
if err := os.MkdirAll(dateDir, 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "无法创建日期子目录: %v\n", err)
|
||||
dateDir = l.logDir // 如果创建失败,回退到基础日志目录
|
||||
}
|
||||
|
||||
timeStr := time.Now().Format("20060102_150405")
|
||||
baseLogName := fmt.Sprintf("%s_%s.txt", l.exeName, timeStr)
|
||||
logFilePath := filepath.Join(dateDir, baseLogName)
|
||||
|
||||
// 确保新文件不会超过大小限制
|
||||
logFilePath = ensureLogFileSize(dateDir, baseLogName)
|
||||
|
||||
// 打开新的日志文件
|
||||
var err error
|
||||
l.logFile, err = os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err == nil {
|
||||
// 重新设置logger的输出
|
||||
l.Logger.SetOutput(l.logFile)
|
||||
// 重置当前大小
|
||||
l.currentSize = 0
|
||||
} else {
|
||||
// 如果无法打开新文件,输出错误到标准错误
|
||||
fmt.Fprintf(os.Stderr, "无法创建新的日志文件: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetEnabled 设置日志总开关
|
||||
func (l *Logger) SetEnabled(enabled bool) {
|
||||
l.Lock.Lock()
|
||||
defer l.Lock.Unlock()
|
||||
|
||||
if l.LogEnabled == enabled {
|
||||
return
|
||||
}
|
||||
|
||||
l.LogEnabled = enabled
|
||||
|
||||
if enabled {
|
||||
// 如果启用日志,但文件已关闭,重新打开
|
||||
if l.logFile == nil {
|
||||
today := time.Now().Format("2006-01-02")
|
||||
dateDir := filepath.Join(l.logDir, today)
|
||||
|
||||
// 创建日期子目录
|
||||
if err := os.MkdirAll(dateDir, 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "无法创建日期子目录: %v\n", err)
|
||||
dateDir = l.logDir // 如果创建失败,回退到基础日志目录
|
||||
}
|
||||
|
||||
timeStr := time.Now().Format("20060102_150405")
|
||||
baseLogName := fmt.Sprintf("%s_%s.txt", l.exeName, timeStr)
|
||||
logFilePath := filepath.Join(dateDir, baseLogName)
|
||||
|
||||
// 确保新文件不会超过大小限制
|
||||
logFilePath = ensureLogFileSize(dateDir, baseLogName)
|
||||
|
||||
// 打开日志文件
|
||||
var err error
|
||||
l.logFile, err = os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err == nil {
|
||||
// 设置logger的输出
|
||||
l.Logger.SetOutput(l.logFile)
|
||||
|
||||
// 获取文件当前大小
|
||||
fileInfo, err := l.logFile.Stat()
|
||||
if err == nil {
|
||||
l.currentSize = fileInfo.Size()
|
||||
}
|
||||
} else {
|
||||
// 如果无法打开文件,输出错误到标准错误
|
||||
fmt.Fprintf(os.Stderr, "无法打开日志文件: %v\n", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果禁用日志,将输出重定向到/dev/null
|
||||
l.Logger.SetOutput(io.Discard)
|
||||
}
|
||||
}
|
||||
|
||||
// SetLogLevel 设置日志级别
|
||||
func (l *Logger) SetLogLevel(level LogLevel) {
|
||||
l.Lock.Lock()
|
||||
defer l.Lock.Unlock()
|
||||
l.LogLevel = level
|
||||
}
|
||||
|
||||
// CleanOldLogs 清理指定天数之前的旧日志文件
|
||||
func CleanOldLogs(logDir string, daysToKeep int) error {
|
||||
if daysToKeep <= 0 {
|
||||
daysToKeep = 30 // 默认保留30天
|
||||
}
|
||||
|
||||
cutoffTime := time.Now().AddDate(0, 0, -daysToKeep)
|
||||
|
||||
// 遍历日志目录下的所有子目录和文件
|
||||
err := filepath.Walk(logDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil // 跳过错误,继续处理
|
||||
}
|
||||
|
||||
// 跳过目录
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 只处理.txt和.log文件
|
||||
ext := strings.ToLower(filepath.Ext(path))
|
||||
if ext != ".txt" && ext != ".log" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 检查文件修改时间
|
||||
if info.ModTime().Before(cutoffTime) {
|
||||
// 删除旧日志文件
|
||||
if removeErr := os.Remove(path); removeErr != nil {
|
||||
// 记录错误但不中断清理过程
|
||||
fmt.Printf("删除旧日志文件失败: %s, 错误: %v\n", path, removeErr)
|
||||
} else {
|
||||
fmt.Printf("已删除旧日志文件: %s\n", path)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// StartLogCleanupScheduler 启动日志清理定时器
|
||||
func StartLogCleanupScheduler(logDir string, daysToKeep int, checkInterval time.Duration) {
|
||||
if daysToKeep <= 0 {
|
||||
daysToKeep = 30
|
||||
}
|
||||
if checkInterval <= 0 {
|
||||
checkInterval = 24 * time.Hour // 默认每天检查一次
|
||||
}
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(checkInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
// 立即执行一次清理
|
||||
if err := CleanOldLogs(logDir, daysToKeep); err != nil {
|
||||
fmt.Printf("日志清理失败: %v\n", err)
|
||||
}
|
||||
|
||||
// 定时清理
|
||||
for range ticker.C {
|
||||
if err := CleanOldLogs(logDir, daysToKeep); err != nil {
|
||||
fmt.Printf("日志清理失败: %v\n", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
Reference in New Issue
Block a user