Skip to content

Commit

Permalink
Merged internal logger from v0.6.0. (#161)
Browse files Browse the repository at this point in the history
  • Loading branch information
JivanAmara committed Dec 13, 2017
1 parent 3df3417 commit 81fc214
Show file tree
Hide file tree
Showing 4 changed files with 374 additions and 0 deletions.
58 changes: 58 additions & 0 deletions internal/log/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Logger Proxy API
================

## Consts (in decreasing order of severity):
* FATAL
* ERROR
* WARN
* INFO
* DEBUG
* TRACE

## Functions:
* log.SetLogLevel(<const from above>):
Log messages below this level of severity will be ignored.
Default Log Level is INFO.

* log.Fatal(string, vals... interface{}):
prevents the program from continuing
i.e. can't allocate additional memory

* log.Error(string, vals... interface{}):
non-fatal, but prevents valid execution
i.e. can't connect to a database, complete a function call, open file, invalid format

* log.Warn(string, vals... interface{}):
looks unusual, but does not clearly prevent execution

* log.Info(string, vals... interface{}):
Least severe message that a sysadmin would be interested in
i.e. server request logs

* log.Debug(string, vals... interface{}):
high level info for a developer. more than what a sysadmin would typically want

* log.Trace(string, vals... interface{}):
excruciating detail.

* log.SetOutput(io.Writer):
Indicates the location for log messages to be written.
Default is stdout.

## Flags:

These package-level flags are provided to disable expensive code when the code is only needed at
a lower severity than the logger is set at:
IsError
IsWarn
IsInfo
IsDebug
IsTrace

example usage:
if log.IsDebug {
...
}

## Output will look like:
"timestamp•LOG_LEVEL•filename.go•linenumber•output"
121 changes: 121 additions & 0 deletions internal/log/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package log

import (
"io"
"sync"
)

type Interface interface {
// These all take args the same as calls to fmt.Printf()
Fatal(string, ...interface{})
Error(string, ...interface{})
Warn(string, ...interface{})
Info(string, ...interface{})
Debug(string, ...interface{})
Trace(string, ...interface{})
SetOutput(io.Writer)
}

func init() {
logger = standard
SetLogLevel(INFO)
}

type Level int

// Allows the ordering of severity to be checked
const (
TRACE = Level(iota - 2)
DEBUG
INFO
WARN
ERROR
FATAL
)

var (
logger Interface
level Level
lock sync.Mutex
// FATAL level is never disabled
IsError bool
IsWarn bool
IsInfo bool
IsDebug bool
IsTrace bool
)

func SetOutput(w io.Writer) {
logger.SetOutput(w)
}

func SetLogLevel(lvl Level) {
lock.Lock()
IsTrace = false
IsDebug = false
IsInfo = false
IsWarn = false
IsError = false
IsTrace = false
if lvl != TRACE && lvl != DEBUG && lvl != INFO && lvl != WARN && lvl != ERROR && lvl != FATAL {
lvl = INFO
}
level = lvl
switch level {
case TRACE:
IsTrace = true
fallthrough
case DEBUG:
IsDebug = true
fallthrough
case INFO:
IsInfo = true
fallthrough
case WARN:
IsWarn = true
fallthrough
case ERROR:
IsError = true
}
lock.Unlock()
}

// Output format should be: "timestamp•LOG_LEVEL•filename.go•linenumber•output"
func Fatal(format string, args ...interface{}) {
logger.Fatal(format, args...)
}

func Error(format string, args ...interface{}) {
if !IsError {
return
}
logger.Error(format, args...)
}

func Warn(format string, args ...interface{}) {
if !IsWarn {
return
}
logger.Warn(format, args...)
}

func Info(format string, args ...interface{}) {
if !IsInfo {
return
}
logger.Info(format, args...)
}

func Debug(format string, args ...interface{}) {
if !IsDebug {
return
}
logger.Debug(format, args...)
}

func Trace(format string, args ...interface{}) {
if !IsTrace {
return
}
logger.Trace(format, args...)
}
146 changes: 146 additions & 0 deletions internal/log/log_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package log

import (
"bytes"
"fmt"
"regexp"
"testing"
)

func TestSetLevel(t *testing.T) {
// Check default level is INFO & Is* flags are set appropriately
if level != INFO || !IsError || !IsWarn || !IsInfo || IsDebug || IsTrace {
fmt.Println("Default level is not set as expected")
t.Fail()
}

// Check TRACE
SetLogLevel(TRACE)
if level != TRACE || !IsError || !IsWarn || !IsInfo || !IsDebug || !IsTrace {
fmt.Println("TRACE level is not set as expected")
t.Fail()
}

// Check DEBUG
SetLogLevel(DEBUG)
if level != DEBUG || !IsError || !IsWarn || !IsInfo || !IsDebug || IsTrace {
fmt.Println("DEBUG level is not set as expected")
t.Fail()
}

// Check INFO
SetLogLevel(INFO)
if level != INFO || !IsError || !IsWarn || !IsInfo || IsDebug || IsTrace {
fmt.Println("INFO level is not set as expected")
t.Fail()
}

// Check WARN
SetLogLevel(WARN)
if level != WARN || !IsError || !IsWarn || IsInfo || IsDebug || IsTrace {
fmt.Println("WARN level is not set as expected")
t.Fail()
}

// Check ERROR
SetLogLevel(ERROR)
if level != ERROR || !IsError || IsWarn || IsInfo || IsDebug || IsTrace {
fmt.Println("ERROR level is not set as expected")
t.Fail()
}

// Check FATAL
SetLogLevel(FATAL)
if level != FATAL || IsError || IsWarn || IsInfo || IsDebug || IsTrace {
fmt.Println("FATAL level is not set as expected")
t.Fail()
}
}

func TestLogging(t *testing.T) {
type TestCase struct {
loggerLevel Level
msgLevel Level
msg string
msgArgs []interface{}
expected string // regex pattern
}

var testCases []TestCase = []TestCase{
// These test cases use ".*" to avoid specifics of timestamp, file location, and line number.
{
loggerLevel: INFO,
msgLevel: INFO,
msg: "Hello",
expected: ".*•INFO•.*log_test.go•.*•Hello",
},
// Logging with logger's level set higher than message should result in no output.
{
loggerLevel: WARN,
msgLevel: INFO,
msg: "Hello",
expected: "",
},
{
loggerLevel: TRACE,
msgLevel: TRACE,
msg: "Hello",
expected: ".*•TRACE•.*log_test.go•.*•Hello",
},
{
loggerLevel: TRACE,
msgLevel: DEBUG,
msg: "Hello",
expected: ".*•DEBUG•.*log_test.go•.*•Hello",
},
{
loggerLevel: TRACE,
msgLevel: INFO,
msg: "Hello",
expected: ".*•INFO•.*log_test.go•.*•Hello",
},
{
loggerLevel: TRACE,
msgLevel: WARN,
msg: "Hello",
expected: ".*•WARN•.*log_test.go•.*•Hello",
},
{
loggerLevel: TRACE,
msgLevel: ERROR,
msg: "Hello",
expected: ".*•ERROR•.*log_test.go•.*•Hello",
},
// Check use of formatting args.
{
loggerLevel: TRACE,
msgLevel: ERROR,
msg: "Hello #%v %v",
msgArgs: []interface{}{1, "Joe"},
expected: ".*•ERROR•.*log_test.go•.*•Hello",
},
}

loggerCalls := map[Level]func(string, ...interface{}){
FATAL: Fatal,
ERROR: Error,
WARN: Warn,
INFO: Info,
DEBUG: Debug,
TRACE: Trace,
}

for i, tc := range testCases {
testOut := bytes.NewBufferString("")
SetOutput(testOut)
SetLogLevel(tc.loggerLevel)
loggerCalls[tc.msgLevel](tc.msg, tc.msgArgs...)

resultMsg := testOut.String()
matched, err := regexp.MatchString(tc.expected, resultMsg)
if err != nil || !matched {
fmt.Printf("TestCase[%v] failed, \n'%v'\ndoesn't match\n'%v'\n", i, tc.expected, resultMsg)
t.Fail()
}
}
}
49 changes: 49 additions & 0 deletions internal/log/standard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package log

import (
"fmt"
"io"
goLog "log"
"os"
"runtime"
)

var standard Standard

type Standard struct{}

func (_ Standard) SetOutput(w io.Writer) {
goLog.SetOutput(w)
}

func Output(level string, format string, args ...interface{}) {
logMsg := fmt.Sprintf(format, args...)
_, file, line, _ := runtime.Caller(4)
// timestamp will be provided by goLog
goLog.Printf("•%v•%v•%v•%v", level, file, line, logMsg)
}

func (_ Standard) Fatal(format string, args ...interface{}) {
Output("FATAL", format, args...)
os.Exit(1)
}

func (_ Standard) Error(format string, args ...interface{}) {
Output("ERROR", format, args...)
}

func (_ Standard) Warn(format string, args ...interface{}) {
Output("WARN", format, args...)
}

func (_ Standard) Info(format string, args ...interface{}) {
Output("INFO", format, args...)
}

func (_ Standard) Debug(format string, args ...interface{}) {
Output("DEBUG", format, args...)
}

func (_ Standard) Trace(format string, args ...interface{}) {
Output("TRACE", format, args...)
}

0 comments on commit 81fc214

Please sign in to comment.