fixes linting errors

This commit is contained in:
Bruno Carlin 2020-02-01 17:30:09 +01:00
parent 3bc019fd4e
commit c6819aa61d
5 changed files with 102 additions and 37 deletions

View file

@ -1,12 +1,13 @@
package logging package logging
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
) )
// Backend is the interface that specifies the methods that a backend must
// implement
type Backend interface { type Backend interface {
Write(*Record) error Write(*Record) error
SetFormatter(*Formatter) SetFormatter(*Formatter)
@ -19,6 +20,7 @@ type Backend interface {
// Backend to write in file-like objects // Backend to write in file-like objects
// //
// FileBackend is a backend that writes to a file.
type FileBackend struct { type FileBackend struct {
l io.Writer l io.Writer
formatter *Formatter formatter *Formatter
@ -26,7 +28,8 @@ type FileBackend struct {
filepath string filepath string
} }
// Creates a new backend to write the logs on the standard output // NewStdoutBackend creates a new backend to write the logs on the standard
// output
func NewStdoutBackend() (b *FileBackend) { func NewStdoutBackend() (b *FileBackend) {
b = &FileBackend{ b = &FileBackend{
l: os.Stdout, l: os.Stdout,
@ -35,7 +38,7 @@ func NewStdoutBackend() (b *FileBackend) {
return return
} }
// Creates a new backend to write the logs on the error output // NewStderrBackend creates a new backend to write the logs on the error output
func NewStderrBackend() (b *FileBackend) { func NewStderrBackend() (b *FileBackend) {
b = &FileBackend{ b = &FileBackend{
l: os.Stderr, l: os.Stderr,
@ -44,21 +47,23 @@ func NewStderrBackend() (b *FileBackend) {
return return
} }
// Creates a new backend to write the logs in a given file // NewFileBackend creates a new backend to write the logs in a given file
func NewFileBackend(filename string) (b *FileBackend, e error) { func NewFileBackend(filename string) (*FileBackend, error) {
filename_fd, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) fd, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) //nolint: gosec
if err != nil { if err != nil {
e = errors.New(fmt.Sprintf("Cannot open log file %s (%s)", filename, err.Error())) return nil, fmt.Errorf("Cannot open log file %s: %w", filename, err)
} }
b = &FileBackend{
l: filename_fd, b := &FileBackend{
l: fd,
formatter: &defaultFormatter, formatter: &defaultFormatter,
filepath: filename, filepath: filename,
} }
return
return b, nil
} }
// Creates a new backend to write the logs in a given io.Writer // NewIoBackend creates a new backend to write the logs in a given io.Writer
func NewIoBackend(buf io.Writer) (b *FileBackend) { func NewIoBackend(buf io.Writer) (b *FileBackend) {
return &FileBackend{ return &FileBackend{
l: buf, l: buf,
@ -72,18 +77,23 @@ func (b FileBackend) Write(r *Record) error {
return err return err
} }
// SetLevel changes the log level of the backend
func (b *FileBackend) SetLevel(l Level) { func (b *FileBackend) SetLevel(l Level) {
b.level = l b.level = l
} }
// Level returns the log level set for this backend
func (b *FileBackend) Level() Level { func (b *FileBackend) Level() Level {
return b.level return b.level
} }
// SetFormatter defines the formatter for this backend
func (b *FileBackend) SetFormatter(f *Formatter) { func (b *FileBackend) SetFormatter(f *Formatter) {
b.formatter = f b.formatter = f
} }
// Reopen closes and reopens the file it writes to. It should be used after log
// rotation
func (b *FileBackend) Reopen() error { func (b *FileBackend) Reopen() error {
if b.filepath == "" { if b.filepath == "" {
return nil return nil
@ -95,7 +105,7 @@ func (b *FileBackend) Reopen() error {
} }
} }
fd, err := os.OpenFile(b.filepath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) fd, err := os.OpenFile(b.filepath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) //nolint: gosec
if err != nil { if err != nil {
return fmt.Errorf("Cannot open log file %s: %w", b.filepath, err) return fmt.Errorf("Cannot open log file %s: %w", b.filepath, err)
} }
@ -109,24 +119,31 @@ func (b *FileBackend) Reopen() error {
// Noop Backend // Noop Backend
// //
// NoopBackend does nothing and discards all log entries without writing them anywhere
type NoopBackend struct{} type NoopBackend struct{}
// NewNoopBackend creates a noop backend
func NewNoopBackend() (Backend, error) { func NewNoopBackend() (Backend, error) {
return &NoopBackend{}, nil return &NoopBackend{}, nil
} }
// Write is a noop
func (nb *NoopBackend) Write(r *Record) error { func (nb *NoopBackend) Write(r *Record) error {
return nil return nil
} }
// SetFormatter is a noop
func (nb *NoopBackend) SetFormatter(f *Formatter) {} func (nb *NoopBackend) SetFormatter(f *Formatter) {}
// SetLevel is a noop
func (nb *NoopBackend) SetLevel(level Level) {} func (nb *NoopBackend) SetLevel(level Level) {}
// Level always returns DefeultLevel
func (nb *NoopBackend) Level() Level { func (nb *NoopBackend) Level() Level {
return DefaultLevel return DefaultLevel
} }
// Reopen is a noop
func (nb *NoopBackend) Reopen() error { func (nb *NoopBackend) Reopen() error {
return nil return nil
} }

View file

@ -12,12 +12,16 @@ import (
// Syslog Backend // Syslog Backend
// //
// SyslogBackend writes the logs to a syslog system
type SyslogBackend struct { type SyslogBackend struct {
w *syslog.Writer w *syslog.Writer
formatter *Formatter formatter *Formatter
level Level level Level
} }
// NewSyslogBackend initializes a new connection to a syslog server with the
// given facility.
// tag can contain an identifier for the log stream. It defaults to os.Arg[0].
func NewSyslogBackend(facilityName string, tag string) (Backend, error) { func NewSyslogBackend(facilityName string, tag string) (Backend, error) {
f, err := facility(facilityName) f, err := facility(facilityName)
if err != nil { if err != nil {
@ -34,37 +38,42 @@ func NewSyslogBackend(facilityName string, tag string) (Backend, error) {
return sb, nil return sb, nil
} }
// Write sends an entry to the syslog server
func (sb *SyslogBackend) Write(r *Record) (err error) { func (sb *SyslogBackend) Write(r *Record) (err error) {
text := (*sb.formatter)(r) text := (*sb.formatter)(r)
switch r.Level { switch r.Level {
case DEBUG: case Debug:
err = sb.w.Debug(text) err = sb.w.Debug(text)
case INFO: case Info:
err = sb.w.Info(text) err = sb.w.Info(text)
case WARNING: case Warning:
err = sb.w.Warning(text) err = sb.w.Warning(text)
case ERROR: case Error:
err = sb.w.Err(text) err = sb.w.Err(text)
case CRITICAL: case Critical:
err = sb.w.Crit(text) err = sb.w.Crit(text)
case FATAL: case Fatal:
err = sb.w.Emerg(text) err = sb.w.Emerg(text)
} }
return err return err
} }
// SetFormatter defines the formatter for this backend
func (sb *SyslogBackend) SetFormatter(f *Formatter) { func (sb *SyslogBackend) SetFormatter(f *Formatter) {
sb.formatter = f sb.formatter = f
} }
// SetLevel changes the log level of the backend
func (sb *SyslogBackend) SetLevel(level Level) { func (sb *SyslogBackend) SetLevel(level Level) {
sb.level = level sb.level = level
} }
// Level returns the log level set for this backend
func (sb *SyslogBackend) Level() Level { func (sb *SyslogBackend) Level() Level {
return sb.level return sb.level
} }
// Reopen is a no-op
func (sb *SyslogBackend) Reopen() error { func (sb *SyslogBackend) Reopen() error {
return nil return nil
} }
@ -93,9 +102,10 @@ var facilities = map[string]syslog.Priority{
} }
func facility(name string) (syslog.Priority, error) { func facility(name string) (syslog.Priority, error) {
if p, ok := facilities[strings.ToLower(name)]; !ok { p, ok := facilities[strings.ToLower(name)]
if !ok {
return 0, fmt.Errorf("Facility '%s' does not exist", name) return 0, fmt.Errorf("Facility '%s' does not exist", name)
} else { }
return p, nil return p, nil
} }
}

View file

@ -7,35 +7,38 @@ import (
"sync" "sync"
) )
// Logger is a facility that writes logs to one or more backands (files,
// stdout/stderr, syslog, etc.) which can be configured independently
//
// Loggers are concurrent-safe.
type Logger struct { type Logger struct {
// added to avoid concurrent writes
sync.Mutex sync.Mutex
name string name string
level Level
backends []Backend backends []Backend
} }
// NewLogger initializes a new Logger with no backend and with the default log level.
func NewLogger(name string) (l *Logger) { func NewLogger(name string) (l *Logger) {
l = &Logger{ l = &Logger{
name: name, name: name,
level: DEFAULT_LEVEL,
} }
return return
} }
// Add a new Backend to the logger // AddBackend add a new Backend to the logger. All set backends are kept.
func (l *Logger) AddBackend(b Backend) { func (l *Logger) AddBackend(b Backend) {
l.backends = append(l.backends, b) l.backends = append(l.backends, b)
} }
// Sets the backend list to the logger. Any existing backend will be lost // SetBackend sets the backend list to the logger. Any existing backend will be lost.
// FIXME must close file backends to avoid fd leaks
func (l *Logger) SetBackend(b ...Backend) { func (l *Logger) SetBackend(b ...Backend) {
l.backends = b l.backends = b
} }
// SetLevel changes the log level of all regisered backends to the given level.
func (l *Logger) SetLevel(level Level) { func (l *Logger) SetLevel(level Level) {
l.level = level
for _, backend := range l.backends { for _, backend := range l.backends {
backend.SetLevel(level) backend.SetLevel(level)
} }
@ -51,12 +54,18 @@ func (b *buffer) Write(p []byte) (n int, err error) {
return len(p), nil return len(p), nil
} }
// AsStdLog encapsulate the logger in an instance of lof.Logger from the
// standard library and returns it.
//
// It is there for interoperability reasons.
func (l *Logger) AsStdLog(level Level) *log.Logger { func (l *Logger) AsStdLog(level Level) *log.Logger {
stdLogger := log.New(&buffer{logger: l, level: level}, "", 0) stdLogger := log.New(&buffer{logger: l, level: level}, "", 0)
return stdLogger return stdLogger
} }
// Log sends a record containing the message `m` to the registered backends
// whose level is at least `level`
func (l *Logger) Log(level Level, m string) { func (l *Logger) Log(level Level, m string) {
l.Lock() l.Lock()
defer l.Unlock() defer l.Unlock()
@ -64,56 +73,74 @@ func (l *Logger) Log(level Level, m string) {
r := NewRecord(l.name, level, m) r := NewRecord(l.name, level, m)
for _, backend := range l.backends { for _, backend := range l.backends {
if r.Level >= backend.Level() { if r.Level >= backend.Level() {
backend.Write(r) _ = backend.Write(r)
} }
} }
} }
// Debug logs a message with the Debug level
func (l *Logger) Debug(text string) { func (l *Logger) Debug(text string) {
l.Log(DEBUG, text) l.Log(Debug, text)
} }
// Debugf formats the message with given args and logs the result with the
// Debug level
func (l *Logger) Debugf(text string, args ...interface{}) { func (l *Logger) Debugf(text string, args ...interface{}) {
l.Debug(fmt.Sprintf(text, args...)) l.Debug(fmt.Sprintf(text, args...))
} }
// Info logs a message with the Info level
func (l *Logger) Info(text string) { func (l *Logger) Info(text string) {
l.Log(INFO, text) l.Log(Info, text)
} }
// Infof formats the message with given args and logs the result with the
// Info level
func (l *Logger) Infof(text string, args ...interface{}) { func (l *Logger) Infof(text string, args ...interface{}) {
l.Info(fmt.Sprintf(text, args...)) l.Info(fmt.Sprintf(text, args...))
} }
// Warning logs a message with the Warning level
func (l *Logger) Warning(text string) { func (l *Logger) Warning(text string) {
l.Log(WARNING, text) l.Log(Warning, text)
} }
// Warningf formats the message with given args and logs the result with the
// Warning level
func (l *Logger) Warningf(text string, args ...interface{}) { func (l *Logger) Warningf(text string, args ...interface{}) {
l.Warning(fmt.Sprintf(text, args...)) l.Warning(fmt.Sprintf(text, args...))
} }
// Error logs a message with the Error level
func (l *Logger) Error(text string) { func (l *Logger) Error(text string) {
l.Log(ERROR, text) l.Log(Error, text)
} }
// Errorf formats the message with given args and logs the result with the
// Error level
func (l *Logger) Errorf(text string, args ...interface{}) { func (l *Logger) Errorf(text string, args ...interface{}) {
l.Error(fmt.Sprintf(text, args...)) l.Error(fmt.Sprintf(text, args...))
} }
// Critical logs a message with the Critical level
func (l *Logger) Critical(text string) { func (l *Logger) Critical(text string) {
l.Log(CRITICAL, text) l.Log(Critical, text)
} }
// Criticalf formats the message with given args and logs the result with the
// Critical level
func (l *Logger) Criticalf(text string, args ...interface{}) { func (l *Logger) Criticalf(text string, args ...interface{}) {
l.Critical(fmt.Sprintf(text, args...)) l.Critical(fmt.Sprintf(text, args...))
} }
// Fatal logs a message with the Fatal level
func (l *Logger) Fatal(text string) { func (l *Logger) Fatal(text string) {
l.Log(DEBUG, text) l.Log(Debug, text)
os.Exit(100) os.Exit(100)
} }
// Fatalf formats the message with given args and logs the result with the
// Fatal level
func (l *Logger) Fatalf(text string, args ...interface{}) { func (l *Logger) Fatalf(text string, args ...interface{}) {
l.Fatal(fmt.Sprintf(text, args...)) l.Fatal(fmt.Sprintf(text, args...))
} }

View file

@ -1,7 +1,6 @@
package logging package logging
import ( import (
"errors"
"fmt" "fmt"
"strings" "strings"
"sync" "sync"
@ -12,8 +11,10 @@ var (
lock sync.Mutex lock sync.Mutex
) )
// Level is the type of log levels
type Level byte type Level byte
//nolint: golint
const ( const (
Debug Level = iota Debug Level = iota
Info Info
@ -26,21 +27,28 @@ const (
var levelNames = [6]string{"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "FATAL"} var levelNames = [6]string{"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "FATAL"}
// Name returns the name of the log level
func (l Level) Name() string { func (l Level) Name() string {
return levelNames[l] return levelNames[l]
} }
// LevelByName creates a log level given its name. It returns an error if the
// name does not match any level.
func LevelByName(l string) (Level, error) { func LevelByName(l string) (Level, error) {
for pos, name := range levelNames { for pos, name := range levelNames {
if name == l { if name == l {
return Level(pos), nil return Level(pos), nil
} }
} }
return DEBUG, errors.New(fmt.Sprintf("Invalid log level %s", l)) return Debug, fmt.Errorf("Invalid log level %s", l)
} }
// Formatter is the types of the functions that can be used to format a log
// entry. They take a pointer to a record and return a formatted string.
type Formatter func(*Record) string type Formatter func(*Record) string
// GetLogger returns a logger given its name. if the logger does not exist, it
// initializes one with the defaults (it logs to stdout with level debug)
func GetLogger(name string) (l *Logger) { func GetLogger(name string) (l *Logger) {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()

View file

@ -4,6 +4,8 @@ import (
"time" "time"
) )
// Record contains the data to be logged. It is passed to a formatter to
// generate the logged message
type Record struct { type Record struct {
Logger string Logger string
Timestamp time.Time Timestamp time.Time
@ -11,6 +13,7 @@ type Record struct {
Message string Message string
} }
// NewRecord creates a new record, setting its timestamp to time.Now()
func NewRecord(name string, l Level, m string) (r *Record) { func NewRecord(name string, l Level, m string) (r *Record) {
r = &Record{ r = &Record{
Logger: name, Logger: name,