|
@@ -8,6 +8,7 @@ import (
|
|
|
"path/filepath"
|
|
"path/filepath"
|
|
|
"runtime"
|
|
"runtime"
|
|
|
"sync"
|
|
"sync"
|
|
|
|
|
+ "sync/atomic"
|
|
|
"time"
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/op/go-logging"
|
|
"github.com/op/go-logging"
|
|
@@ -30,10 +31,13 @@ const (
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
var (
|
|
|
- // Initialized to a usable default so logging never nil-derefs before InitLogger
|
|
|
|
|
- // runs — the "migrate" and "setting" CLI subcommands log without calling it.
|
|
|
|
|
- logger = logging.MustGetLogger("x-ui")
|
|
|
|
|
- fileRotate *lumberjack.Logger // nil when file backend disabled
|
|
|
|
|
|
|
+ // InitLogger swaps the handle while other goroutines are logging, so it is
|
|
|
|
|
+ // published atomically — a plain assignment is an unsafe publication.
|
|
|
|
|
+ logger atomic.Pointer[logging.Logger]
|
|
|
|
|
+
|
|
|
|
|
+ // fileRotateMu guards fileRotate against a concurrent InitLogger/CloseLogger.
|
|
|
|
|
+ fileRotateMu sync.Mutex
|
|
|
|
|
+ fileRotate *lumberjack.Logger // nil when file backend disabled
|
|
|
|
|
|
|
|
// logBuffer maintains recent log entries in memory for web UI retrieval;
|
|
// logBuffer maintains recent log entries in memory for web UI retrieval;
|
|
|
// logBufferMu guards it — written from many goroutines, read by the web UI.
|
|
// logBufferMu guards it — written from many goroutines, read by the web UI.
|
|
@@ -45,6 +49,12 @@ var (
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+// A usable default so logging never nil-derefs before InitLogger runs — the
|
|
|
|
|
+// "migrate" and "setting" CLI subcommands log without calling it.
|
|
|
|
|
+func init() {
|
|
|
|
|
+ logger.Store(logging.MustGetLogger("x-ui"))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// InitLogger initializes dual logging backends: console/syslog and file.
|
|
// InitLogger initializes dual logging backends: console/syslog and file.
|
|
|
// Console logging uses the specified level, file logging always uses DEBUG level.
|
|
// Console logging uses the specified level, file logging always uses DEBUG level.
|
|
|
func InitLogger(level logging.Level) {
|
|
func InitLogger(level logging.Level) {
|
|
@@ -66,7 +76,7 @@ func InitLogger(level logging.Level) {
|
|
|
|
|
|
|
|
multiBackend := logging.MultiLogger(backends...)
|
|
multiBackend := logging.MultiLogger(backends...)
|
|
|
newLogger.SetBackend(multiBackend)
|
|
newLogger.SetBackend(multiBackend)
|
|
|
- logger = newLogger
|
|
|
|
|
|
|
+ logger.Store(newLogger)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// initDefaultBackend creates the console/syslog logging backend.
|
|
// initDefaultBackend creates the console/syslog logging backend.
|
|
@@ -104,7 +114,7 @@ func initFileBackend() logging.Backend {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
logPath := filepath.Join(logDir, logFileName)
|
|
logPath := filepath.Join(logDir, logFileName)
|
|
|
- fileRotate = &lumberjack.Logger{
|
|
|
|
|
|
|
+ rotate := &lumberjack.Logger{
|
|
|
Filename: logPath,
|
|
Filename: logPath,
|
|
|
MaxSize: maxLogFileMB,
|
|
MaxSize: maxLogFileMB,
|
|
|
MaxBackups: maxLogBackups,
|
|
MaxBackups: maxLogBackups,
|
|
@@ -112,8 +122,11 @@ func initFileBackend() logging.Backend {
|
|
|
LocalTime: true,
|
|
LocalTime: true,
|
|
|
Compress: compressRotated,
|
|
Compress: compressRotated,
|
|
|
}
|
|
}
|
|
|
|
|
+ fileRotateMu.Lock()
|
|
|
|
|
+ fileRotate = rotate
|
|
|
|
|
+ fileRotateMu.Unlock()
|
|
|
|
|
|
|
|
- backend := logging.NewLogBackend(fileRotate, "", 0)
|
|
|
|
|
|
|
+ backend := logging.NewLogBackend(rotate, "", 0)
|
|
|
return logging.NewBackendFormatter(backend, newFormatter(true))
|
|
return logging.NewBackendFormatter(backend, newFormatter(true))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -129,6 +142,8 @@ func newFormatter(withTime bool) logging.Formatter {
|
|
|
// CloseLogger closes the rotating log writer and cleans up resources.
|
|
// CloseLogger closes the rotating log writer and cleans up resources.
|
|
|
// Should be called during application shutdown.
|
|
// Should be called during application shutdown.
|
|
|
func CloseLogger() {
|
|
func CloseLogger() {
|
|
|
|
|
+ fileRotateMu.Lock()
|
|
|
|
|
+ defer fileRotateMu.Unlock()
|
|
|
if fileRotate != nil {
|
|
if fileRotate != nil {
|
|
|
_ = fileRotate.Close()
|
|
_ = fileRotate.Close()
|
|
|
fileRotate = nil
|
|
fileRotate = nil
|
|
@@ -137,61 +152,61 @@ func CloseLogger() {
|
|
|
|
|
|
|
|
// Debug logs a debug message and adds it to the log buffer.
|
|
// Debug logs a debug message and adds it to the log buffer.
|
|
|
func Debug(args ...any) {
|
|
func Debug(args ...any) {
|
|
|
- logger.Debug(args...)
|
|
|
|
|
|
|
+ logger.Load().Debug(args...)
|
|
|
addToBuffer("DEBUG", fmt.Sprint(args...))
|
|
addToBuffer("DEBUG", fmt.Sprint(args...))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Debugf logs a formatted debug message and adds it to the log buffer.
|
|
// Debugf logs a formatted debug message and adds it to the log buffer.
|
|
|
func Debugf(format string, args ...any) {
|
|
func Debugf(format string, args ...any) {
|
|
|
- logger.Debugf(format, args...)
|
|
|
|
|
|
|
+ logger.Load().Debugf(format, args...)
|
|
|
addToBuffer("DEBUG", fmt.Sprintf(format, args...))
|
|
addToBuffer("DEBUG", fmt.Sprintf(format, args...))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Info logs an info message and adds it to the log buffer.
|
|
// Info logs an info message and adds it to the log buffer.
|
|
|
func Info(args ...any) {
|
|
func Info(args ...any) {
|
|
|
- logger.Info(args...)
|
|
|
|
|
|
|
+ logger.Load().Info(args...)
|
|
|
addToBuffer("INFO", fmt.Sprint(args...))
|
|
addToBuffer("INFO", fmt.Sprint(args...))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Infof logs a formatted info message and adds it to the log buffer.
|
|
// Infof logs a formatted info message and adds it to the log buffer.
|
|
|
func Infof(format string, args ...any) {
|
|
func Infof(format string, args ...any) {
|
|
|
- logger.Infof(format, args...)
|
|
|
|
|
|
|
+ logger.Load().Infof(format, args...)
|
|
|
addToBuffer("INFO", fmt.Sprintf(format, args...))
|
|
addToBuffer("INFO", fmt.Sprintf(format, args...))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Notice logs a notice message and adds it to the log buffer.
|
|
// Notice logs a notice message and adds it to the log buffer.
|
|
|
func Notice(args ...any) {
|
|
func Notice(args ...any) {
|
|
|
- logger.Notice(args...)
|
|
|
|
|
|
|
+ logger.Load().Notice(args...)
|
|
|
addToBuffer("NOTICE", fmt.Sprint(args...))
|
|
addToBuffer("NOTICE", fmt.Sprint(args...))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Noticef logs a formatted notice message and adds it to the log buffer.
|
|
// Noticef logs a formatted notice message and adds it to the log buffer.
|
|
|
func Noticef(format string, args ...any) {
|
|
func Noticef(format string, args ...any) {
|
|
|
- logger.Noticef(format, args...)
|
|
|
|
|
|
|
+ logger.Load().Noticef(format, args...)
|
|
|
addToBuffer("NOTICE", fmt.Sprintf(format, args...))
|
|
addToBuffer("NOTICE", fmt.Sprintf(format, args...))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Warning logs a warning message and adds it to the log buffer.
|
|
// Warning logs a warning message and adds it to the log buffer.
|
|
|
func Warning(args ...any) {
|
|
func Warning(args ...any) {
|
|
|
- logger.Warning(args...)
|
|
|
|
|
|
|
+ logger.Load().Warning(args...)
|
|
|
addToBuffer("WARNING", fmt.Sprint(args...))
|
|
addToBuffer("WARNING", fmt.Sprint(args...))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Warningf logs a formatted warning message and adds it to the log buffer.
|
|
// Warningf logs a formatted warning message and adds it to the log buffer.
|
|
|
func Warningf(format string, args ...any) {
|
|
func Warningf(format string, args ...any) {
|
|
|
- logger.Warningf(format, args...)
|
|
|
|
|
|
|
+ logger.Load().Warningf(format, args...)
|
|
|
addToBuffer("WARNING", fmt.Sprintf(format, args...))
|
|
addToBuffer("WARNING", fmt.Sprintf(format, args...))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Error logs an error message and adds it to the log buffer.
|
|
// Error logs an error message and adds it to the log buffer.
|
|
|
func Error(args ...any) {
|
|
func Error(args ...any) {
|
|
|
- logger.Error(args...)
|
|
|
|
|
|
|
+ logger.Load().Error(args...)
|
|
|
addToBuffer("ERROR", fmt.Sprint(args...))
|
|
addToBuffer("ERROR", fmt.Sprint(args...))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Errorf logs a formatted error message and adds it to the log buffer.
|
|
// Errorf logs a formatted error message and adds it to the log buffer.
|
|
|
func Errorf(format string, args ...any) {
|
|
func Errorf(format string, args ...any) {
|
|
|
- logger.Errorf(format, args...)
|
|
|
|
|
|
|
+ logger.Load().Errorf(format, args...)
|
|
|
addToBuffer("ERROR", fmt.Sprintf(format, args...))
|
|
addToBuffer("ERROR", fmt.Sprintf(format, args...))
|
|
|
}
|
|
}
|
|
|
|
|
|