226 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
	
	
| // 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"
 | |
| 	"io/ioutil"
 | |
| 	"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
 | |
| 
 | |
| 	logListeners []LogListener
 | |
| }
 | |
| 
 | |
| // A LogListener can ble supplied to a Notepad to listen on log writes for a given
 | |
| // threshold. This can be used to capture log events in unit tests and similar.
 | |
| // Note that this function will be invoked once for each log threshold. If
 | |
| // the given threshold is not of interest to you, return nil.
 | |
| // Note that these listeners will receive log events for a given threshold, even
 | |
| // if the current configuration says not to log it. That way you can count ERRORs even
 | |
| // if you don't print them to the console.
 | |
| type LogListener func(t Threshold) io.Writer
 | |
| 
 | |
| // NewNotepad creates a new Notepad.
 | |
| func NewNotepad(
 | |
| 	outThreshold Threshold,
 | |
| 	logThreshold Threshold,
 | |
| 	outHandle, logHandle io.Writer,
 | |
| 	prefix string, flags int,
 | |
| 	logListeners ...LogListener,
 | |
| ) *Notepad {
 | |
| 
 | |
| 	n := &Notepad{logListeners: logListeners}
 | |
| 
 | |
| 	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)
 | |
| 		prefix := n.prefix + threshold.String() + " "
 | |
| 
 | |
| 		switch {
 | |
| 		case threshold >= n.logThreshold && threshold >= n.stdoutThreshold:
 | |
| 			*logger = log.New(n.createLogWriters(threshold, logAndOut), prefix, n.flags)
 | |
| 
 | |
| 		case threshold >= n.logThreshold:
 | |
| 			*logger = log.New(n.createLogWriters(threshold, n.logHandle), prefix, n.flags)
 | |
| 
 | |
| 		case threshold >= n.stdoutThreshold:
 | |
| 			*logger = log.New(n.createLogWriters(threshold, n.outHandle), prefix, n.flags)
 | |
| 
 | |
| 		default:
 | |
| 			*logger = log.New(n.createLogWriters(threshold, ioutil.Discard), prefix, n.flags)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (n *Notepad) createLogWriters(t Threshold, handle io.Writer) io.Writer {
 | |
| 	if len(n.logListeners) == 0 {
 | |
| 		return handle
 | |
| 	}
 | |
| 	writers := []io.Writer{handle}
 | |
| 	for _, l := range n.logListeners {
 | |
| 		w := l(t)
 | |
| 		if w != nil {
 | |
| 			writers = append(writers, w)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if len(writers) == 1 {
 | |
| 		return handle
 | |
| 	}
 | |
| 
 | |
| 	return io.MultiWriter(writers...)
 | |
| }
 | |
| 
 | |
| // 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)
 | |
| 	}
 | |
| }
 |