package hlog import ( "io" "os" "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/pkgerrors" ) // Logger wraps a zerolog.Logger and manages an optional log file. // It embeds *zerolog.Logger, so all zerolog methods are available directly. type Logger struct { *zerolog.Logger logFile *os.File } // NewLogger creates a new Logger instance based on the provided configuration. // // The logger output depends on cfg.LogOutput: // - "console": Logs to the provided io.Writer w // - "file": Logs to a file in cfg.LogDir (w can be nil) // - "both": Logs to both the io.Writer and a file // // When file logging is enabled, cfg.LogDir must be set to a valid directory path. // The log file will be named "server.log" and placed in that directory. // // The logger is configured with: // - Unix timestamp format // - Error stack trace marshaling // - Log level from cfg.LogLevel // // Returns an error if: // - cfg is nil // - w is nil when cfg.LogOutput is not "file" // - cfg.LogDir is empty when file logging is enabled // - cfg.LogFileName is empty when file logging is enabled // - The log file cannot be created func NewLogger( cfg *Config, w io.Writer, ) (*Logger, error) { if cfg == nil { return nil, errors.New("No config provided") } if w == nil && cfg.LogOutput != "file" { return nil, errors.New("No Writer provided for log output.") } zerolog.TimeFieldFormat = zerolog.TimeFormatUnix zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack var consoleWriter zerolog.ConsoleWriter if w != nil { consoleWriter = zerolog.ConsoleWriter{Out: w} } var logFile *os.File var err error if cfg.LogOutput == "file" || cfg.LogOutput == "both" { if cfg.LogDir == "" { return nil, errors.New("LOG_DIR must be set when LOG_OUTPUT is 'file' or 'both'") } if cfg.LogFileName == "" { return nil, errors.New("LOG_FILE_NAME must be set when LOG_OUTPUT is 'file' or 'both'") } logFile, err = newLogFile(cfg.LogDir, cfg.LogFileName, cfg.LogAppend) if err != nil { return nil, errors.Wrap(err, "newLogFile") } } var output io.Writer if logFile != nil { if w != nil { output = zerolog.MultiLevelWriter(logFile, consoleWriter) } else { output = logFile } } else { output = consoleWriter } logger := zerolog.New(output). With(). Timestamp(). Logger(). Level(cfg.LogLevel) hlog := &Logger{ Logger: &logger, logFile: logFile, } return hlog, nil }