Refactor so it can be used in non-global settings
diff --git a/README.md b/README.md index c6f327c..350a968 100644 --- a/README.md +++ b/README.md
@@ -44,7 +44,7 @@ * FATAL These each are loggers based on the log standard library and follow the -standard usage. Eg.. +standard usage. Eg. ```go import ( @@ -79,6 +79,13 @@ ``` +NOTE: You can also use the library in a non-global setting by creating an instance of a Notebook: + +```go +notepad = jww.NewNotepad(jww.LevelInfo, jww.LevelTrace, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime) +notepad.WARN.Println("Some warning"") +``` + _Why 7 levels?_ Maybe you think that 7 levels are too much for any application... and you @@ -118,35 +125,15 @@ Note that JWW's own internal output uses log levels as well, so set the log level before making any other calls if you want to see what it's up to. -### Using a temp log file - -JWW conveniently creates a temporary file and sets the log Handle to -a io.Writer created for it. You should call this early in your application -initialization routine as it will only log calls made after it is executed. -When this option is used, the library will fmt.Println where to find the -log file. - -```go - import ( - jww "github.com/spf13/jwalterweatherman" - ) - - jww.UseTempLogFile("YourAppName") - -``` ### Setting a log file -JWW can log to any file you provide a path to (provided it’s writable). -Will only append to this file. +JWW can log to any `io.Writer`: ```go - import ( - jww "github.com/spf13/jwalterweatherman" - ) - jww.SetLogFile("/path/to/logfile") + jww.SetLogOutput(customWriter) ```
diff --git a/default_notepad.go b/default_notepad.go new file mode 100644 index 0000000..ea8003d --- /dev/null +++ b/default_notepad.go
@@ -0,0 +1,103 @@ +// 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 ( + "io" + "io/ioutil" + "log" + "os" +) + +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 + + defaultNotepad *Notepad +) + +func reloadDefaultNotepad() { + TRACE = defaultNotepad.TRACE + DEBUG = defaultNotepad.DEBUG + INFO = defaultNotepad.INFO + WARN = defaultNotepad.WARN + ERROR = defaultNotepad.ERROR + CRITICAL = defaultNotepad.CRITICAL + FATAL = defaultNotepad.FATAL + + LOG = defaultNotepad.LOG + FEEDBACK = defaultNotepad.FEEDBACK +} + +func init() { + defaultNotepad = NewNotepad(LevelInfo, LevelTrace, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime) + reloadDefaultNotepad() +} + +// SetLogThreshold set the log threshold for the default notepad. Trace by default. +func SetLogThreshold(threshold Threshold) { + defaultNotepad.SetLogThreshold(threshold) + reloadDefaultNotepad() +} + +// SetLogOutput set the log output for the default notepad. Discarded by default. +func SetLogOutput(handle io.Writer) { + defaultNotepad.SetLogOutput(handle) + reloadDefaultNotepad() +} + +// SetStdoutThreshold set the standard output threshold for the default notepad. +// Info by default. +func SetStdoutThreshold(threshold Threshold) { + defaultNotepad.SetStdoutThreshold(threshold) + reloadDefaultNotepad() +} + +// SetPrefix set the prefix for the default logger. Empty by default. +func SetPrefix(prefix string) { + defaultNotepad.SetPrefix(prefix) + reloadDefaultNotepad() +} + +// SetFlags set the flags for the default logger. "log.Ldate | log.Ltime" by default. +func SetFlags(flags int) { + defaultNotepad.SetFlags(flags) + reloadDefaultNotepad() +} + +// Level returns the current global log threshold. +func LogThreshold() Threshold { + return defaultNotepad.logThreshold +} + +// Level returns the current global output threshold. +func StdoutThreshold() Threshold { + return defaultNotepad.stdoutThreshold +} + +// LogCountForLevel returns the number of log invocations for a given threshold. +func LogCountForLevel(l Threshold) uint64 { + return defaultNotepad.LogCountForLevel(l) +} + +// LogCountForLevelsGreaterThanorEqualTo returns the number of log invocations +// greater than or equal to a given threshold. +func LogCountForLevelsGreaterThanorEqualTo(threshold Threshold) uint64 { + return defaultNotepad.LogCountForLevelsGreaterThanorEqualTo(threshold) +} + +// ResetLogCounters resets the invocation counters for all levels. +func ResetLogCounters() { + defaultNotepad.ResetLogCounters() +}
diff --git a/default_notepad_test.go b/default_notepad_test.go new file mode 100644 index 0000000..2670c8d --- /dev/null +++ b/default_notepad_test.go
@@ -0,0 +1,102 @@ +// 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 ( + "bytes" + "io/ioutil" + "sync" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestThresholds(t *testing.T) { + SetStdoutThreshold(LevelError) + require.Equal(t, StdoutThreshold(), LevelError) + SetLogThreshold(LevelCritical) + require.Equal(t, LogThreshold(), LevelCritical) + require.NotEqual(t, StdoutThreshold(), LevelCritical) + SetStdoutThreshold(LevelWarn) + require.Equal(t, StdoutThreshold(), LevelWarn) +} + +func TestDefaultLogging(t *testing.T) { + var outputBuf, logBuf bytes.Buffer + + defaultNotepad.logHandle = &logBuf + defaultNotepad.outHandle = &outputBuf + + SetLogThreshold(LevelWarn) + SetStdoutThreshold(LevelError) + + FATAL.Println("fatal err") + CRITICAL.Println("critical err") + ERROR.Println("an error") + WARN.Println("a warning") + INFO.Println("information") + DEBUG.Println("debugging info") + TRACE.Println("trace") + + require.Contains(t, logBuf.String(), "fatal err") + require.Contains(t, logBuf.String(), "critical err") + require.Contains(t, logBuf.String(), "an error") + require.Contains(t, logBuf.String(), "a warning") + require.NotContains(t, logBuf.String(), "information") + require.NotContains(t, logBuf.String(), "debugging info") + require.NotContains(t, logBuf.String(), "trace") + + require.Contains(t, outputBuf.String(), "fatal err") + require.Contains(t, outputBuf.String(), "critical err") + require.Contains(t, outputBuf.String(), "an error") + require.NotContains(t, outputBuf.String(), "a warning") + require.NotContains(t, outputBuf.String(), "information") + require.NotContains(t, outputBuf.String(), "debugging info") + require.NotContains(t, outputBuf.String(), "trace") +} + +func TestLogCounter(t *testing.T) { + defaultNotepad.logHandle = ioutil.Discard + defaultNotepad.outHandle = ioutil.Discard + + SetLogThreshold(LevelTrace) + SetStdoutThreshold(LevelTrace) + + FATAL.Println("fatal err") + CRITICAL.Println("critical err") + WARN.Println("a warning") + WARN.Println("another warning") + INFO.Println("information") + DEBUG.Println("debugging info") + TRACE.Println("trace") + + wg := &sync.WaitGroup{} + + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < 10; j++ { + ERROR.Println("error", j) + // check for data races + require.True(t, LogCountForLevel(LevelError) > uint64(j)) + require.True(t, LogCountForLevelsGreaterThanorEqualTo(LevelError) > uint64(j)) + } + }() + + } + + wg.Wait() + + require.Equal(t, uint64(1), LogCountForLevel(LevelFatal)) + require.Equal(t, uint64(1), LogCountForLevel(LevelCritical)) + require.Equal(t, uint64(2), LogCountForLevel(LevelWarn)) + require.Equal(t, uint64(1), LogCountForLevel(LevelInfo)) + require.Equal(t, uint64(1), LogCountForLevel(LevelDebug)) + require.Equal(t, uint64(1), LogCountForLevel(LevelTrace)) + require.Equal(t, uint64(100), LogCountForLevel(LevelError)) + require.Equal(t, uint64(102), LogCountForLevelsGreaterThanorEqualTo(LevelError)) +}
diff --git a/jww_test.go b/jww_test.go deleted file mode 100644 index c405185..0000000 --- a/jww_test.go +++ /dev/null
@@ -1,96 +0,0 @@ -// 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 ( - "bytes" - "github.com/stretchr/testify/assert" - "sync" - "testing" -) - -func TestLevels(t *testing.T) { - SetStdoutThreshold(LevelError) - assert.Equal(t, StdoutThreshold(), LevelError) - SetLogThreshold(LevelCritical) - assert.Equal(t, LogThreshold(), LevelCritical) - assert.NotEqual(t, StdoutThreshold(), LevelCritical) - SetStdoutThreshold(LevelWarn) - assert.Equal(t, StdoutThreshold(), LevelWarn) -} - -func TestDefaultLogging(t *testing.T) { - outputBuf := new(bytes.Buffer) - logBuf := new(bytes.Buffer) - LogHandle = logBuf - OutHandle = outputBuf - - SetLogThreshold(LevelWarn) - SetStdoutThreshold(LevelError) - - FATAL.Println("fatal err") - CRITICAL.Println("critical err") - ERROR.Println("an error") - WARN.Println("a warning") - INFO.Println("information") - DEBUG.Println("debugging info") - TRACE.Println("trace") - - assert.Contains(t, logBuf.String(), "fatal err") - assert.Contains(t, logBuf.String(), "critical err") - assert.Contains(t, logBuf.String(), "an error") - assert.Contains(t, logBuf.String(), "a warning") - assert.NotContains(t, logBuf.String(), "information") - assert.NotContains(t, logBuf.String(), "debugging info") - assert.NotContains(t, logBuf.String(), "trace") - - assert.Contains(t, outputBuf.String(), "fatal err") - assert.Contains(t, outputBuf.String(), "critical err") - assert.Contains(t, outputBuf.String(), "an error") - assert.NotContains(t, outputBuf.String(), "a warning") - assert.NotContains(t, outputBuf.String(), "information") - assert.NotContains(t, outputBuf.String(), "debugging info") - assert.NotContains(t, outputBuf.String(), "trace") -} - -func TestLogCounter(t *testing.T) { - ResetLogCounters() - - FATAL.Println("fatal err") - CRITICAL.Println("critical err") - WARN.Println("a warning") - WARN.Println("another warning") - INFO.Println("information") - DEBUG.Println("debugging info") - TRACE.Println("trace") - - wg := &sync.WaitGroup{} - - for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < 10; j++ { - ERROR.Println("error", j) - // check for data races - assert.True(t, LogCountForLevel(LevelError) > uint64(j)) - assert.True(t, LogCountForLevelsGreaterThanorEqualTo(LevelError) > uint64(j)) - } - }() - - } - - wg.Wait() - - assert.Equal(t, uint64(1), LogCountForLevel(LevelFatal)) - assert.Equal(t, uint64(1), LogCountForLevel(LevelCritical)) - assert.Equal(t, uint64(2), LogCountForLevel(LevelWarn)) - assert.Equal(t, uint64(1), LogCountForLevel(LevelInfo)) - assert.Equal(t, uint64(1), LogCountForLevel(LevelDebug)) - assert.Equal(t, uint64(1), LogCountForLevel(LevelTrace)) - assert.Equal(t, uint64(100), LogCountForLevel(LevelError)) - assert.Equal(t, uint64(102), LogCountForLevelsGreaterThanorEqualTo(LevelError)) -}
diff --git a/log_counter.go b/log_counter.go new file mode 100644 index 0000000..cada5cc --- /dev/null +++ b/log_counter.go
@@ -0,0 +1,57 @@ +// 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 ( + "sync/atomic" +) + +type logCounter struct { + id int + counter uint64 +} + +func (c *logCounter) incr() { + atomic.AddUint64(&c.counter, 1) +} + +func (c *logCounter) resetCounter() { + atomic.StoreUint64(&c.counter, 0) +} + +func (c *logCounter) getCount() uint64 { + return atomic.LoadUint64(&c.counter) +} + +func (c *logCounter) Write(p []byte) (n int, err error) { + c.incr() + + return len(p), nil +} + +// LogCountForLevel returns the number of log invocations for a given threshold. +func (n *Notepad) LogCountForLevel(l Threshold) uint64 { + return n.logCounters[l].getCount() +} + +// LogCountForLevelsGreaterThanorEqualTo returns the number of log invocations +// greater than or equal to a given threshold. +func (n *Notepad) LogCountForLevelsGreaterThanorEqualTo(threshold Threshold) uint64 { + var cnt uint64 + + for i := int(threshold); i < len(n.logCounters); i++ { + cnt += n.LogCountForLevel(Threshold(i)) + } + + return cnt +} + +// ResetLogCounters resets the invocation counters for all levels. +func (n *Notepad) ResetLogCounters() { + for _, np := range n.logCounters { + np.resetCounter() + } +}
diff --git a/notepad.go b/notepad.go new file mode 100644 index 0000000..40b9620 --- /dev/null +++ b/notepad.go
@@ -0,0 +1,177 @@ +// 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" + "log" + "os" +) + +type Threshold int + +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 []**log.Logger + logHandle io.Writer + outHandle io.Writer + logThreshold Threshold + stdoutThreshold Threshold + prefix string + flags int + + // One per Threshold + logCounters [7]*logCounter +} + +// NewNotepad create a new notepad. +func NewNotepad(outThreshold Threshold, logThreshold Threshold, outHandle, logHandle io.Writer, prefix string, flags int) *Notepad { + n := &Notepad{} + + n.loggers = append(n.loggers, &n.TRACE, &n.DEBUG, &n.INFO, &n.WARN, &n.ERROR, &n.CRITICAL, &n.FATAL) + n.logHandle = logHandle + n.outHandle = outHandle + n.logThreshold = logThreshold + n.stdoutThreshold = outThreshold + + 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{n} + + n.init() + + return n +} + +// 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 { + *Notepad +} + +// init create the loggers for each level depending on the notepad thresholds +func (n *Notepad) init() { + bothHandle := io.MultiWriter(n.outHandle, n.logHandle) + + for t, logger := range n.loggers { + threshold := Threshold(t) + counter := &logCounter{id: t} + n.logCounters[t] = counter + + switch { + case threshold >= n.logThreshold && threshold >= n.stdoutThreshold: + *logger = log.New(io.MultiWriter(counter, bothHandle), n.prefix+prefixes[threshold], n.flags) + + case threshold >= n.logThreshold: + *logger = log.New(io.MultiWriter(counter, n.logHandle), n.prefix+prefixes[threshold], n.flags) + + case threshold >= n.stdoutThreshold: + *logger = log.New(io.MultiWriter(counter, os.Stdout), n.prefix+prefixes[threshold], n.flags) + + default: + *logger = log.New(counter, n.prefix+prefixes[threshold], n.flags) + } + } +} + +// SetLogThreshold change the threshold above which messages are written to the +// log file +func (n *Notepad) SetLogThreshold(threshold Threshold) { + n.logThreshold = threshold + n.init() +} + +// SetLogOutput change the file where log messages are written +func (n *Notepad) SetLogOutput(handle io.Writer) { + n.logHandle = handle + n.init() +} + +// SetStdoutThreshold change the threshold above which messages are written to the +// standard output +func (n *Notepad) SetStdoutThreshold(threshold Threshold) { + n.stdoutThreshold = threshold + n.init() +} + +// SetPrefix change the prefix used by the notepad. Prefixes are displayed between +// brackets at the begining 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 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{}) { + s := fmt.Sprintln(v...) + fmt.Print(s) + fb.LOG.Output(2, s) +} + +// 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{}) { + s := fmt.Sprintf(format, v...) + fmt.Print(s) + fb.LOG.Output(2, s) +}
diff --git a/notepad_test.go b/notepad_test.go new file mode 100644 index 0000000..7f6739b --- /dev/null +++ b/notepad_test.go
@@ -0,0 +1,33 @@ +// 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 ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNotepad(t *testing.T) { + + var logHandle, outHandle bytes.Buffer + + n := NewNotepad(LevelCritical, LevelError, &outHandle, &logHandle, "TestNotePad", 0) + + n.DEBUG.Println("Some debug") + n.ERROR.Println("Some error") + n.CRITICAL.Println("Some critical error") + + require.Contains(t, logHandle.String(), "[TestNotePad] ERROR Some error") + require.NotContains(t, logHandle.String(), "Some debug") + require.NotContains(t, outHandle.String(), "Some error") + require.Contains(t, outHandle.String(), "Some critical error") + + require.Equal(t, n.LogCountForLevel(LevelError), uint64(1)) + require.Equal(t, n.LogCountForLevel(LevelDebug), uint64(1)) + require.Equal(t, n.LogCountForLevel(LevelTrace), uint64(0)) +}
diff --git a/thatswhyyoualwaysleaveanote.go b/thatswhyyoualwaysleaveanote.go deleted file mode 100644 index b64ed46..0000000 --- a/thatswhyyoualwaysleaveanote.go +++ /dev/null
@@ -1,256 +0,0 @@ -// 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" - "os" - "sync/atomic" -) - -// 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 - counter uint64 -} - -func (n *NotePad) incr() { - atomic.AddUint64(&n.counter, 1) -} - -func (n *NotePad) resetCounter() { - atomic.StoreUint64(&n.counter, 0) -} - -func (n *NotePad) getCount() uint64 { - return atomic.LoadUint64(&n.counter) -} - -type countingWriter struct { - incrFunc func() -} - -func (cw *countingWriter) Write(p []byte) (n int, err error) { - cw.incrFunc() - - return 0, nil -} - -// 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 -) - -const ( - DATE = log.Ldate - TIME = log.Ltime - SFILE = log.Lshortfile - LFILE = log.Llongfile - MSEC = log.Lmicroseconds -) - -var logFlags = DATE | TIME | SFILE - -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 { - n.Handle = io.MultiWriter(n.Handle, &countingWriter{n.incr}) - *n.Logger = log.New(n.Handle, n.Prefix, logFlags) - } - - LOG = log.New(LogHandle, - "LOG: ", - logFlags) -} - -// Set the log Flags (Available flag: DATE, TIME, SFILE, LFILE and MSEC) -func SetLogFlag(flags int) { - logFlags = flags - initialize() -} - -// 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) - } - - INFO.Println("Logging to", file.Name()) - - 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) - } - - INFO.Println("Logging to", file.Name()) - - LogHandle = file - initialize() -} - -// LogCountForLevel returns the number of log invocations for a given level. -func LogCountForLevel(l Level) uint64 { - for _, np := range NotePads { - if np.Level == l { - return np.getCount() - } - } - return 0 -} - -// LogCountForLevelsGreaterThanorEqualTo returns the number of log invocations -// greater than or equal to a given level threshold. -func LogCountForLevelsGreaterThanorEqualTo(threshold Level) uint64 { - var cnt uint64 - for _, np := range NotePads { - if np.Level >= threshold { - cnt += np.getCount() - } - } - return cnt -} - -// ResetLogCounters resets the invocation counters for all levels. -func ResetLogCounters() { - for _, np := range NotePads { - np.resetCounter() - } -} - -// 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{}) { - s := fmt.Sprintln(v...) - fmt.Print(s) - LOG.Output(2, s) -} - -// 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{}) { - s := fmt.Sprintf(format, v...) - fmt.Print(s) - LOG.Output(2, s) -}