1// Copyright © 2016 Steve Francia <spf@spf13.com>. 2// 3// Use of this source code is governed by an MIT-style 4// license that can be found in the LICENSE file. 5 6package jwalterweatherman 7 8import ( 9 "fmt" 10 "io" 11 "log" 12) 13 14type Threshold int 15 16func (t Threshold) String() string { 17 return prefixes[t] 18} 19 20const ( 21 LevelTrace Threshold = iota 22 LevelDebug 23 LevelInfo 24 LevelWarn 25 LevelError 26 LevelCritical 27 LevelFatal 28) 29 30var prefixes map[Threshold]string = map[Threshold]string{ 31 LevelTrace: "TRACE", 32 LevelDebug: "DEBUG", 33 LevelInfo: "INFO", 34 LevelWarn: "WARN", 35 LevelError: "ERROR", 36 LevelCritical: "CRITICAL", 37 LevelFatal: "FATAL", 38} 39 40// Notepad is where you leave a note! 41type Notepad struct { 42 TRACE *log.Logger 43 DEBUG *log.Logger 44 INFO *log.Logger 45 WARN *log.Logger 46 ERROR *log.Logger 47 CRITICAL *log.Logger 48 FATAL *log.Logger 49 50 LOG *log.Logger 51 FEEDBACK *Feedback 52 53 loggers [7]**log.Logger 54 logHandle io.Writer 55 outHandle io.Writer 56 logThreshold Threshold 57 stdoutThreshold Threshold 58 prefix string 59 flags int 60 61 // One per Threshold 62 logCounters [7]*logCounter 63} 64 65// NewNotepad create a new notepad. 66func NewNotepad(outThreshold Threshold, logThreshold Threshold, outHandle, logHandle io.Writer, prefix string, flags int) *Notepad { 67 n := &Notepad{} 68 69 n.loggers = [7]**log.Logger{&n.TRACE, &n.DEBUG, &n.INFO, &n.WARN, &n.ERROR, &n.CRITICAL, &n.FATAL} 70 n.outHandle = outHandle 71 n.logHandle = logHandle 72 n.stdoutThreshold = outThreshold 73 n.logThreshold = logThreshold 74 75 if len(prefix) != 0 { 76 n.prefix = "[" + prefix + "] " 77 } else { 78 n.prefix = "" 79 } 80 81 n.flags = flags 82 83 n.LOG = log.New(n.logHandle, 84 "LOG: ", 85 n.flags) 86 n.FEEDBACK = &Feedback{out: log.New(outHandle, "", 0), log: n.LOG} 87 88 n.init() 89 return n 90} 91 92// init creates the loggers for each level depending on the notepad thresholds. 93func (n *Notepad) init() { 94 logAndOut := io.MultiWriter(n.outHandle, n.logHandle) 95 96 for t, logger := range n.loggers { 97 threshold := Threshold(t) 98 counter := &logCounter{} 99 n.logCounters[t] = counter 100 prefix := n.prefix + threshold.String() + " " 101 102 switch { 103 case threshold >= n.logThreshold && threshold >= n.stdoutThreshold: 104 *logger = log.New(io.MultiWriter(counter, logAndOut), prefix, n.flags) 105 106 case threshold >= n.logThreshold: 107 *logger = log.New(io.MultiWriter(counter, n.logHandle), prefix, n.flags) 108 109 case threshold >= n.stdoutThreshold: 110 *logger = log.New(io.MultiWriter(counter, n.outHandle), prefix, n.flags) 111 112 default: 113 // counter doesn't care about prefix and flags, so don't use them 114 // for performance. 115 *logger = log.New(counter, "", 0) 116 } 117 } 118} 119 120// SetLogThreshold changes the threshold above which messages are written to the 121// log file. 122func (n *Notepad) SetLogThreshold(threshold Threshold) { 123 n.logThreshold = threshold 124 n.init() 125} 126 127// SetLogOutput changes the file where log messages are written. 128func (n *Notepad) SetLogOutput(handle io.Writer) { 129 n.logHandle = handle 130 n.init() 131} 132 133// GetStdoutThreshold returns the defined Treshold for the log logger. 134func (n *Notepad) GetLogThreshold() Threshold { 135 return n.logThreshold 136} 137 138// SetStdoutThreshold changes the threshold above which messages are written to the 139// standard output. 140func (n *Notepad) SetStdoutThreshold(threshold Threshold) { 141 n.stdoutThreshold = threshold 142 n.init() 143} 144 145// GetStdoutThreshold returns the Treshold for the stdout logger. 146func (n *Notepad) GetStdoutThreshold() Threshold { 147 return n.stdoutThreshold 148} 149 150// SetPrefix changes the prefix used by the notepad. Prefixes are displayed between 151// brackets at the beginning of the line. An empty prefix won't be displayed at all. 152func (n *Notepad) SetPrefix(prefix string) { 153 if len(prefix) != 0 { 154 n.prefix = "[" + prefix + "] " 155 } else { 156 n.prefix = "" 157 } 158 n.init() 159} 160 161// SetFlags choose which flags the logger will display (after prefix and message 162// level). See the package log for more informations on this. 163func (n *Notepad) SetFlags(flags int) { 164 n.flags = flags 165 n.init() 166} 167 168// Feedback writes plainly to the outHandle while 169// logging with the standard extra information (date, file, etc). 170type Feedback struct { 171 out *log.Logger 172 log *log.Logger 173} 174 175func (fb *Feedback) Println(v ...interface{}) { 176 fb.output(fmt.Sprintln(v...)) 177} 178 179func (fb *Feedback) Printf(format string, v ...interface{}) { 180 fb.output(fmt.Sprintf(format, v...)) 181} 182 183func (fb *Feedback) Print(v ...interface{}) { 184 fb.output(fmt.Sprint(v...)) 185} 186 187func (fb *Feedback) output(s string) { 188 if fb.out != nil { 189 fb.out.Output(2, s) 190 } 191 if fb.log != nil { 192 fb.log.Output(2, s) 193 } 194} 195