mlog: use a Clone and Set methods instead of With methods

This commit is contained in:
Brian Picciano 2018-10-28 15:09:42 -04:00
parent 910704ae67
commit 5c85969bc7
2 changed files with 63 additions and 56 deletions

View File

@ -245,6 +245,8 @@ type msg struct {
// Logger will use them (in a thread-safe manner) to write out log messages. // Logger will use them (in a thread-safe manner) to write out log messages.
type Logger struct { type Logger struct {
wc io.WriteCloser wc io.WriteCloser
l *sync.RWMutex
wfn WriteFn wfn WriteFn
maxLevel uint maxLevel uint
kv KVer kv KVer
@ -262,14 +264,15 @@ type Logger struct {
func NewLogger(wc io.WriteCloser) *Logger { func NewLogger(wc io.WriteCloser) *Logger {
l := &Logger{ l := &Logger{
wc: wc, wc: wc,
l: new(sync.RWMutex),
wfn: DefaultWriteFn, wfn: DefaultWriteFn,
maxLevel: InfoLevel.Uint(),
msgBufPool: &sync.Pool{ msgBufPool: &sync.Pool{
New: func() interface{} { New: func() interface{} {
return new(bytes.Buffer) return new(bytes.Buffer)
}, },
}, },
msgCh: make(chan msg, 1024), msgCh: make(chan msg, 1024),
maxLevel: InfoLevel.Uint(),
stopCh: make(chan struct{}), stopCh: make(chan struct{}),
wg: new(sync.WaitGroup), wg: new(sync.WaitGroup),
} }
@ -281,8 +284,13 @@ func NewLogger(wc io.WriteCloser) *Logger {
return l return l
} }
func (l *Logger) cp() *Logger { // Clone returns a usable copy of the *Logger. Changes to the returned Logger
// will not effect the original, e.g. if the SetMaxLevel is called on one it
// will not effect the max level of written logs on the other. If Stop is called
// on any Logger it effects all parent and child Loggers produced from Clone.
func (l *Logger) Clone() *Logger {
l2 := *l l2 := *l
l2.l = new(sync.RWMutex)
return &l2 return &l2
} }
@ -324,37 +332,35 @@ func (l *Logger) spin() {
} }
} }
// WithMaxLevelUint returns a copy of the Logger with its max logging level set // SetMaxLevelUint sets the Logger's max logging level so that it will not log
// to the given uint. The Logger will not log any messages with a higher // any messages with a higher Level.Uint value.
// Level.Uint value. func (l *Logger) SetMaxLevelUint(i uint) {
func (l *Logger) WithMaxLevelUint(i uint) *Logger { l.l.Lock()
l = l.cp() defer l.l.Unlock()
l.maxLevel = i l.maxLevel = i
return l
} }
// WithMaxLevel returns a copy of the Logger with its max Level set to the given // SetMaxLevel sets the Logger's max logging level so that it will not log any
// one. The Logger will not log any messages with a higher Level.Uint value. // messages with a higher Level.Uint value.
func (l *Logger) WithMaxLevel(lvl Level) *Logger { func (l *Logger) SetMaxLevel(lvl Level) {
return l.WithMaxLevelUint(lvl.Uint()) l.SetMaxLevelUint(lvl.Uint())
} }
// WithWriteFn returns a copy of the Logger which will use the given WriteFn // SetWriteFn sets the Logger to use the given WriteFn to format and write
// to format and write Messages to the Logger's WriteCloser. This does not // Messages to Logger's WriteCloser.
// affect the WriteFn of the original Logger, and both can be used at the same func (l *Logger) SetWriteFn(wfn WriteFn) {
// time. l.l.Lock()
func (l *Logger) WithWriteFn(wfn WriteFn) *Logger { defer l.l.Unlock()
l = l.cp()
l.wfn = wfn l.wfn = wfn
return l
} }
// WithKV returns a copy of Logger which will implicitly use the KVers for all // AddKV sets the Logger to implicitly add the given KVers to all log messages.
// log messages. // This will not affect KVer data previously added the Logger or its parent (see
func (l *Logger) WithKV(kvs ...KVer) *Logger { // Clone), except to overwrite keys in the case of overlap.
l = l.cp() func (l *Logger) AddKV(kvs ...KVer) {
l.l.Lock()
defer l.l.Unlock()
l.kv = MergeInto(l.kv, kvs...) l.kv = MergeInto(l.kv, kvs...)
return l
} }
// Stop stops and cleans up any running go-routines and resources held by the // Stop stops and cleans up any running go-routines and resources held by the
@ -371,6 +377,9 @@ func (l *Logger) Stop() {
// will be Merge'd automatically. If the Level is a fatal (Uint() == 0) then // will be Merge'd automatically. If the Level is a fatal (Uint() == 0) then
// calling this will never return, and the process will have os.Exit(1) called. // calling this will never return, and the process will have os.Exit(1) called.
func (l *Logger) Log(lvl Level, msgStr string, kvs ...KVer) { func (l *Logger) Log(lvl Level, msgStr string, kvs ...KVer) {
l.l.RLock()
defer l.l.RUnlock()
if l.maxLevel < lvl.Uint() { if l.maxLevel < lvl.Uint() {
return return
} }

View File

@ -62,7 +62,7 @@ func TestLogger(t *T) {
select { select {
case <-l.testMsgWrittenCh: case <-l.testMsgWrittenCh:
case <-time.After(1 * time.Second): case <-time.After(1 * time.Second):
t.Fatal("waited too long for msg to write") return massert.Errf("waited too long for msg to write")
} }
out, err := buf.ReadString('\n') out, err := buf.ReadString('\n')
return massert.All( return massert.All(
@ -82,8 +82,7 @@ func TestLogger(t *T) {
assertOut("~ ERROR -- buz\n"), assertOut("~ ERROR -- buz\n"),
)) ))
{ l.SetMaxLevel(WarnLevel)
l := l.WithMaxLevel(WarnLevel)
l.Log(DebugLevel, "foo") l.Log(DebugLevel, "foo")
l.Log(InfoLevel, "bar") l.Log(InfoLevel, "bar")
l.Log(WarnLevel, "baz") l.Log(WarnLevel, "baz")
@ -92,10 +91,10 @@ func TestLogger(t *T) {
assertOut("~ WARN -- baz\n"), assertOut("~ WARN -- baz\n"),
assertOut("~ ERROR -- buz -- a=\"b\"\n"), assertOut("~ ERROR -- buz -- a=\"b\"\n"),
)) ))
}
{ l2 := l.Clone()
l2 := l.WithWriteFn(func(w io.Writer, msg Message) error { l2.SetMaxLevel(InfoLevel)
l2.SetWriteFn(func(w io.Writer, msg Message) error {
msg.Msg = strings.ToUpper(msg.Msg) msg.Msg = strings.ToUpper(msg.Msg)
return DefaultWriteFn(w, msg) return DefaultWriteFn(w, msg)
}) })
@ -108,7 +107,6 @@ func TestLogger(t *T) {
assertOut("~ ERROR -- buz\n"), assertOut("~ ERROR -- buz\n"),
)) ))
} }
}
func TestDefaultWriteFn(t *T) { func TestDefaultWriteFn(t *T) {
assertFormat := func(postfix string, msg Message) massert.Assertion { assertFormat := func(postfix string, msg Message) massert.Assertion {