mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
This commit introduces a new configuration option `logs-max-total-size-mb` that allows users to set a maximum total size (in MB) for log files in the logs directory. When this limit is exceeded, the oldest log files will be automatically deleted to stay within the specified size. Setting this value to 0 (the default) disables this feature. This change enhances log management by preventing excessive disk space usage.
136 lines
3.3 KiB
Go
136 lines
3.3 KiB
Go
package logging
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
|
log "github.com/sirupsen/logrus"
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
)
|
|
|
|
var (
|
|
setupOnce sync.Once
|
|
writerMu sync.Mutex
|
|
logWriter *lumberjack.Logger
|
|
ginInfoWriter *io.PipeWriter
|
|
ginErrorWriter *io.PipeWriter
|
|
)
|
|
|
|
// LogFormatter defines a custom log format for logrus.
|
|
// This formatter adds timestamp, level, and source location to each log entry.
|
|
type LogFormatter struct{}
|
|
|
|
// Format renders a single log entry with custom formatting.
|
|
func (m *LogFormatter) Format(entry *log.Entry) ([]byte, error) {
|
|
var buffer *bytes.Buffer
|
|
if entry.Buffer != nil {
|
|
buffer = entry.Buffer
|
|
} else {
|
|
buffer = &bytes.Buffer{}
|
|
}
|
|
|
|
timestamp := entry.Time.Format("2006-01-02 15:04:05")
|
|
message := strings.TrimRight(entry.Message, "\r\n")
|
|
|
|
var formatted string
|
|
if entry.Caller != nil {
|
|
formatted = fmt.Sprintf("[%s] [%s] [%s:%d] %s\n", timestamp, entry.Level, filepath.Base(entry.Caller.File), entry.Caller.Line, message)
|
|
} else {
|
|
formatted = fmt.Sprintf("[%s] [%s] %s\n", timestamp, entry.Level, message)
|
|
}
|
|
buffer.WriteString(formatted)
|
|
|
|
return buffer.Bytes(), nil
|
|
}
|
|
|
|
// SetupBaseLogger configures the shared logrus instance and Gin writers.
|
|
// It is safe to call multiple times; initialization happens only once.
|
|
func SetupBaseLogger() {
|
|
setupOnce.Do(func() {
|
|
log.SetOutput(os.Stdout)
|
|
log.SetReportCaller(true)
|
|
log.SetFormatter(&LogFormatter{})
|
|
|
|
ginInfoWriter = log.StandardLogger().Writer()
|
|
gin.DefaultWriter = ginInfoWriter
|
|
ginErrorWriter = log.StandardLogger().WriterLevel(log.ErrorLevel)
|
|
gin.DefaultErrorWriter = ginErrorWriter
|
|
gin.DebugPrintFunc = func(format string, values ...interface{}) {
|
|
format = strings.TrimRight(format, "\r\n")
|
|
log.StandardLogger().Infof(format, values...)
|
|
}
|
|
|
|
log.RegisterExitHandler(closeLogOutputs)
|
|
})
|
|
}
|
|
|
|
// ConfigureLogOutput switches the global log destination between rotating files and stdout.
|
|
// When logsMaxTotalSizeMB > 0, a background cleaner removes the oldest log files in the logs directory
|
|
// until the total size is within the limit.
|
|
func ConfigureLogOutput(loggingToFile bool, logsMaxTotalSizeMB int) error {
|
|
SetupBaseLogger()
|
|
|
|
writerMu.Lock()
|
|
defer writerMu.Unlock()
|
|
|
|
logDir := "logs"
|
|
if base := util.WritablePath(); base != "" {
|
|
logDir = filepath.Join(base, "logs")
|
|
}
|
|
|
|
protectedPath := ""
|
|
if loggingToFile {
|
|
if err := os.MkdirAll(logDir, 0o755); err != nil {
|
|
return fmt.Errorf("logging: failed to create log directory: %w", err)
|
|
}
|
|
if logWriter != nil {
|
|
_ = logWriter.Close()
|
|
}
|
|
protectedPath = filepath.Join(logDir, "main.log")
|
|
logWriter = &lumberjack.Logger{
|
|
Filename: protectedPath,
|
|
MaxSize: 10,
|
|
MaxBackups: 0,
|
|
MaxAge: 0,
|
|
Compress: false,
|
|
}
|
|
log.SetOutput(logWriter)
|
|
} else {
|
|
if logWriter != nil {
|
|
_ = logWriter.Close()
|
|
logWriter = nil
|
|
}
|
|
log.SetOutput(os.Stdout)
|
|
}
|
|
|
|
configureLogDirCleanerLocked(logDir, logsMaxTotalSizeMB, protectedPath)
|
|
return nil
|
|
}
|
|
|
|
func closeLogOutputs() {
|
|
writerMu.Lock()
|
|
defer writerMu.Unlock()
|
|
|
|
stopLogDirCleanerLocked()
|
|
|
|
if logWriter != nil {
|
|
_ = logWriter.Close()
|
|
logWriter = nil
|
|
}
|
|
if ginInfoWriter != nil {
|
|
_ = ginInfoWriter.Close()
|
|
ginInfoWriter = nil
|
|
}
|
|
if ginErrorWriter != nil {
|
|
_ = ginErrorWriter.Close()
|
|
ginErrorWriter = nil
|
|
}
|
|
}
|