// Copyright © 2016 Steve Francia <spf@spf13.com>. // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package jwalterweatherman import ( "fmt" "io" "log" ) type Threshold int func (t Threshold) String() string { return prefixes[t] } const ( LevelTrace Threshold = iota LevelDebug LevelInfo LevelWarn LevelError LevelCritical LevelFatal ) var prefixes map[Threshold]string = map[Threshold]string{ LevelTrace: "TRACE", LevelDebug: "DEBUG", LevelInfo: "INFO", LevelWarn: "WARN", LevelError: "ERROR", LevelCritical: "CRITICAL", LevelFatal: "FATAL", } // Notepad is where you leave a note! type Notepad struct { TRACE *log.Logger DEBUG *log.Logger INFO *log.Logger WARN *log.Logger ERROR *log.Logger CRITICAL *log.Logger FATAL *log.Logger LOG *log.Logger FEEDBACK *Feedback loggers [7]**log.Logger logHandle io.Writer outHandle io.Writer logThreshold Threshold stdoutThreshold Threshold prefix string flags int // One per Threshold logCounters [7]*logCounter } // NewNotepad create a new notepad. func NewNotepad(outThreshold Threshold, logThreshold Threshold, outHandle, logHandle io.Writer, prefix string, flags int) *Notepad { n := &Notepad{} n.loggers = [7]**log.Logger{&n.TRACE, &n.DEBUG, &n.INFO, &n.WARN, &n.ERROR, &n.CRITICAL, &n.FATAL} n.outHandle = outHandle n.logHandle = logHandle n.stdoutThreshold = outThreshold n.logThreshold = logThreshold if len(prefix) != 0 { n.prefix = "[" + prefix + "] " } else { n.prefix = "" } n.flags = flags n.LOG = log.New(n.logHandle, "LOG: ", n.flags) n.FEEDBACK = &Feedback{out: log.New(outHandle, "", 0), log: n.LOG} n.init() return n } // init creates the loggers for each level depending on the notepad thresholds. func (n *Notepad) init() { logAndOut := io.MultiWriter(n.outHandle, n.logHandle) for t, logger := range n.loggers { threshold := Threshold(t) counter := &logCounter{} n.logCounters[t] = counter prefix := n.prefix + threshold.String() + " " switch { case threshold >= n.logThreshold && threshold >= n.stdoutThreshold: *logger = log.New(io.MultiWriter(counter, logAndOut), prefix, n.flags) case threshold >= n.logThreshold: *logger = log.New(io.MultiWriter(counter, n.logHandle), prefix, n.flags) case threshold >= n.stdoutThreshold: *logger = log.New(io.MultiWriter(counter, n.outHandle), prefix, n.flags) default: // counter doesn't care about prefix and flags, so don't use them // for performance. *logger = log.New(counter, "", 0) } } } // SetLogThreshold changes the threshold above which messages are written to the // log file. func (n *Notepad) SetLogThreshold(threshold Threshold) { n.logThreshold = threshold n.init() } // SetLogOutput changes the file where log messages are written. func (n *Notepad) SetLogOutput(handle io.Writer) { n.logHandle = handle n.init() } // GetStdoutThreshold returns the defined Treshold for the log logger. func (n *Notepad) GetLogThreshold() Threshold { return n.logThreshold } // SetStdoutThreshold changes the threshold above which messages are written to the // standard output. func (n *Notepad) SetStdoutThreshold(threshold Threshold) { n.stdoutThreshold = threshold n.init() } // GetStdoutThreshold returns the Treshold for the stdout logger. func (n *Notepad) GetStdoutThreshold() Threshold { return n.stdoutThreshold } // SetPrefix changes the prefix used by the notepad. Prefixes are displayed between // brackets at the beginning of the line. An empty prefix won't be displayed at all. func (n *Notepad) SetPrefix(prefix string) { if len(prefix) != 0 { n.prefix = "[" + prefix + "] " } else { n.prefix = "" } n.init() } // SetFlags choose which flags the logger will display (after prefix and message // level). See the package log for more informations on this. func (n *Notepad) SetFlags(flags int) { n.flags = flags n.init() } // Feedback writes plainly to the outHandle while // logging with the standard extra information (date, file, etc). type Feedback struct { out *log.Logger log *log.Logger } func (fb *Feedback) Println(v ...interface{}) { fb.output(fmt.Sprintln(v...)) } func (fb *Feedback) Printf(format string, v ...interface{}) { fb.output(fmt.Sprintf(format, v...)) } func (fb *Feedback) Print(v ...interface{}) { fb.output(fmt.Sprint(v...)) } func (fb *Feedback) output(s string) { if fb.out != nil { fb.out.Output(2, s) } if fb.log != nil { fb.log.Output(2, s) } }