package logger import ( "fmt" "log" "os" "time" "gorm.io/gorm" "rhemawavetech.com/rhemawave/internal/db" ) type Level int const ( LevelDebug Level = iota LevelInfo LevelWarning LevelError LevelFatal ) func (l Level) String() string { return [...]string{"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"}[l] } var ( DB *gorm.DB file *os.File console = log.New(os.Stderr, "", 0) ) // AddFile attaches or replaces the file backend func AddFile(path string) error { if file != nil { file.Close() } _, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return err } return nil } // AddDatabase attaches or replaces the database backend func AddDatabase(d *gorm.DB) { console.Println("Adding database to logger") DB = d } // Close shuts down the file backend. func Close() error { if file != nil { err := file.Close() file = nil return err } return nil } func logMsg(level Level, format string, args ...any) { message := fmt.Sprintf(format, args...) now := time.Now() timestamp := now.Format("2006-01-02 15:04:05.000") line := fmt.Sprintf("%s [%s] %s", timestamp, level, message) // 1. The standard log package is internally thread-safe console.Println(line) // 2. Write to file if file != nil { fmt.Fprintln(file, line) } // 3. Write to DB using GORM. It handles its own connection pooling and locking. if DB != nil { var entry db.Log entry.Timestamp = now.UnixMilli() entry.Level = level.String() entry.Message = message // Use Save() to insert the new record. result := DB.Save(&entry) if result.Error != nil { console.Printf("Failed to write log to database: %v\n", result.Error) } } } func Debug(format string, args ...any) { logMsg(LevelDebug, format, args...) } func Info(format string, args ...any) { logMsg(LevelInfo, format, args...) } func Warning(format string, args ...any) { logMsg(LevelWarning, format, args...) } func Error(format string, args ...any) { logMsg(LevelError, format, args...) } func Fatal(format string, args ...any) { logMsg(LevelFatal, format, args...) time.Sleep(100 * time.Millisecond) os.Exit(1) }