Files
AoXuan 6b646378b6 refactor(updater): 重构 Go 版本更新器
- 更新项目名称为 AUTO_MAA_Go_Updater
- 重构代码结构,优化函数命名和逻辑
- 移除 CDK 相关的冗余代码
- 调整版本号为 git commit hash
- 更新构建配置和脚本
- 优化 API 客户端实现
2025-07-22 21:51:58 +08:00

439 lines
10 KiB
Go
Raw Permalink 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 logger
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
"sync"
"time"
)
// LogLevel 日志级别
type LogLevel int
const (
DEBUG LogLevel = iota
INFO
WARN
ERROR
)
// String 返回日志级别的字符串表示
func (l LogLevel) String() string {
switch l {
case DEBUG:
return "DEBUG"
case INFO:
return "INFO"
case WARN:
return "WARN"
case ERROR:
return "ERROR"
default:
return "UNKNOWN"
}
}
// Logger 日志记录器接口
type Logger interface {
Debug(msg string, fields ...interface{})
Info(msg string, fields ...interface{})
Warn(msg string, fields ...interface{})
Error(msg string, fields ...interface{})
SetLevel(level LogLevel)
Close() error
}
// FileLogger 文件日志记录器
type FileLogger struct {
mu sync.RWMutex
file *os.File
logger *log.Logger
level LogLevel
maxSize int64 // 最大文件大小(字节)
maxBackups int // 最大备份文件数
logDir string // 日志目录
filename string // 日志文件名
currentSize int64 // 当前文件大小
}
// LoggerConfig 日志配置
type LoggerConfig struct {
Level LogLevel
MaxSize int64 // 最大文件大小字节默认10MB
MaxBackups int // 最大备份文件数默认5
LogDir string // 日志目录
Filename string // 日志文件名
}
// DefaultLoggerConfig 默认日志配置
func DefaultLoggerConfig() *LoggerConfig {
// 获取当前可执行文件目录
exePath, err := os.Executable()
var logDir string
if err != nil {
logDir = "debug"
} else {
exeDir := filepath.Dir(exePath)
logDir = filepath.Join(exeDir, "debug")
}
return &LoggerConfig{
Level: INFO,
MaxSize: 10 * 1024 * 1024, // 10MB
MaxBackups: 5,
LogDir: logDir,
Filename: "AUTO_MAA_Go_Updater.log",
}
}
// NewFileLogger 创建新的文件日志记录器
func NewFileLogger(config *LoggerConfig) (*FileLogger, error) {
if config == nil {
config = DefaultLoggerConfig()
}
// 创建日志目录
if err := os.MkdirAll(config.LogDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create log directory: %w", err)
}
logPath := filepath.Join(config.LogDir, config.Filename)
// 打开或创建日志文件
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %w", err)
}
// 获取当前文件大小
stat, err := file.Stat()
if err != nil {
file.Close()
return nil, fmt.Errorf("failed to get file stats: %w", err)
}
logger := &FileLogger{
file: file,
logger: log.New(file, "", 0), // 我们自己处理格式
level: config.Level,
maxSize: config.MaxSize,
maxBackups: config.MaxBackups,
logDir: config.LogDir,
filename: config.Filename,
currentSize: stat.Size(),
}
return logger, nil
}
// formatMessage 格式化日志消息
func (fl *FileLogger) formatMessage(level LogLevel, msg string, fields ...interface{}) string {
timestamp := time.Now().Format("2006-01-02 15:04:05.000")
if len(fields) > 0 {
msg = fmt.Sprintf(msg, fields...)
}
return fmt.Sprintf("[%s] %s %s\n", timestamp, level.String(), msg)
}
// writeLog 写入日志
func (fl *FileLogger) writeLog(level LogLevel, msg string, fields ...interface{}) {
fl.mu.Lock()
defer fl.mu.Unlock()
// 检查日志级别
if level < fl.level {
return
}
formattedMsg := fl.formatMessage(level, msg, fields...)
// 检查是否需要轮转
if fl.currentSize+int64(len(formattedMsg)) > fl.maxSize {
if err := fl.rotate(); err != nil {
// 轮转失败尝试写入stderr
fmt.Fprintf(os.Stderr, "Failed to rotate log: %v\n", err)
}
}
// 写入日志
n, err := fl.file.WriteString(formattedMsg)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to write log: %v\n", err)
return
}
fl.currentSize += int64(n)
fl.file.Sync() // 确保写入磁盘
}
// rotate 轮转日志文件
func (fl *FileLogger) rotate() error {
// 关闭当前文件
if err := fl.file.Close(); err != nil {
return fmt.Errorf("failed to close current log file: %w", err)
}
// 轮转备份文件
if err := fl.rotateBackups(); err != nil {
return fmt.Errorf("failed to rotate backups: %w", err)
}
// 创建新的日志文件
logPath := filepath.Join(fl.logDir, fl.filename)
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return fmt.Errorf("failed to create new log file: %w", err)
}
fl.file = file
fl.logger.SetOutput(file)
fl.currentSize = 0
return nil
}
// rotateBackups 轮转备份文件
func (fl *FileLogger) rotateBackups() error {
basePath := filepath.Join(fl.logDir, fl.filename)
// 删除最老的备份文件
if fl.maxBackups > 0 {
oldestBackup := fmt.Sprintf("%s.%d", basePath, fl.maxBackups)
os.Remove(oldestBackup) // 忽略错误,文件可能不存在
}
// 重命名现有备份文件
for i := fl.maxBackups - 1; i > 0; i-- {
oldName := fmt.Sprintf("%s.%d", basePath, i)
newName := fmt.Sprintf("%s.%d", basePath, i+1)
os.Rename(oldName, newName) // 忽略错误,文件可能不存在
}
// 将当前日志文件重命名为第一个备份
if fl.maxBackups > 0 {
backupName := fmt.Sprintf("%s.1", basePath)
return os.Rename(basePath, backupName)
}
return nil
}
// Debug 记录调试级别日志
func (fl *FileLogger) Debug(msg string, fields ...interface{}) {
fl.writeLog(DEBUG, msg, fields...)
}
// Info 记录信息级别日志
func (fl *FileLogger) Info(msg string, fields ...interface{}) {
fl.writeLog(INFO, msg, fields...)
}
// Warn 记录警告级别日志
func (fl *FileLogger) Warn(msg string, fields ...interface{}) {
fl.writeLog(WARN, msg, fields...)
}
// Error 记录错误级别日志
func (fl *FileLogger) Error(msg string, fields ...interface{}) {
fl.writeLog(ERROR, msg, fields...)
}
// SetLevel 设置日志级别
func (fl *FileLogger) SetLevel(level LogLevel) {
fl.mu.Lock()
defer fl.mu.Unlock()
fl.level = level
}
// Close 关闭日志记录器
func (fl *FileLogger) Close() error {
fl.mu.Lock()
defer fl.mu.Unlock()
if fl.file != nil {
return fl.file.Close()
}
return nil
}
// MultiLogger 多输出日志记录器
type MultiLogger struct {
loggers []Logger
level LogLevel
}
// NewMultiLogger 创建多输出日志记录器
func NewMultiLogger(loggers ...Logger) *MultiLogger {
return &MultiLogger{
loggers: loggers,
level: INFO,
}
}
// Debug 记录调试级别日志
func (ml *MultiLogger) Debug(msg string, fields ...interface{}) {
for _, logger := range ml.loggers {
logger.Debug(msg, fields...)
}
}
// Info 记录信息级别日志
func (ml *MultiLogger) Info(msg string, fields ...interface{}) {
for _, logger := range ml.loggers {
logger.Info(msg, fields...)
}
}
// Warn 记录警告级别日志
func (ml *MultiLogger) Warn(msg string, fields ...interface{}) {
for _, logger := range ml.loggers {
logger.Warn(msg, fields...)
}
}
// Error 记录错误级别日志
func (ml *MultiLogger) Error(msg string, fields ...interface{}) {
for _, logger := range ml.loggers {
logger.Error(msg, fields...)
}
}
// SetLevel 设置日志级别
func (ml *MultiLogger) SetLevel(level LogLevel) {
ml.level = level
for _, logger := range ml.loggers {
logger.SetLevel(level)
}
}
// Close 关闭所有日志记录器
func (ml *MultiLogger) Close() error {
var lastErr error
for _, logger := range ml.loggers {
if err := logger.Close(); err != nil {
lastErr = err
}
}
return lastErr
}
// ConsoleLogger 控制台日志记录器
type ConsoleLogger struct {
writer io.Writer
level LogLevel
}
// NewConsoleLogger 创建控制台日志记录器
func NewConsoleLogger(writer io.Writer) *ConsoleLogger {
if writer == nil {
writer = os.Stdout
}
return &ConsoleLogger{
writer: writer,
level: INFO,
}
}
// formatMessage 格式化控制台日志消息
func (cl *ConsoleLogger) formatMessage(level LogLevel, msg string, fields ...interface{}) string {
timestamp := time.Now().Format("15:04:05")
if len(fields) > 0 {
msg = fmt.Sprintf(msg, fields...)
}
return fmt.Sprintf("[%s] %s %s\n", timestamp, level.String(), msg)
}
// writeLog 写入控制台日志
func (cl *ConsoleLogger) writeLog(level LogLevel, msg string, fields ...interface{}) {
if level < cl.level {
return
}
formattedMsg := cl.formatMessage(level, msg, fields...)
fmt.Fprint(cl.writer, formattedMsg)
}
// Debug 记录调试级别日志
func (cl *ConsoleLogger) Debug(msg string, fields ...interface{}) {
cl.writeLog(DEBUG, msg, fields...)
}
// Info 记录信息级别日志
func (cl *ConsoleLogger) Info(msg string, fields ...interface{}) {
cl.writeLog(INFO, msg, fields...)
}
// Warn 记录警告级别日志
func (cl *ConsoleLogger) Warn(msg string, fields ...interface{}) {
cl.writeLog(WARN, msg, fields...)
}
// Error 记录错误级别日志
func (cl *ConsoleLogger) Error(msg string, fields ...interface{}) {
cl.writeLog(ERROR, msg, fields...)
}
// SetLevel 设置日志级别
func (cl *ConsoleLogger) SetLevel(level LogLevel) {
cl.level = level
}
// Close 关闭控制台日志记录器(无操作)
func (cl *ConsoleLogger) Close() error {
return nil
}
// 全局日志记录器实例
var (
defaultLogger Logger
once sync.Once
)
// GetDefaultLogger 获取默认日志记录器
func GetDefaultLogger() Logger {
once.Do(func() {
fileLogger, err := NewFileLogger(DefaultLoggerConfig())
if err != nil {
// 如果文件日志创建失败,使用控制台日志
defaultLogger = NewConsoleLogger(os.Stderr)
} else {
// 同时输出到文件和控制台
consoleLogger := NewConsoleLogger(os.Stdout)
defaultLogger = NewMultiLogger(fileLogger, consoleLogger)
}
})
return defaultLogger
}
// 便捷函数
func Debug(msg string, fields ...interface{}) {
GetDefaultLogger().Debug(msg, fields...)
}
func Info(msg string, fields ...interface{}) {
GetDefaultLogger().Info(msg, fields...)
}
func Warn(msg string, fields ...interface{}) {
GetDefaultLogger().Warn(msg, fields...)
}
func Error(msg string, fields ...interface{}) {
GetDefaultLogger().Error(msg, fields...)
}
func SetLevel(level LogLevel) {
GetDefaultLogger().SetLevel(level)
}
func Close() error {
return GetDefaultLogger().Close()
}