TUN-3194: Don't render log output when level is not enabled

This commit is contained in:
Igor Postelnik 2020-07-23 18:36:31 -05:00
parent cf1c9a3083
commit 4791ba3b87
6 changed files with 74 additions and 43 deletions

View File

@ -8,17 +8,17 @@ import (
"github.com/acmacalister/skittles" "github.com/acmacalister/skittles"
) )
// Level of logging // Level of logging, lower number means more verbose logging, higher more terse
type Level int type Level int
const ( const (
// InfoLevel is for standard log messages
InfoLevel Level = iota
// DebugLevel is for messages that are intended for purposes debugging only // DebugLevel is for messages that are intended for purposes debugging only
DebugLevel DebugLevel Level = iota
// ErrorLevel is for error message to indicte something has gone wrong // InfoLevel is for standard log messages
InfoLevel
// ErrorLevel is for error message to indicate something has gone wrong
ErrorLevel ErrorLevel
// FatalLevel is for error message that log and kill the program with an os.exit(1) // FatalLevel is for error message that log and kill the program with an os.exit(1)

View File

@ -7,7 +7,7 @@ import "sync"
var SharedWriteManager = NewWriteManager() var SharedWriteManager = NewWriteManager()
type writeData struct { type writeData struct {
writeFunc func([]byte) target LogOutput
data []byte data []byte
} }
@ -31,9 +31,9 @@ func NewWriteManager() OutputManager {
} }
// Append adds a message to the writer runloop // Append adds a message to the writer runloop
func (m *WriteManager) Append(data []byte, callback func([]byte)) { func (m *WriteManager) Append(data []byte, target LogOutput) {
m.wg.Add(1) m.wg.Add(1)
m.writeChan <- writeData{data: data, writeFunc: callback} m.writeChan <- writeData{data: data, target: target}
} }
// Shutdown stops the sync manager service // Shutdown stops the sync manager service
@ -49,7 +49,7 @@ func (m *WriteManager) run() {
select { select {
case event, ok := <-m.writeChan: case event, ok := <-m.writeChan:
if ok { if ok {
event.writeFunc(event.data) event.target.WriteLogLine(event.data)
m.wg.Done() m.wg.Done()
} }
case <-m.shutdown: case <-m.shutdown:

View File

@ -6,13 +6,19 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
type outputFunc func(b []byte)
func (f outputFunc) WriteLogLine(data []byte) {
f(data)
}
func TestWriteManger(t *testing.T) { func TestWriteManger(t *testing.T) {
testData := []byte(string("hello Austin, how are you doing?")) testData := []byte(string("hello Austin, how are you doing?"))
waitChan := make(chan []byte) waitChan := make(chan []byte)
m := NewWriteManager() m := NewWriteManager()
m.Append(testData, func(b []byte) { m.Append(testData, outputFunc(func(b []byte) {
waitChan <- b waitChan <- b
}) }))
resp := <-waitChan resp := <-waitChan
assert.Equal(t, testData, resp) assert.Equal(t, testData, resp)
} }

View File

@ -10,7 +10,7 @@ func NewMockWriteManager() OutputManager {
} }
// Append is a mock stub // Append is a mock stub
func (m *MockWriteManager) Append(data []byte, callback func([]byte)) { func (m *MockWriteManager) Append(data []byte, target LogOutput) {
} }
// Shutdown is a mock stub // Shutdown is a mock stub

View File

@ -10,9 +10,13 @@ import (
// provided for testing // provided for testing
var osExit = os.Exit var osExit = os.Exit
type LogOutput interface {
WriteLogLine([]byte)
}
// OutputManager is used to sync data of Output // OutputManager is used to sync data of Output
type OutputManager interface { type OutputManager interface {
Append([]byte, func([]byte)) Append([]byte, LogOutput)
Shutdown() Shutdown()
} }
@ -35,39 +39,67 @@ type sourceGroup struct {
levelsSupported []Level levelsSupported []Level
} }
func (s *sourceGroup) WriteLogLine(data []byte) {
_, _ = s.writer.Write(data)
}
func (s *sourceGroup) supportsLevel(l Level) bool {
for _, level := range s.levelsSupported {
if l == level {
return true
}
}
return false
}
// OutputWriter is the standard logging implementation // OutputWriter is the standard logging implementation
type OutputWriter struct { type OutputWriter struct {
groups []sourceGroup groups []*sourceGroup
syncWriter OutputManager syncWriter OutputManager
minLevel Level
} }
// NewOutputWriter create a new logger // NewOutputWriter create a new logger
func NewOutputWriter(syncWriter OutputManager) *OutputWriter { func NewOutputWriter(syncWriter OutputManager) *OutputWriter {
return &OutputWriter{ return &OutputWriter{
syncWriter: syncWriter, syncWriter: syncWriter,
groups: make([]sourceGroup, 0), groups: nil,
minLevel: FatalLevel,
} }
} }
// Add a writer and formatter to output to // Add a writer and formatter to output to
func (s *OutputWriter) Add(writer io.Writer, formatter Formatter, levels ...Level) { func (s *OutputWriter) Add(writer io.Writer, formatter Formatter, levels ...Level) {
s.groups = append(s.groups, sourceGroup{writer: writer, formatter: formatter, levelsSupported: levels}) s.groups = append(s.groups, &sourceGroup{writer: writer, formatter: formatter, levelsSupported: levels})
// track most verbose (lowest) level we need to output
for _, level := range levels {
if level < s.minLevel {
s.minLevel = level
}
}
} }
// Error writes an error to the logging sources // Error writes an error to the logging sources
func (s *OutputWriter) Error(message string) { func (s *OutputWriter) Error(message string) {
if s.minLevel <= ErrorLevel {
s.output(ErrorLevel, message) s.output(ErrorLevel, message)
} }
}
// Info writes an info string to the logging sources // Info writes an info string to the logging sources
func (s *OutputWriter) Info(message string) { func (s *OutputWriter) Info(message string) {
if s.minLevel <= InfoLevel {
s.output(InfoLevel, message) s.output(InfoLevel, message)
} }
}
// Debug writes a debug string to the logging sources // Debug writes a debug string to the logging sources
func (s *OutputWriter) Debug(message string) { func (s *OutputWriter) Debug(message string) {
if s.minLevel <= DebugLevel {
s.output(DebugLevel, message) s.output(DebugLevel, message)
} }
}
// Fatal writes a error string to the logging sources and runs does an os.exit() // Fatal writes a error string to the logging sources and runs does an os.exit()
func (s *OutputWriter) Fatal(message string) { func (s *OutputWriter) Fatal(message string) {
@ -78,18 +110,24 @@ func (s *OutputWriter) Fatal(message string) {
// Errorf writes a formatted error to the logging sources // Errorf writes a formatted error to the logging sources
func (s *OutputWriter) Errorf(format string, args ...interface{}) { func (s *OutputWriter) Errorf(format string, args ...interface{}) {
if s.minLevel <= ErrorLevel {
s.output(ErrorLevel, fmt.Sprintf(format, args...)) s.output(ErrorLevel, fmt.Sprintf(format, args...))
} }
}
// Infof writes a formatted info statement to the logging sources // Infof writes a formatted info statement to the logging sources
func (s *OutputWriter) Infof(format string, args ...interface{}) { func (s *OutputWriter) Infof(format string, args ...interface{}) {
if s.minLevel <= InfoLevel {
s.output(InfoLevel, fmt.Sprintf(format, args...)) s.output(InfoLevel, fmt.Sprintf(format, args...))
} }
}
// Debugf writes a formatted debug statement to the logging sources // Debugf writes a formatted debug statement to the logging sources
func (s *OutputWriter) Debugf(format string, args ...interface{}) { func (s *OutputWriter) Debugf(format string, args ...interface{}) {
if s.minLevel <= DebugLevel {
s.output(DebugLevel, fmt.Sprintf(format, args...)) s.output(DebugLevel, fmt.Sprintf(format, args...))
} }
}
// Fatalf writes a writes a formatted error statement and runs does an os.exit() // Fatalf writes a writes a formatted error statement and runs does an os.exit()
func (s *OutputWriter) Fatalf(format string, args ...interface{}) { func (s *OutputWriter) Fatalf(format string, args ...interface{}) {
@ -100,31 +138,16 @@ func (s *OutputWriter) Fatalf(format string, args ...interface{}) {
// output does the actual write to the sync manager // output does the actual write to the sync manager
func (s *OutputWriter) output(l Level, content string) { func (s *OutputWriter) output(l Level, content string) {
now := time.Now()
for _, group := range s.groups { for _, group := range s.groups {
if isSupported(group, l) { if group.supportsLevel(l) {
logLine := fmt.Sprintf("%s%s\n", group.formatter.Timestamp(l, time.Now()), logLine := fmt.Sprintf("%s%s\n", group.formatter.Timestamp(l, now),
group.formatter.Content(l, content)) group.formatter.Content(l, content))
s.append(group, []byte(logLine)) s.syncWriter.Append([]byte(logLine), group)
} }
} }
} }
func (s *OutputWriter) append(group sourceGroup, logLine []byte) {
s.syncWriter.Append(logLine, func(b []byte) {
group.writer.Write(b)
})
}
// isSupported checks if the log level is supported
func isSupported(group sourceGroup, l Level) bool {
for _, level := range group.levelsSupported {
if l == level {
return true
}
}
return false
}
// Write implements io.Writer to support SetOutput of the log package // Write implements io.Writer to support SetOutput of the log package
func (s *OutputWriter) Write(p []byte) (n int, err error) { func (s *OutputWriter) Write(p []byte) (n int, err error) {
s.Info(string(p)) s.Info(string(p))

View File

@ -55,6 +55,8 @@ func TestOutputWrite(t *testing.T) {
logger := NewOutputWriter(m) logger := NewOutputWriter(m)
logger.Add(&testBuffer, f, InfoLevel) logger.Add(&testBuffer, f, InfoLevel)
logger.Debugf("debug message not logged here")
testData := "hello Bob Bork, how are you doing?" testData := "hello Bob Bork, how are you doing?"
logger.Info(testData) logger.Info(testData)
testTime := f.Timestamp(InfoLevel, time.Now()) testTime := f.Timestamp(InfoLevel, time.Now())