logging/logging.go

124 lines
2.7 KiB
Go

// Package logging provides a multi-backend leveled logging facility.
/*
Backends
package logging defines the following builtin backends:
* NoopBackend, which discards all messages
* FileBackend, which redirects logs to an io.Writer object. It has constructors
for files, stdout and stderr
* SyslogBackend, only available in unix-like systems, writes log entryies to a
syslog daemon.
A backend can safely be used by multiple loggers.
It is the caller's responsibility to call Close on backends when they are not
used anymore to free their resources.
*/
package logging
import (
"errors"
"fmt"
"strings"
"sync"
)
//nolint:gochecknoglobals // designed this way
var (
loggers map[string]*Logger
lock sync.Mutex
errInvalidLogLevel = errors.New("invalid log level")
)
// Level is the type of log levels.
type Level byte
//revive:disable:exported
const (
Trace Level = iota
Debug
Info
Notice
Warning
Error
Critical
Alert
Fatal
DefaultLevel = Info
)
//revive:enable:exported
//nolint:gochecknoglobals // designed this way
var levelNames = [...]string{
"TRACE", "DEBUG", "INFO", "NOTICE", "WARNING",
"ERROR", "CRITICAL", "ALERT", "FATAL",
}
// Name returns the name of the log level.
func (l Level) Name() string {
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) {
for pos, name := range levelNames {
if name == l {
return Level(pos), nil
}
}
return Debug, fmt.Errorf("unknown log level %q: %w", l, errInvalidLogLevel)
}
// 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
// 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 INFO).
func GetLogger(name string) *Logger {
lock.Lock()
defer lock.Unlock()
if name == "" {
name = "default"
}
l, ok := loggers[name]
if !ok {
l = NewLogger(name)
backend := NewStdoutBackend()
l.AddBackend(backend)
l.SetLevel(DefaultLevel)
loggers[name] = l
}
return l
}
func defaultFormatter(r *Record) string {
return fmt.Sprintf("%s [%-8s] %s: %s\n",
r.Timestamp.Format("2006/01/02 15:04:05"), r.Level.Name(), r.Logger,
strings.TrimSpace(r.Message))
}
func basicFormatter(r *Record) string {
return fmt.Sprintf("%s: %s", r.Logger, strings.TrimSpace(r.Message))
}
//nolint:gochecknoinits // init is used by design
func init() {
loggers = map[string]*Logger{}
logger := NewLogger("default")
backend := NewStdoutBackend()
logger.AddBackend(backend)
logger.SetLevel(DefaultLevel)
loggers["default"] = logger
}