gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

notepad.go (5482B)


      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 
      6 package jwalterweatherman
      7 
      8 import (
      9 	"fmt"
     10 	"io"
     11 	"io/ioutil"
     12 	"log"
     13 )
     14 
     15 type Threshold int
     16 
     17 func (t Threshold) String() string {
     18 	return prefixes[t]
     19 }
     20 
     21 const (
     22 	LevelTrace Threshold = iota
     23 	LevelDebug
     24 	LevelInfo
     25 	LevelWarn
     26 	LevelError
     27 	LevelCritical
     28 	LevelFatal
     29 )
     30 
     31 var prefixes map[Threshold]string = map[Threshold]string{
     32 	LevelTrace:    "TRACE",
     33 	LevelDebug:    "DEBUG",
     34 	LevelInfo:     "INFO",
     35 	LevelWarn:     "WARN",
     36 	LevelError:    "ERROR",
     37 	LevelCritical: "CRITICAL",
     38 	LevelFatal:    "FATAL",
     39 }
     40 
     41 // Notepad is where you leave a note!
     42 type Notepad struct {
     43 	TRACE    *log.Logger
     44 	DEBUG    *log.Logger
     45 	INFO     *log.Logger
     46 	WARN     *log.Logger
     47 	ERROR    *log.Logger
     48 	CRITICAL *log.Logger
     49 	FATAL    *log.Logger
     50 
     51 	LOG      *log.Logger
     52 	FEEDBACK *Feedback
     53 
     54 	loggers         [7]**log.Logger
     55 	logHandle       io.Writer
     56 	outHandle       io.Writer
     57 	logThreshold    Threshold
     58 	stdoutThreshold Threshold
     59 	prefix          string
     60 	flags           int
     61 
     62 	logListeners []LogListener
     63 }
     64 
     65 // A LogListener can ble supplied to a Notepad to listen on log writes for a given
     66 // threshold. This can be used to capture log events in unit tests and similar.
     67 // Note that this function will be invoked once for each log threshold. If
     68 // the given threshold is not of interest to you, return nil.
     69 // Note that these listeners will receive log events for a given threshold, even
     70 // if the current configuration says not to log it. That way you can count ERRORs even
     71 // if you don't print them to the console.
     72 type LogListener func(t Threshold) io.Writer
     73 
     74 // NewNotepad creates a new Notepad.
     75 func NewNotepad(
     76 	outThreshold Threshold,
     77 	logThreshold Threshold,
     78 	outHandle, logHandle io.Writer,
     79 	prefix string, flags int,
     80 	logListeners ...LogListener,
     81 ) *Notepad {
     82 
     83 	n := &Notepad{logListeners: logListeners}
     84 
     85 	n.loggers = [7]**log.Logger{&n.TRACE, &n.DEBUG, &n.INFO, &n.WARN, &n.ERROR, &n.CRITICAL, &n.FATAL}
     86 	n.outHandle = outHandle
     87 	n.logHandle = logHandle
     88 	n.stdoutThreshold = outThreshold
     89 	n.logThreshold = logThreshold
     90 
     91 	if len(prefix) != 0 {
     92 		n.prefix = "[" + prefix + "] "
     93 	} else {
     94 		n.prefix = ""
     95 	}
     96 
     97 	n.flags = flags
     98 
     99 	n.LOG = log.New(n.logHandle,
    100 		"LOG:   ",
    101 		n.flags)
    102 	n.FEEDBACK = &Feedback{out: log.New(outHandle, "", 0), log: n.LOG}
    103 
    104 	n.init()
    105 	return n
    106 }
    107 
    108 // init creates the loggers for each level depending on the notepad thresholds.
    109 func (n *Notepad) init() {
    110 	logAndOut := io.MultiWriter(n.outHandle, n.logHandle)
    111 
    112 	for t, logger := range n.loggers {
    113 		threshold := Threshold(t)
    114 		prefix := n.prefix + threshold.String() + " "
    115 
    116 		switch {
    117 		case threshold >= n.logThreshold && threshold >= n.stdoutThreshold:
    118 			*logger = log.New(n.createLogWriters(threshold, logAndOut), prefix, n.flags)
    119 
    120 		case threshold >= n.logThreshold:
    121 			*logger = log.New(n.createLogWriters(threshold, n.logHandle), prefix, n.flags)
    122 
    123 		case threshold >= n.stdoutThreshold:
    124 			*logger = log.New(n.createLogWriters(threshold, n.outHandle), prefix, n.flags)
    125 
    126 		default:
    127 			*logger = log.New(n.createLogWriters(threshold, ioutil.Discard), prefix, n.flags)
    128 		}
    129 	}
    130 }
    131 
    132 func (n *Notepad) createLogWriters(t Threshold, handle io.Writer) io.Writer {
    133 	if len(n.logListeners) == 0 {
    134 		return handle
    135 	}
    136 	writers := []io.Writer{handle}
    137 	for _, l := range n.logListeners {
    138 		w := l(t)
    139 		if w != nil {
    140 			writers = append(writers, w)
    141 		}
    142 	}
    143 
    144 	if len(writers) == 1 {
    145 		return handle
    146 	}
    147 
    148 	return io.MultiWriter(writers...)
    149 }
    150 
    151 // SetLogThreshold changes the threshold above which messages are written to the
    152 // log file.
    153 func (n *Notepad) SetLogThreshold(threshold Threshold) {
    154 	n.logThreshold = threshold
    155 	n.init()
    156 }
    157 
    158 // SetLogOutput changes the file where log messages are written.
    159 func (n *Notepad) SetLogOutput(handle io.Writer) {
    160 	n.logHandle = handle
    161 	n.init()
    162 }
    163 
    164 // GetStdoutThreshold returns the defined Treshold for the log logger.
    165 func (n *Notepad) GetLogThreshold() Threshold {
    166 	return n.logThreshold
    167 }
    168 
    169 // SetStdoutThreshold changes the threshold above which messages are written to the
    170 // standard output.
    171 func (n *Notepad) SetStdoutThreshold(threshold Threshold) {
    172 	n.stdoutThreshold = threshold
    173 	n.init()
    174 }
    175 
    176 // GetStdoutThreshold returns the Treshold for the stdout logger.
    177 func (n *Notepad) GetStdoutThreshold() Threshold {
    178 	return n.stdoutThreshold
    179 }
    180 
    181 // SetPrefix changes the prefix used by the notepad. Prefixes are displayed between
    182 // brackets at the beginning of the line. An empty prefix won't be displayed at all.
    183 func (n *Notepad) SetPrefix(prefix string) {
    184 	if len(prefix) != 0 {
    185 		n.prefix = "[" + prefix + "] "
    186 	} else {
    187 		n.prefix = ""
    188 	}
    189 	n.init()
    190 }
    191 
    192 // SetFlags choose which flags the logger will display (after prefix and message
    193 // level). See the package log for more informations on this.
    194 func (n *Notepad) SetFlags(flags int) {
    195 	n.flags = flags
    196 	n.init()
    197 }
    198 
    199 // Feedback writes plainly to the outHandle while
    200 // logging with the standard extra information (date, file, etc).
    201 type Feedback struct {
    202 	out *log.Logger
    203 	log *log.Logger
    204 }
    205 
    206 func (fb *Feedback) Println(v ...interface{}) {
    207 	fb.output(fmt.Sprintln(v...))
    208 }
    209 
    210 func (fb *Feedback) Printf(format string, v ...interface{}) {
    211 	fb.output(fmt.Sprintf(format, v...))
    212 }
    213 
    214 func (fb *Feedback) Print(v ...interface{}) {
    215 	fb.output(fmt.Sprint(v...))
    216 }
    217 
    218 func (fb *Feedback) output(s string) {
    219 	if fb.out != nil {
    220 		fb.out.Output(2, s)
    221 	}
    222 	if fb.log != nil {
    223 		fb.log.Output(2, s)
    224 	}
    225 }