package loggers import ( "fmt" "io" "os" "time" "github.com/haydenhargreaves/Potion/internal/infrastructure/logging" ) type FileLogger struct { writer io.Writer file *os.File filter logging.LogLevel } var _ logging.Logger = (*FileLogger)(nil) // NewFileLogger creates a new file logger, opened on the filepath provided. If any errors // occur, an error will be returned, along with an EMPTY logger. This is not a pointer return // so it will never be nil, just empty. // // This function does not close the file, cleanup function that is returned should be called // to close the file opened in this function. func NewFileLogger(filepath string, filter logging.LogLevel) (logging.Logger, func() error, error) { f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644) if err != nil { return &FileLogger{}, nil, err } if f == nil { return &FileLogger{}, nil, fmt.Errorf("File could not be opened. File is nil.") } logger := &FileLogger{ writer: f, file: f, filter: filter, } cleanup := func() error { return f.Close() } return logger, cleanup, nil } // Log implements the interface. func (l *FileLogger) Log(level logging.LogLevel, format string, v ...any) { // level is too low, do not log if !l.filter.MatchFilter(level) { return } timestamp := time.Now().UTC().Format("01/02/2006 - 15:04:05") fullFormat := fmt.Sprintf("%-13s %s | %s\n", "["+level+"]", timestamp, format) bytes := fmt.Appendf(nil, fullFormat, v...) l.writer.Write(bytes) l.file.Sync() }