124 lines
2.7 KiB
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
|
|
}
|