mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-26 13:29:12 -05:00
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)
|
||
|
}
|
||
|
}
|