2015-05-25 02:00:21 +00:00
|
|
|
// Copyright © 2014 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"
|
|
|
|
"os"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Level describes the chosen log level between
|
|
|
|
// debug and critical.
|
|
|
|
type Level int
|
|
|
|
|
|
|
|
type NotePad struct {
|
|
|
|
Handle io.Writer
|
|
|
|
Level Level
|
|
|
|
Prefix string
|
|
|
|
Logger **log.Logger
|
|
|
|
}
|
|
|
|
|
|
|
|
// Feedback is special. It writes plainly to the output while
|
|
|
|
// logging with the standard extra information (date, file, etc)
|
|
|
|
// Only Println and Printf are currently provided for this
|
|
|
|
type Feedback struct{}
|
|
|
|
|
|
|
|
const (
|
|
|
|
LevelTrace Level = iota
|
|
|
|
LevelDebug
|
|
|
|
LevelInfo
|
|
|
|
LevelWarn
|
|
|
|
LevelError
|
|
|
|
LevelCritical
|
|
|
|
LevelFatal
|
|
|
|
DefaultLogThreshold = LevelWarn
|
|
|
|
DefaultStdoutThreshold = LevelError
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
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
|
|
|
|
LogHandle io.Writer = ioutil.Discard
|
|
|
|
OutHandle io.Writer = os.Stdout
|
|
|
|
BothHandle io.Writer = io.MultiWriter(LogHandle, OutHandle)
|
|
|
|
NotePads []*NotePad = []*NotePad{trace, debug, info, warn, err, critical, fatal}
|
|
|
|
|
|
|
|
trace *NotePad = &NotePad{Level: LevelTrace, Handle: os.Stdout, Logger: &TRACE, Prefix: "TRACE: "}
|
|
|
|
debug *NotePad = &NotePad{Level: LevelDebug, Handle: os.Stdout, Logger: &DEBUG, Prefix: "DEBUG: "}
|
|
|
|
info *NotePad = &NotePad{Level: LevelInfo, Handle: os.Stdout, Logger: &INFO, Prefix: "INFO: "}
|
|
|
|
warn *NotePad = &NotePad{Level: LevelWarn, Handle: os.Stdout, Logger: &WARN, Prefix: "WARN: "}
|
|
|
|
err *NotePad = &NotePad{Level: LevelError, Handle: os.Stdout, Logger: &ERROR, Prefix: "ERROR: "}
|
|
|
|
critical *NotePad = &NotePad{Level: LevelCritical, Handle: os.Stdout, Logger: &CRITICAL, Prefix: "CRITICAL: "}
|
|
|
|
fatal *NotePad = &NotePad{Level: LevelFatal, Handle: os.Stdout, Logger: &FATAL, Prefix: "FATAL: "}
|
|
|
|
logThreshold Level = DefaultLogThreshold
|
|
|
|
outputThreshold Level = DefaultStdoutThreshold
|
2016-03-01 00:51:26 +00:00
|
|
|
|
|
|
|
DATE = log.Ldate
|
|
|
|
TIME = log.Ltime
|
|
|
|
SFILE = log.Lshortfile
|
|
|
|
LFILE = log.Llongfile
|
|
|
|
MSEC = log.Lmicroseconds
|
|
|
|
logFlags = DATE | TIME | SFILE
|
2015-05-25 02:00:21 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
SetStdoutThreshold(DefaultStdoutThreshold)
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize will setup the jWalterWeatherman standard approach of providing the user
|
|
|
|
// some feedback and logging a potentially different amount based on independent log and output thresholds.
|
|
|
|
// By default the output has a lower threshold than logged
|
|
|
|
// Don't use if you have manually set the Handles of the different levels as it will overwrite them.
|
|
|
|
func initialize() {
|
|
|
|
BothHandle = io.MultiWriter(LogHandle, OutHandle)
|
|
|
|
|
|
|
|
for _, n := range NotePads {
|
|
|
|
if n.Level < outputThreshold && n.Level < logThreshold {
|
|
|
|
n.Handle = ioutil.Discard
|
|
|
|
} else if n.Level >= outputThreshold && n.Level >= logThreshold {
|
|
|
|
n.Handle = BothHandle
|
|
|
|
} else if n.Level >= outputThreshold && n.Level < logThreshold {
|
|
|
|
n.Handle = OutHandle
|
|
|
|
} else {
|
|
|
|
n.Handle = LogHandle
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, n := range NotePads {
|
2016-03-01 00:51:26 +00:00
|
|
|
*n.Logger = log.New(n.Handle, n.Prefix, logFlags)
|
2015-05-25 02:00:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LOG = log.New(LogHandle,
|
|
|
|
"LOG: ",
|
2016-03-01 00:51:26 +00:00
|
|
|
logFlags)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the log Flags (Available flag: DATE, TIME, SFILE, LFILE and MSEC)
|
|
|
|
func SetLogFlag(flags int) {
|
|
|
|
logFlags = flags
|
2015-05-25 02:00:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Level returns the current global log threshold.
|
|
|
|
func LogThreshold() Level {
|
|
|
|
return logThreshold
|
|
|
|
}
|
|
|
|
|
|
|
|
// Level returns the current global output threshold.
|
|
|
|
func StdoutThreshold() Level {
|
|
|
|
return outputThreshold
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensures that the level provided is within the bounds of available levels
|
|
|
|
func levelCheck(level Level) Level {
|
|
|
|
switch {
|
|
|
|
case level <= LevelTrace:
|
|
|
|
return LevelTrace
|
|
|
|
case level >= LevelFatal:
|
|
|
|
return LevelFatal
|
|
|
|
default:
|
|
|
|
return level
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Establishes a threshold where anything matching or above will be logged
|
|
|
|
func SetLogThreshold(level Level) {
|
|
|
|
logThreshold = levelCheck(level)
|
|
|
|
initialize()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Establishes a threshold where anything matching or above will be output
|
|
|
|
func SetStdoutThreshold(level Level) {
|
|
|
|
outputThreshold = levelCheck(level)
|
|
|
|
initialize()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conveniently Sets the Log Handle to a io.writer created for the file behind the given filepath
|
|
|
|
// Will only append to this file
|
|
|
|
func SetLogFile(path string) {
|
|
|
|
file, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
|
|
|
|
if err != nil {
|
|
|
|
CRITICAL.Println("Failed to open log file:", path, err)
|
|
|
|
os.Exit(-1)
|
|
|
|
}
|
2016-03-01 00:51:26 +00:00
|
|
|
|
|
|
|
INFO.Println("Logging to", file.Name())
|
2015-05-25 02:00:21 +00:00
|
|
|
|
|
|
|
LogHandle = file
|
|
|
|
initialize()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conveniently Creates a temporary file and sets the Log Handle to a io.writer created for it
|
|
|
|
func UseTempLogFile(prefix string) {
|
|
|
|
file, err := ioutil.TempFile(os.TempDir(), prefix)
|
|
|
|
if err != nil {
|
|
|
|
CRITICAL.Println(err)
|
|
|
|
}
|
|
|
|
|
2016-03-01 00:51:26 +00:00
|
|
|
INFO.Println("Logging to", file.Name())
|
2015-05-25 02:00:21 +00:00
|
|
|
|
|
|
|
LogHandle = file
|
|
|
|
initialize()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disables logging for the entire JWW system
|
|
|
|
func DiscardLogging() {
|
|
|
|
LogHandle = ioutil.Discard
|
|
|
|
initialize()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Feedback is special. It writes plainly to the output while
|
|
|
|
// logging with the standard extra information (date, file, etc)
|
|
|
|
// Only Println and Printf are currently provided for this
|
|
|
|
func (fb *Feedback) Println(v ...interface{}) {
|
2016-03-01 00:51:26 +00:00
|
|
|
s := fmt.Sprintln(v...)
|
|
|
|
fmt.Print(s)
|
|
|
|
LOG.Output(2, s)
|
2015-05-25 02:00:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Feedback is special. It writes plainly to the output while
|
|
|
|
// logging with the standard extra information (date, file, etc)
|
|
|
|
// Only Println and Printf are currently provided for this
|
|
|
|
func (fb *Feedback) Printf(format string, v ...interface{}) {
|
2016-03-01 00:51:26 +00:00
|
|
|
s := fmt.Sprintf(format, v...)
|
|
|
|
fmt.Print(s)
|
|
|
|
LOG.Output(2, s)
|
2015-05-25 02:00:21 +00:00
|
|
|
}
|