package logging import ( "fmt" "io" "os" ) // Backend is the interface that specifies the methods that a backend must // implement type Backend interface { Write(*Record) error SetFormatter(*Formatter) SetLevel(Level) Level() Level Reopen() error } // // Backend to write in file-like objects // // FileBackend is a backend that writes to a file. type FileBackend struct { l io.Writer formatter *Formatter level Level filepath string } // NewStdoutBackend creates a new backend to write the logs on the standard // output func NewStdoutBackend() (b *FileBackend) { b = &FileBackend{ l: os.Stdout, formatter: &defaultFormatter, } return } // NewStderrBackend creates a new backend to write the logs on the error output func NewStderrBackend() (b *FileBackend) { b = &FileBackend{ l: os.Stderr, formatter: &defaultFormatter, } return } // NewFileBackend creates a new backend to write the logs in a given file func NewFileBackend(filename string) (*FileBackend, error) { fd, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) //nolint: gosec if err != nil { return nil, fmt.Errorf("Cannot open log file %s: %w", filename, err) } b := &FileBackend{ l: fd, formatter: &defaultFormatter, filepath: filename, } return b, nil } // NewIoBackend creates a new backend to write the logs in a given io.Writer func NewIoBackend(buf io.Writer) (b *FileBackend) { return &FileBackend{ l: buf, formatter: &defaultFormatter, } } func (b FileBackend) Write(r *Record) error { text := (*b.formatter)(r) _, err := io.WriteString(b.l, text) return err } // SetLevel changes the log level of the backend func (b *FileBackend) SetLevel(l Level) { b.level = l } // Level returns the log level set for this backend func (b *FileBackend) Level() Level { return b.level } // SetFormatter defines the formatter for this backend func (b *FileBackend) SetFormatter(f *Formatter) { b.formatter = f } // Reopen closes and reopens the file it writes to. It should be used after log // rotation func (b *FileBackend) Reopen() error { if b.filepath == "" { return nil } if c, ok := b.l.(io.Closer); ok { if err := c.Close(); err != nil { return err } } fd, err := os.OpenFile(b.filepath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) //nolint: gosec if err != nil { return fmt.Errorf("Cannot open log file %s: %w", b.filepath, err) } b.l = fd return nil } // // Noop Backend // // NoopBackend does nothing and discards all log entries without writing them anywhere type NoopBackend struct{} // NewNoopBackend creates a noop backend func NewNoopBackend() (Backend, error) { return &NoopBackend{}, nil } // Write is a noop func (nb *NoopBackend) Write(r *Record) error { return nil } // SetFormatter is a noop func (nb *NoopBackend) SetFormatter(f *Formatter) {} // SetLevel is a noop func (nb *NoopBackend) SetLevel(level Level) {} // Level always returns DefeultLevel func (nb *NoopBackend) Level() Level { return DefaultLevel } // Reopen is a noop func (nb *NoopBackend) Reopen() error { return nil }