Fix data race between logger.Init and GetLogger.
CI / check-and-test (pull_request) Successful in 20s
CI / check-and-test (pull_request) Successful in 20s
Copy the global logger pointer under the mutex and publish a fully built instance so concurrent tests and handleConnection logging are race-free.
This commit is contained in:
+27
-18
@@ -27,28 +27,24 @@ type Config struct {
|
||||
Compress bool `yaml:"compress"` // compress backup files
|
||||
}
|
||||
|
||||
// Init initializes the global logger with the given configuration
|
||||
func Init(config Config) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
Log = logrus.New()
|
||||
func newLogger(config Config) (*logrus.Logger, error) {
|
||||
l := logrus.New()
|
||||
|
||||
// Set log level
|
||||
level, err := logrus.ParseLevel(config.Level)
|
||||
if err != nil {
|
||||
level = logrus.InfoLevel
|
||||
}
|
||||
Log.SetLevel(level)
|
||||
l.SetLevel(level)
|
||||
|
||||
// Set log format with sanitization
|
||||
switch config.Format {
|
||||
case "json":
|
||||
Log.SetFormatter(&SanitizedJSONFormatter{
|
||||
l.SetFormatter(&SanitizedJSONFormatter{
|
||||
TimestampFormat: "2006-01-02 15:04:05",
|
||||
})
|
||||
default:
|
||||
Log.SetFormatter(&SanitizedTextFormatter{
|
||||
l.SetFormatter(&SanitizedTextFormatter{
|
||||
FullTimestamp: true,
|
||||
TimestampFormat: "2006-01-02 15:04:05",
|
||||
})
|
||||
@@ -59,36 +55,49 @@ func Init(config Config) error {
|
||||
// Ensure directory exists
|
||||
dir := filepath.Dir(config.File)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Open log file with secure permissions (owner read/write only)
|
||||
file, err := os.OpenFile(config.File, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set output to both file and stdout
|
||||
Log.SetOutput(io.MultiWriter(file, os.Stdout))
|
||||
l.SetOutput(io.MultiWriter(file, os.Stdout))
|
||||
} else {
|
||||
Log.SetOutput(os.Stdout)
|
||||
l.SetOutput(os.Stdout)
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Init initializes the global logger with the given configuration
|
||||
func Init(config Config) error {
|
||||
l, err := newLogger(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
Log = l
|
||||
mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLogger returns the global logger instance
|
||||
func GetLogger() *logrus.Logger {
|
||||
mu.RLock()
|
||||
if Log != nil {
|
||||
mu.RUnlock()
|
||||
return Log
|
||||
}
|
||||
l := Log
|
||||
mu.RUnlock()
|
||||
if l != nil {
|
||||
return l
|
||||
}
|
||||
|
||||
// Initialize with default config if not already initialized
|
||||
once.Do(func() {
|
||||
Init(Config{
|
||||
_ = Init(Config{
|
||||
Level: "info",
|
||||
Format: "text",
|
||||
File: "",
|
||||
|
||||
Reference in New Issue
Block a user