mirror of
https://github.com/octoleo/syncthing.git
synced 2024-12-22 19:08:58 +00:00
Adopt calmh/logger into lib/logger
This commit is contained in:
parent
5ae84970e7
commit
2de364414f
19
lib/logger/LICENSE
Normal file
19
lib/logger/LICENSE
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (C) 2013 Jakob Borg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
- The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
281
lib/logger/logger.go
Normal file
281
lib/logger/logger.go
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||||
|
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package logger implements a standardized logger with callback functionality
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogLevel int
|
||||||
|
|
||||||
|
const (
|
||||||
|
LevelDebug LogLevel = iota
|
||||||
|
LevelVerbose
|
||||||
|
LevelInfo
|
||||||
|
LevelOK
|
||||||
|
LevelWarn
|
||||||
|
LevelFatal
|
||||||
|
NumLevels
|
||||||
|
)
|
||||||
|
|
||||||
|
// A MessageHandler is called with the log level and message text.
|
||||||
|
type MessageHandler func(l LogLevel, msg string)
|
||||||
|
|
||||||
|
type Logger interface {
|
||||||
|
AddHandler(level LogLevel, h MessageHandler)
|
||||||
|
SetFlags(flag int)
|
||||||
|
SetPrefix(prefix string)
|
||||||
|
Debugln(vals ...interface{})
|
||||||
|
Debugf(format string, vals ...interface{})
|
||||||
|
Verboseln(vals ...interface{})
|
||||||
|
Verbosef(format string, vals ...interface{})
|
||||||
|
Infoln(vals ...interface{})
|
||||||
|
Infof(format string, vals ...interface{})
|
||||||
|
Okln(vals ...interface{})
|
||||||
|
Okf(format string, vals ...interface{})
|
||||||
|
Warnln(vals ...interface{})
|
||||||
|
Warnf(format string, vals ...interface{})
|
||||||
|
Fatalln(vals ...interface{})
|
||||||
|
Fatalf(format string, vals ...interface{})
|
||||||
|
ShouldDebug(facility string) bool
|
||||||
|
SetDebug(facility string, enabled bool)
|
||||||
|
Facilities() (enabled, disabled []string)
|
||||||
|
NewFacility(facility string) Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
type logger struct {
|
||||||
|
logger *log.Logger
|
||||||
|
handlers [NumLevels][]MessageHandler
|
||||||
|
debug map[string]bool
|
||||||
|
mut sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default logger logs to standard output with a time prefix.
|
||||||
|
var DefaultLogger = New()
|
||||||
|
|
||||||
|
func New() Logger {
|
||||||
|
if os.Getenv("LOGGER_DISCARD") != "" {
|
||||||
|
// Hack to completely disable logging, for example when running benchmarks.
|
||||||
|
return &logger{
|
||||||
|
logger: log.New(ioutil.Discard, "", 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &logger{
|
||||||
|
logger: log.New(os.Stdout, "", log.Ltime),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHandler registers a new MessageHandler to receive messages with the
|
||||||
|
// specified log level or above.
|
||||||
|
func (l *logger) AddHandler(level LogLevel, h MessageHandler) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
l.handlers[level] = append(l.handlers[level], h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See log.SetFlags
|
||||||
|
func (l *logger) SetFlags(flag int) {
|
||||||
|
l.logger.SetFlags(flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See log.SetPrefix
|
||||||
|
func (l *logger) SetPrefix(prefix string) {
|
||||||
|
l.logger.SetPrefix(prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) callHandlers(level LogLevel, s string) {
|
||||||
|
for ll := LevelDebug; ll <= level; ll++ {
|
||||||
|
for _, h := range l.handlers[ll] {
|
||||||
|
h(level, strings.TrimSpace(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugln logs a line with a DEBUG prefix.
|
||||||
|
func (l *logger) Debugln(vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintln(vals...)
|
||||||
|
l.logger.Output(2, "DEBUG: "+s)
|
||||||
|
l.callHandlers(LevelDebug, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf logs a formatted line with a DEBUG prefix.
|
||||||
|
func (l *logger) Debugf(format string, vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintf(format, vals...)
|
||||||
|
l.logger.Output(2, "DEBUG: "+s)
|
||||||
|
l.callHandlers(LevelDebug, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infoln logs a line with a VERBOSE prefix.
|
||||||
|
func (l *logger) Verboseln(vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintln(vals...)
|
||||||
|
l.logger.Output(2, "VERBOSE: "+s)
|
||||||
|
l.callHandlers(LevelVerbose, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs a formatted line with a VERBOSE prefix.
|
||||||
|
func (l *logger) Verbosef(format string, vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintf(format, vals...)
|
||||||
|
l.logger.Output(2, "VERBOSE: "+s)
|
||||||
|
l.callHandlers(LevelVerbose, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infoln logs a line with an INFO prefix.
|
||||||
|
func (l *logger) Infoln(vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintln(vals...)
|
||||||
|
l.logger.Output(2, "INFO: "+s)
|
||||||
|
l.callHandlers(LevelInfo, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs a formatted line with an INFO prefix.
|
||||||
|
func (l *logger) Infof(format string, vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintf(format, vals...)
|
||||||
|
l.logger.Output(2, "INFO: "+s)
|
||||||
|
l.callHandlers(LevelInfo, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Okln logs a line with an OK prefix.
|
||||||
|
func (l *logger) Okln(vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintln(vals...)
|
||||||
|
l.logger.Output(2, "OK: "+s)
|
||||||
|
l.callHandlers(LevelOK, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Okf logs a formatted line with an OK prefix.
|
||||||
|
func (l *logger) Okf(format string, vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintf(format, vals...)
|
||||||
|
l.logger.Output(2, "OK: "+s)
|
||||||
|
l.callHandlers(LevelOK, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnln logs a formatted line with a WARNING prefix.
|
||||||
|
func (l *logger) Warnln(vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintln(vals...)
|
||||||
|
l.logger.Output(2, "WARNING: "+s)
|
||||||
|
l.callHandlers(LevelWarn, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf logs a formatted line with a WARNING prefix.
|
||||||
|
func (l *logger) Warnf(format string, vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintf(format, vals...)
|
||||||
|
l.logger.Output(2, "WARNING: "+s)
|
||||||
|
l.callHandlers(LevelWarn, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalln logs a line with a FATAL prefix and exits the process with exit
|
||||||
|
// code 1.
|
||||||
|
func (l *logger) Fatalln(vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintln(vals...)
|
||||||
|
l.logger.Output(2, "FATAL: "+s)
|
||||||
|
l.callHandlers(LevelFatal, s)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf logs a formatted line with a FATAL prefix and exits the process with
|
||||||
|
// exit code 1.
|
||||||
|
func (l *logger) Fatalf(format string, vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintf(format, vals...)
|
||||||
|
l.logger.Output(2, "FATAL: "+s)
|
||||||
|
l.callHandlers(LevelFatal, s)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldDebug returns true if the given facility has debugging enabled.
|
||||||
|
func (l *logger) ShouldDebug(facility string) bool {
|
||||||
|
l.mut.Lock()
|
||||||
|
res := l.debug[facility]
|
||||||
|
l.mut.Unlock()
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDebug enabled or disables debugging for the given facility name.
|
||||||
|
func (l *logger) SetDebug(facility string, enabled bool) {
|
||||||
|
l.mut.Lock()
|
||||||
|
l.debug[facility] = enabled
|
||||||
|
l.mut.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Facilities returns the currently known set of facilities, both those for
|
||||||
|
// which debug is enabled and those for which it is disabled.
|
||||||
|
func (l *logger) Facilities() (enabled, disabled []string) {
|
||||||
|
l.mut.Lock()
|
||||||
|
for facility, isEnabled := range l.debug {
|
||||||
|
if isEnabled {
|
||||||
|
enabled = append(enabled, facility)
|
||||||
|
} else {
|
||||||
|
disabled = append(disabled, facility)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.mut.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFacility returns a new logger bound to the named facility.
|
||||||
|
func (l *logger) NewFacility(facility string) Logger {
|
||||||
|
l.mut.Lock()
|
||||||
|
if l.debug == nil {
|
||||||
|
l.debug = make(map[string]bool)
|
||||||
|
}
|
||||||
|
l.debug[facility] = false
|
||||||
|
l.mut.Unlock()
|
||||||
|
|
||||||
|
return &facilityLogger{
|
||||||
|
logger: l,
|
||||||
|
facility: facility,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A facilityLogger is a regular logger but bound to a facility name. The
|
||||||
|
// Debugln and Debugf methods are no-ops unless debugging has been enabled for
|
||||||
|
// this facility on the parent logger.
|
||||||
|
type facilityLogger struct {
|
||||||
|
*logger
|
||||||
|
facility string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugln logs a line with a DEBUG prefix.
|
||||||
|
func (l *facilityLogger) Debugln(vals ...interface{}) {
|
||||||
|
if !l.ShouldDebug(l.facility) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.logger.Debugln(vals...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf logs a formatted line with a DEBUG prefix.
|
||||||
|
func (l *facilityLogger) Debugf(format string, vals ...interface{}) {
|
||||||
|
if !l.ShouldDebug(l.facility) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.logger.Debugf(format, vals...)
|
||||||
|
}
|
84
lib/logger/logger_test.go
Normal file
84
lib/logger/logger_test.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||||
|
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAPI(t *testing.T) {
|
||||||
|
l := New()
|
||||||
|
l.SetFlags(0)
|
||||||
|
l.SetPrefix("testing")
|
||||||
|
|
||||||
|
debug := 0
|
||||||
|
l.AddHandler(LevelDebug, checkFunc(t, LevelDebug, "test 0", &debug))
|
||||||
|
info := 0
|
||||||
|
l.AddHandler(LevelInfo, checkFunc(t, LevelInfo, "test 1", &info))
|
||||||
|
warn := 0
|
||||||
|
l.AddHandler(LevelWarn, checkFunc(t, LevelWarn, "test 2", &warn))
|
||||||
|
ok := 0
|
||||||
|
l.AddHandler(LevelOK, checkFunc(t, LevelOK, "test 3", &ok))
|
||||||
|
|
||||||
|
l.Debugf("test %d", 0)
|
||||||
|
l.Debugln("test", 0)
|
||||||
|
l.Infof("test %d", 1)
|
||||||
|
l.Infoln("test", 1)
|
||||||
|
l.Warnf("test %d", 2)
|
||||||
|
l.Warnln("test", 2)
|
||||||
|
l.Okf("test %d", 3)
|
||||||
|
l.Okln("test", 3)
|
||||||
|
|
||||||
|
if debug != 2 {
|
||||||
|
t.Errorf("Debug handler called %d != 2 times", debug)
|
||||||
|
}
|
||||||
|
if info != 2 {
|
||||||
|
t.Errorf("Info handler called %d != 2 times", info)
|
||||||
|
}
|
||||||
|
if warn != 2 {
|
||||||
|
t.Errorf("Warn handler called %d != 2 times", warn)
|
||||||
|
}
|
||||||
|
if ok != 2 {
|
||||||
|
t.Errorf("Ok handler called %d != 2 times", ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFunc(t *testing.T, expectl LogLevel, expectmsg string, counter *int) func(LogLevel, string) {
|
||||||
|
return func(l LogLevel, msg string) {
|
||||||
|
*counter++
|
||||||
|
if l != expectl {
|
||||||
|
t.Errorf("Incorrect message level %d != %d", l, expectl)
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(msg, expectmsg) {
|
||||||
|
t.Errorf("%q does not end with %q", msg, expectmsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFacilityDebugging(t *testing.T) {
|
||||||
|
l := New()
|
||||||
|
l.SetFlags(0)
|
||||||
|
|
||||||
|
msgs := 0
|
||||||
|
l.AddHandler(LevelDebug, func(l LogLevel, msg string) {
|
||||||
|
msgs++
|
||||||
|
if strings.Contains(msg, "f1") {
|
||||||
|
t.Fatal("Should not get message for facility f1")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
l.SetDebug("f0", true)
|
||||||
|
l.SetDebug("f1", false)
|
||||||
|
|
||||||
|
f0 := l.NewFacility("f0")
|
||||||
|
f1 := l.NewFacility("f1")
|
||||||
|
|
||||||
|
f0.Debugln("Debug line from f0")
|
||||||
|
f1.Debugln("Debug line from f1")
|
||||||
|
|
||||||
|
if msgs != 1 {
|
||||||
|
t.Fatalf("Incorrent number of messages, %d != 1", msgs)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user