licensing-cotton/internal/logger/logger.go
2025-11-01 15:19:24 +08:00

208 lines
3.7 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 logger
import (
"fmt"
"log"
"os"
"strings"
"sync"
"time"
)
type LogLevel int
const (
DEBUG LogLevel = iota
INFO
WARN
ERROR
)
var (
logLevel LogLevel = INFO
logFile *os.File
logger *log.Logger
mu sync.Mutex
maxSize int64 = 10 * 1024 * 1024 // 10MB
maxFiles int = 5
basePath string = "logs/licensing-cotton.log"
)
func init() {
// 创建日志目录
if err := os.MkdirAll("logs", 0755); err != nil {
log.Printf("无法创建日志目录: %v\n", err)
return
}
// 打开日志文件
if err := openLogFile(); err != nil {
log.Printf("无法打开日志文件: %v\n", err)
return
}
// 创建标准 logger
logger = log.New(logFile, "", 0)
}
// SetLevel 设置日志级别
func SetLevel(level string) {
switch strings.ToUpper(level) {
case "DEBUG":
logLevel = DEBUG
case "INFO":
logLevel = INFO
case "WARN":
logLevel = WARN
case "ERROR":
logLevel = ERROR
default:
logLevel = INFO
}
}
// SetLogPath 设置日志文件路径
func SetLogPath(path string) {
mu.Lock()
defer mu.Unlock()
basePath = path
}
// SetMaxSize 设置单个日志文件最大大小(字节)
func SetMaxSize(size int64) {
mu.Lock()
defer mu.Unlock()
maxSize = size
}
// SetMaxFiles 设置保留的日志文件数量
func SetMaxFiles(count int) {
mu.Lock()
defer mu.Unlock()
maxFiles = count
}
func openLogFile() error {
file, err := os.OpenFile(basePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return err
}
logFile = file
return nil
}
func rotateLogFile() error {
// 关闭当前文件
if logFile != nil {
logFile.Close()
}
// 轮转文件:最新的移动到 .1.1 移动到 .2,以此类推
for i := maxFiles - 1; i >= 1; i-- {
oldPath := fmt.Sprintf("%s.%d", basePath, i)
newPath := fmt.Sprintf("%s.%d", basePath, i+1)
// 删除最旧的文件
if i == maxFiles-1 {
os.Remove(newPath)
}
// 重命名文件
if _, err := os.Stat(oldPath); err == nil {
os.Rename(oldPath, newPath)
}
}
// 将当前日志文件重命名为 .1
rotatedPath := fmt.Sprintf("%s.1", basePath)
os.Rename(basePath, rotatedPath)
// 打开新文件
return openLogFile()
}
func checkAndRotate() error {
if logFile == nil {
return openLogFile()
}
// 检查文件大小
stat, err := logFile.Stat()
if err != nil {
return err
}
if stat.Size() >= maxSize {
return rotateLogFile()
}
return nil
}
func formatLog(level string, format string, args ...interface{}) string {
timestamp := time.Now().Format("2006-01-02 15:04:05")
message := fmt.Sprintf(format, args...)
return fmt.Sprintf("[%s] [%s] %s\n", timestamp, level, message)
}
func writeLog(level LogLevel, levelStr string, format string, args ...interface{}) {
if level < logLevel {
return
}
mu.Lock()
defer mu.Unlock()
// 检查并轮转日志文件
if err := checkAndRotate(); err != nil {
log.Printf("日志轮转失败: %v\n", err)
return
}
// 写入日志文件
if logger != nil {
logger.Printf(formatLog(levelStr, format, args...))
}
// 同时输出到标准输出
fmt.Printf(formatLog(levelStr, format, args...))
}
func Debug(format string, args ...interface{}) {
writeLog(DEBUG, "DEBUG", format, args...)
}
func Info(format string, args ...interface{}) {
writeLog(INFO, "INFO", format, args...)
}
func Warn(format string, args ...interface{}) {
writeLog(WARN, "WARN", format, args...)
}
func Error(format string, args ...interface{}) {
writeLog(ERROR, "ERROR", format, args...)
}
func Fatal(format string, args ...interface{}) {
mu.Lock()
defer mu.Unlock()
message := formatLog("FATAL", format, args...)
if logger != nil {
logger.Printf(message)
}
fmt.Printf(message)
os.Exit(1)
}
// Close 关闭日志文件
func Close() {
mu.Lock()
defer mu.Unlock()
if logFile != nil {
logFile.Close()
}
}