diff --git a/config.go b/config.go index e083711..aba7a42 100644 --- a/config.go +++ b/config.go @@ -17,6 +17,7 @@ type Config struct { ListeningPort int `yaml:"listening_port"` LogFile string `yaml:"log_file"` LogLevel string `yaml:"log_level"` + LogOutput string `yaml:"log_output"` StaticFilesDir string `yaml:"static_files_dir"` AllowedOrigins string `yaml:"allowed_origins"` } diff --git a/config/config.yaml b/config/config.yaml index c0cca05..0b51b63 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -5,5 +5,6 @@ image_storage_dir: "/app/images" listening_port: 8080 log_file: "/app/data/boxes.log" log_level: "INFO" +log_output: "both" # Can be "file", "stdout", or "both" static_files_dir: "/app/build/" allowed_origins: "*" diff --git a/logger.go b/logger.go index cb6933f..1e18865 100644 --- a/logger.go +++ b/logger.go @@ -3,6 +3,7 @@ package main import ( "fmt" + "io" "log" "os" "runtime" @@ -11,8 +12,8 @@ import ( type Logger struct { *log.Logger - file *os.File - level LogLevel + writers []io.Writer + level LogLevel } type LogLevel int @@ -35,23 +36,50 @@ var ( } ) -// Initialize creates a new logger instance with specified level -func Initialize(logPath string) (*Logger, error) { +// Initialize creates a new logger instance with specified configuration +func Initialize(logPath string, logOutput string) (*Logger, error) { if instance != nil { return instance, nil } - file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) - if err != nil { - return nil, fmt.Errorf("failed to open log file: %v", err) + var writers []io.Writer + + // Handle different output configurations + switch logOutput { + case "stdout": + writers = append(writers, os.Stdout) + case "file": + file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + return nil, fmt.Errorf("failed to open log file: %v", err) + } + writers = append(writers, file) + case "both": + file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + return nil, fmt.Errorf("failed to open log file: %v", err) + } + writers = append(writers, file, os.Stdout) + default: + // Default to stdout if invalid option provided + writers = append(writers, os.Stdout) } - // Default to INFO if no level specified - logger := &Logger{ - Logger: log.New(file, "", 0), - file: file, - level: INFO, + // Create multi-writer if we have multiple writers + var writer io.Writer + if len(writers) > 1 { + writer = io.MultiWriter(writers...) + } else { + writer = writers[0] } + + // Create logger instance + logger := &Logger{ + Logger: log.New(writer, "", 0), + writers: writers, + level: INFO, // Default to INFO level + } + instance = logger return logger, nil } @@ -81,10 +109,18 @@ func GetLogger() *Logger { return instance } -// Close closes the log file +// Close closes all writers that implement io.Closer func (l *Logger) Close() error { - if l.file != nil { - return l.file.Close() + var errs []error + for _, writer := range l.writers { + if closer, ok := writer.(io.Closer); ok { + if err := closer.Close(); err != nil { + errs = append(errs, err) + } + } + } + if len(errs) > 0 { + return fmt.Errorf("errors closing writers: %v", errs) } return nil } @@ -94,7 +130,6 @@ func (l *Logger) log(level LogLevel, format string, v ...interface{}) { return } - // Check if this message should be logged based on current level if level < l.level { return } diff --git a/main.go b/main.go index 0d4486a..b0406e7 100644 --- a/main.go +++ b/main.go @@ -22,13 +22,14 @@ var ( ) func main() { + log := GetLogger() // Load configuration var err error config, err = loadAndValidateConfig() if err != nil { log.Fatalf("Failed to load config: %v", err) } - fmt.Printf("Config loaded successfully in main(), DB path %s\n", config.DatabasePath) + log.Printf("Config loaded successfully in main(), DB path %s\n", config.DatabasePath) // Set up logging BEFORE logging config details if err := setupLogging(config.LogFile); err != nil { @@ -86,7 +87,7 @@ func loadAndValidateConfig() (*Config, error) { func setupLogging(logFile string) error { // Initialize returns a *Logger, but we don't need to store it - _, err := Initialize(logFile) + _, err := Initialize(logFile, config.LogOutput) if err != nil { return fmt.Errorf("failed to initialize logger: %v", err) } @@ -109,7 +110,7 @@ func setupLogging(logFile string) error { func logConfigDetails(config *Config) { log := GetLogger() if log == nil { - fmt.Println("Warning: Logger not initialized when attempting to log config details") + log.Warn("Warning: Logger not initialized when attempting to log config details") return } @@ -118,6 +119,7 @@ func logConfigDetails(config *Config) { log.Info("Image Storage Dir: %s", config.ImageStorageDir) log.Info("Log File: %s", config.LogFile) log.Info("Log Level: %s", config.LogLevel) + log.Info("Log Output: %s", config.LogOutput) log.Info("Listening Port: %d", config.ListeningPort) log.Info("Allowed Origins: %s", config.AllowedOrigins) @@ -126,14 +128,17 @@ func logConfigDetails(config *Config) { } func connectToDatabase(config *Config) (*gorm.DB, error) { + log := GetLogger() if config == nil { - return nil, fmt.Errorf("config is nil in connectToDatabase") + log.Error("config is nil in connectToDatabase") + return nil, fmt.Errorf("config is nil") } db, err := ConnectDB(config.DatabasePath) if err != nil || db == nil { + log.Error("Failed to connect to database in connectToDatabase") return nil, fmt.Errorf("failed to connect to database: %v", err) } - log.Println("Connected to database in connectToDatabase") + log.Info("Connected to database in connectToDatabase") return db, nil } @@ -225,7 +230,7 @@ func startServer(port int, handler http.Handler) { if log != nil { log.Info("Server starting on port %d", port) } - fmt.Printf("Server listening on port %d\n", port) + log.Info("Server listening on port %d\n", port) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), handler)) }