diff --git a/logging/example_test.go b/logging/example_test.go index a013ed1..eb3aa73 100644 --- a/logging/example_test.go +++ b/logging/example_test.go @@ -3,7 +3,7 @@ package logging_test import "github.com/kisom/goutils/logging" var log = logging.Init() -var olog = logging.New("subsystem #42", logging.LevelNotice) +var olog, _ = logging.New("subsystem #42", logging.LevelNotice) func Example() { log.Notice("Hello, world.") diff --git a/logging/levels.go b/logging/levels.go index 209cac1..8efd583 100644 --- a/logging/levels.go +++ b/logging/levels.go @@ -64,7 +64,7 @@ var levelPrefix = [...]string{ var DateFormat = "2006-01-02T15:03:04-0700" func (l *Logger) outputf(level Level, format string, v []interface{}) { - if !logConfig.registered[l.domain] { + if !l.Enabled() { return } @@ -81,7 +81,7 @@ func (l *Logger) outputf(level Level, format string, v []interface{}) { } func (l *Logger) output(level Level, v []interface{}) { - if !logConfig.registered[l.domain] { + if !l.Enabled() { return } diff --git a/logging/log.go b/logging/log.go index da600a0..a7e7df9 100644 --- a/logging/log.go +++ b/logging/log.go @@ -9,16 +9,17 @@ import ( "io" "os" "path/filepath" + "strings" "sync" "github.com/kisom/goutils/mwc" ) var logConfig = struct { - registered map[string]bool + registered map[string]*Logger lock *sync.Mutex }{ - registered: map[string]bool{}, + registered: map[string]*Logger{}, lock: new(sync.Mutex), } @@ -28,16 +29,19 @@ const DefaultLevel = LevelNotice // Init returns a new default logger. The domain is set to the // program's name, and the default logging level is used. func Init() *Logger { - return New(filepath.Base(os.Args[0]), DefaultLevel) + l, _ := New(filepath.Base(os.Args[0]), DefaultLevel) + return l } // A Logger writes logs on behalf of a particular domain at a certain // level. type Logger struct { - domain string - level Level - out io.WriteCloser - err io.WriteCloser + enabled bool + lock *sync.Mutex + domain string + level Level + out io.WriteCloser + err io.WriteCloser } // Close closes the log's writers and suppresses the logger. @@ -55,15 +59,30 @@ func (l *Logger) Close() error { func Suppress(domain string) { logConfig.lock.Lock() defer logConfig.lock.Unlock() - logConfig.registered[domain] = false + l, ok := logConfig.registered[domain] + if ok { + l.Suppress() + } +} + +// SuppressPrefix suppress logs whose domain is prefixed with the +// prefix. +func SuppressPrefix(prefix string) { + logConfig.lock.Lock() + defer logConfig.lock.Unlock() + for domain, l := range logConfig.registered { + if strings.HasPrefix(domain, prefix) { + l.Suppress() + } + } } // SuppressAll suppresses all logging output. func SuppressAll() { logConfig.lock.Lock() defer logConfig.lock.Unlock() - for domain := range logConfig.registered { - logConfig.registered[domain] = false + for _, l := range logConfig.registered { + l.Suppress() } } @@ -71,48 +90,90 @@ func SuppressAll() { func Enable(domain string) { logConfig.lock.Lock() defer logConfig.lock.Unlock() - logConfig.registered[domain] = true + l, ok := logConfig.registered[domain] + if ok { + l.Enable() + } +} + +// EnablePrefix enables logs whose domain is prefixed with prefix. +func EnablePrefix(prefix string) { + logConfig.lock.Lock() + defer logConfig.lock.Unlock() + for domain, l := range logConfig.registered { + if strings.HasPrefix(domain, prefix) { + l.Enable() + } + } } // EnableAll enables all domains. func EnableAll() { logConfig.lock.Lock() defer logConfig.lock.Unlock() - for domain := range logConfig.registered { - logConfig.registered[domain] = true + for _, l := range logConfig.registered { + l.Enable() } } // New returns a new logger that writes to standard output for Notice -// and below and standard error for levels above Notice. -func New(domain string, level Level) *Logger { - l := &Logger{ +// and below and standard error for levels above Notice. If a logger +// with the same domain exists, the logger will set its level to level +// and return the logger; in this case, the registered return value +// will be true. +func New(domain string, level Level) (l *Logger, registered bool) { + logConfig.lock.Lock() + defer logConfig.lock.Unlock() + + l = logConfig.registered[domain] + if l != nil { + l.SetLevel(level) + return l, true + } + + l = &Logger{ domain: domain, level: level, out: os.Stdout, err: os.Stderr, + lock: new(sync.Mutex), } - Enable(domain) - return l + l.Enable() + logConfig.registered[domain] = l + return l, false } -// NewWriters returns a new logger that writes to the w io.WriteCloser for -// Notice and below and to the e io.WriteCloser for levels above Notice. If e is nil, w will be used. -func NewFromWriters(domain string, level Level, w, e io.WriteCloser) *Logger { +// NewWriters returns a new logger that writes to the w io.WriteCloser +// for Notice and below and to the e io.WriteCloser for levels above +// Notice. If e is nil, w will be used. If a logger with the same +// domain exists, the logger will set its level to level and return +// the logger; in this case, the registered return value will be true. +func NewFromWriters(domain string, level Level, w, e io.WriteCloser) (l *Logger, registered bool) { + logConfig.lock.Lock() + defer logConfig.lock.Unlock() + + l = logConfig.registered[domain] + if l != nil { + l.SetLevel(level) + return l, true + } + if e == nil { e = w } - l := &Logger{ + l = &Logger{ domain: domain, level: level, out: w, err: e, + lock: new(sync.Mutex), } - Enable(domain) - return l + l.Enable() + logConfig.registered[domain] = l + return l, false } // NewFile returns a new logger that opens the files for writing. If @@ -122,6 +183,7 @@ func NewFromFile(domain string, level Level, outFile, errFile string, multiplex l := &Logger{ domain: domain, level: level, + lock: new(sync.Mutex), } outf, err := os.OpenFile(outFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) @@ -148,12 +210,21 @@ func NewFromFile(domain string, level Level, outFile, errFile string, multiplex // Enable allows output from the logger. func (l *Logger) Enable() { - Enable(l.domain) + l.lock.Lock() + defer l.lock.Unlock() + l.enabled = true +} + +// Enabled returns true if the logger is enabled. +func (l *Logger) Enabled() bool { + return l.enabled } // Suppress ignores output from the logger. func (l *Logger) Suppress() { - Suppress(l.domain) + l.lock.Lock() + defer l.lock.Unlock() + l.enabled = false } // Domain returns the domain of the logger. @@ -163,5 +234,7 @@ func (l *Logger) Domain() string { // SetLevel changes the level of the logger. func (l *Logger) SetLevel(level Level) { + l.lock.Lock() + defer l.lock.Unlock() l.level = level }