Initial commit

Signed-off-by: jolheiser <john.olheiser@gmail.com>
pull/1/head v1.0.0
jolheiser 2020-01-28 22:01:33 -06:00
commit 45decbb778
Signed by: jolheiser
GPG Key ID: B853ADA5DA7BBF7A
16 changed files with 999 additions and 0 deletions

2
.gitignore vendored 100644
View File

@ -0,0 +1,2 @@
# GoLand
.idea/

23
.golangci.yml 100644
View File

@ -0,0 +1,23 @@
linters:
enable:
- deadcode
- dogsled
- dupl
- errcheck
- funlen
- gocognit
- goconst
- gocritic
- gocyclo
- gofmt
- golint
- gosimple
- govet
- misspell
- prealloc
- staticcheck
- structcheck
- typecheck
- unparam
- unused
- varcheck

13
Makefile 100644
View File

@ -0,0 +1,13 @@
GO ?= go
.PHONY: lint
lint:
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
export BINARY="golangci-lint"; \
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.23.1; \
fi
golangci-lint run --timeout 5m
.PHONY: fmt
fmt:
$(GO) fmt ./...

51
README.md 100644
View File

@ -0,0 +1,51 @@
# beaver
Make short work of your logs
## Loggers
Beaver comes ready to log to `stdout` via shorthand `beaver.Info` etc. usage.
However, Beaver can also create new standalone loggers with `beaver.New`.
Beaver loggers can write to anything implementing the `io.Writer` interface.
## Options
| Option | Type | Effect |
|:------------:|:-------:|:-------------------------------------------------------:|
| TimePrefix | boolean | Prepends the date/time |
| StackPrefix | boolean | Prepends the calling file/line |
| StackLimit | integer | Amount of calling file to show, defaults to entire path |
| LevelPrefix | boolean | Prepends a logging level prefix, e.g. [T] for Trace |
| LevelColor | boolean | Colors the LevelPrefix if enabled |
| MessageColor | boolean | Colors the message itself |
The default Console configuration is below
Colored messages convey the logging level while reducing the amount of space used for CLI applications
| Option | Value |
|:------------:|:-----:|
| TimePrefix | false |
| StackPrefix | false |
| StackLimit | 0 |
| LevelPrefix | false |
| LevelColor | false |
| MessageColor | true |
## Colors
Beaver allows you to customize the colors of various parts of the message
| `color.` | Default Format |
|:--------:|----------------|
| Trace | Bold, FgCyan |
| Debug | Bold, FgBlue |
| Info | Bold, FgGreen |
| Warn | Bold, FgYellow |
| Error | Bold, FgRed |
| Fatal | Bold, BgRed |
| Default | None |
| Time | `Default` |
| Stack | `Default` |
```go
// Set Trace logging to Magenta instead of Bold-Cyan
color.Trace = color.New(color.FgMagenta)
```

View File

@ -0,0 +1,107 @@
package main
import (
"fmt"
"os"
"gitea.com/jolheiser/beaver"
"gitea.com/jolheiser/beaver/color"
)
func main() {
// Formatting
fmt.Println(color.New(color.Reset).Format("Reset"))
fmt.Println(color.New(color.Bold).Format("Bold"))
fmt.Println(color.New(color.Faint).Format("Faint"))
fmt.Println(color.New(color.Italic).Format("Italic"))
fmt.Println(color.New(color.Underline).Format("Underline"))
fmt.Println(color.New(color.BlinkSlow).Format("BlinkSlow"))
fmt.Println(color.New(color.BlinkRapid).Format("BlinkRapid"))
fmt.Println(color.New(color.ReverseVideo).Format("ReverseVideo"))
fmt.Println(color.New(color.Concealed).Format("Concealed"))
fmt.Println(color.New(color.CrossedOut).Format("CrossedOut"))
// Foreground
fmt.Println(color.New(color.FgBlack).Format("FgBlack"))
fmt.Println(color.New(color.FgRed).Format("FgRed"))
fmt.Println(color.New(color.FgGreen).Format("FgGreen"))
fmt.Println(color.New(color.FgYellow).Format("FgYellow"))
fmt.Println(color.New(color.FgBlue).Format("FgBlue"))
fmt.Println(color.New(color.FgMagenta).Format("FgMagenta"))
fmt.Println(color.New(color.FgCyan).Format("FgCyan"))
fmt.Println(color.New(color.FgWhite).Format("FgWhite"))
// Hi
fmt.Println(color.New(color.FgHiBlack).Format("FgHiBlack"))
fmt.Println(color.New(color.FgHiRed).Format("FgHiRed"))
fmt.Println(color.New(color.FgHiGreen).Format("FgHiGreen"))
fmt.Println(color.New(color.FgHiYellow).Format("FgHiYellow"))
fmt.Println(color.New(color.FgHiBlue).Format("FgHiBlue"))
fmt.Println(color.New(color.FgHiMagenta).Format("FgHiMagenta"))
fmt.Println(color.New(color.FgHiCyan).Format("FgHiCyan"))
fmt.Println(color.New(color.FgHiWhite).Format("FgHiWhite"))
// Background
fmt.Println(color.New(color.BgBlack).Format("BgBlack"))
fmt.Println(color.New(color.BgRed).Format("BgRed"))
fmt.Println(color.New(color.BgGreen).Format("BgGreen"))
fmt.Println(color.New(color.BgYellow).Format("BgYellow"))
fmt.Println(color.New(color.BgBlue).Format("BgBlue"))
fmt.Println(color.New(color.BgMagenta).Format("BgMagenta"))
fmt.Println(color.New(color.BgCyan).Format("BgCyan"))
fmt.Println(color.New(color.BgWhite).Format("BgWhite"))
// Hi
fmt.Println(color.New(color.BgHiBlack).Format("BgHiBlack"))
fmt.Println(color.New(color.BgHiRed).Format("BgHiRed"))
fmt.Println(color.New(color.BgHiGreen).Format("BgHiGreen"))
fmt.Println(color.New(color.BgHiYellow).Format("BgHiYellow"))
fmt.Println(color.New(color.BgHiBlue).Format("BgHiBlue"))
fmt.Println(color.New(color.BgHiMagenta).Format("BgHiMagenta"))
fmt.Println(color.New(color.BgHiCyan).Format("BgHiCyan"))
fmt.Println(color.New(color.BgHiWhite).Format("BgHiWhite"))
// Presets
beaver.Console.Level = beaver.TRACE
beaver.Console.Format.LevelPrefix = true
beaver.Console.Format.LevelColor = true
beaver.Log(beaver.TRACE, "Trace")
beaver.Log(beaver.DEBUG, "Debug")
beaver.Log(beaver.INFO, "Info")
beaver.Log(beaver.WARN, "Warn")
beaver.Log(beaver.ERROR, "Error")
beaver.Log(beaver.FATAL, "Fatal")
// Time Prefix
b := beaver.New(os.Stdout, beaver.INFO, beaver.FormatOptions{
TimePrefix: true,
})
b.Info("\t\tTime")
// Stack Prefix
b.Format = beaver.FormatOptions{
StackPrefix: true,
StackLimit: 25,
}
b.Info("Stack")
// All options, non-color
b.Format = beaver.FormatOptions{
TimePrefix: true,
StackPrefix: true,
LevelPrefix: true,
}
b.Info("Full options with no color")
// All options, Gitea colors
b.Format = beaver.FormatOptions{
TimePrefix: true,
StackPrefix: true,
StackLimit: 20,
LevelPrefix: true,
LevelColor: true,
MessageColor: false,
}
color.Time = color.New(color.FgCyan)
color.Stack = color.New(color.FgGreen)
b.Info("Full options emulating Gitea")
}

168
color/color.go 100644
View File

@ -0,0 +1,168 @@
package color
import (
"fmt"
"strconv"
"strings"
)
// Color defines a custom color object which is defined by SGR parameters.
type Color struct {
Attrs []Attribute
}
// Attribute defines a single SGR Code
type Attribute int
const escape = "\x1b"
// Logger attributes
const (
Reset Attribute = iota
Bold
Faint
Italic
Underline
BlinkSlow
BlinkRapid
ReverseVideo
Concealed
CrossedOut
)
// Foreground text colors
const (
FgBlack Attribute = iota + 30
FgRed
FgGreen
FgYellow
FgBlue
FgMagenta
FgCyan
FgWhite
)
// Foreground Hi-Intensity text colors
const (
FgHiBlack Attribute = iota + 90
FgHiRed
FgHiGreen
FgHiYellow
FgHiBlue
FgHiMagenta
FgHiCyan
FgHiWhite
)
// Background text colors
const (
BgBlack Attribute = iota + 40
BgRed
BgGreen
BgYellow
BgBlue
BgMagenta
BgCyan
BgWhite
)
// Background Hi-Intensity text colors
const (
BgHiBlack Attribute = iota + 100
BgHiRed
BgHiGreen
BgHiYellow
BgHiBlue
BgHiMagenta
BgHiCyan
BgHiWhite
)
// New returns a new Color with attrs Attributes
func New(attrs ...Attribute) *Color {
return &Color{Attrs: attrs}
}
// Add adds new Attributes to a Color
func (c *Color) Add(attrs ...Attribute) {
for _, attr := range attrs {
has := false
for _, att := range c.Attrs {
if attr == att {
has = true
break
}
}
if !has {
c.Attrs = append(c.Attrs, attr)
}
}
}
// String returns a string representation of the sum of a Color's Attributes
func (c *Color) String() string {
attrs := 0
for _, attr := range c.Attrs {
attrs += int(attr)
}
return fmt.Sprintf("%d", attrs)
}
func (c *Color) sequence() string {
format := make([]string, len(c.Attrs))
for i, v := range c.Attrs {
format[i] = strconv.Itoa(int(v))
}
return strings.Join(format, ";")
}
func (c *Color) wrap(s string) string {
return c.format() + s + c.unformat()
}
func (c *Color) format() string {
return fmt.Sprintf("%s[%sm", escape, c.sequence())
}
func (c *Color) unformat() string {
return fmt.Sprintf("%s[%dm", escape, Reset)
}
func (c *Color) Format(text string) string {
if len(c.Attrs) > 0 {
return c.wrap(text)
}
return text
}
// ParseLevel parses a string and returns a Beaver Level's Color, defaulting to Info
func ParseLevel(level string) *Color {
switch strings.ToUpper(level) {
case "T", "TRACE":
return Trace
case "D", "DEBUG":
return Debug
case "I", "INFO":
return Info
case "W", "WARN":
return Warn
case "E", "ERROR":
return Error
case "F", "FATAL":
return Fatal
}
return Info
}
var (
Trace = New(Bold, FgCyan)
Debug = New(Bold, FgBlue)
Info = New(Bold, FgGreen)
Warn = New(Bold, FgYellow)
Error = New(Bold, FgRed)
Fatal = New(Bold, BgRed)
Default = New()
Time = Default
Stack = Default
)

View File

@ -0,0 +1,77 @@
package color
import (
"os"
"testing"
)
var color = New()
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
func TestColor(t *testing.T) {
tt := []struct {
Name string
SetAttrs []Attribute
AddAttrs []Attribute
Size int
}{
{"Init", nil, nil, 0},
{"Set 2", []Attribute{Bold, FgBlack}, nil, 2},
{"Add 2", nil, []Attribute{Italic, BgWhite}, 4},
{"Set 3", []Attribute{Bold, FgBlack, BgWhite}, nil, 3},
{"Add 3", nil, []Attribute{Italic, FgWhite, BgBlack}, 6},
{"Add Same", nil, []Attribute{Italic, FgWhite, BgBlack}, 6},
{"Add 2 Same", nil, []Attribute{Italic, FgWhite, BgGreen}, 7},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
if tc.SetAttrs != nil {
color.Attrs = tc.SetAttrs
}
if tc.AddAttrs != nil {
color.Add(tc.AddAttrs...)
}
if len(color.Attrs) != tc.Size {
t.Logf("color has `%d` attributes, but should have `%d`", len(color.Attrs), tc.Size)
}
})
}
}
func TestParseLevel(t *testing.T) {
tt := []struct {
Parse string
Expected *Color
}{
{"T", Trace},
{"Trace", Trace},
{"D", Debug},
{"Debug", Debug},
{"I", Info},
{"Info", Info},
{"W", Warn},
{"Warn", Warn},
{"E", Error},
{"Error", Error},
{"F", Fatal},
{"Fatal", Fatal},
{"Unknown", Info},
{"N/A", Info},
{"1234", Info},
{"A Space", Info},
}
for _, tc := range tt {
t.Run(tc.Parse, func(t *testing.T) {
level := ParseLevel(tc.Parse)
if level != tc.Expected {
t.Logf("Expected `%s`, got `%s`", tc.Expected, level)
t.Fail()
}
})
}
}

88
console.go 100644
View File

@ -0,0 +1,88 @@
package beaver
import (
"os"
)
var (
Console = &Logger{
Writer: os.Stdout,
Level: INFO,
Format: FormatOptions{
MessageColor: true,
},
skip: 4,
}
)
// Low-Level Log
func Log(level Level, v ...interface{}) {
Console.Log(level, v...)
}
// Low-Level formatted Log
func Logf(level Level, format string, v ...interface{}) {
Console.Logf(level, format, v...)
}
// Trace logs at TRACE Level
func Trace(v ...interface{}) {
Console.Trace(v...)
}
// Tracef formats logs at TRACE Level
func Tracef(format string, v ...interface{}) {
Console.Tracef(format, v...)
}
// Debug logs at DEBUG Level
func Debug(v ...interface{}) {
Console.Debug(v...)
}
// Debugf formats logs at DEBUG Level
func Debugf(format string, v ...interface{}) {
Console.Debugf(format, v...)
}
// Info logs at INFO Level
func Info(v ...interface{}) {
Console.Info(v...)
}
// Infof formats logs at INFO Level
func Infof(format string, v ...interface{}) {
Console.Infof(format, v...)
}
// Warn logs at WARN Level
func Warn(v ...interface{}) {
Console.Warn(v...)
}
// Warnf formats logs at WARN Level
func Warnf(format string, v ...interface{}) {
Console.Warnf(format, v...)
}
// Error logs at ERROR Level
func Error(v ...interface{}) {
Console.Error(v...)
}
// Errorf formats logs at ERROR Level
func Errorf(format string, v ...interface{}) {
Console.Errorf(format, v...)
}
// Fatal logs at FATAL Level
func Fatal(v ...interface{}) {
Console.Fatal(v...)
os.Exit(1)
}
// Fatalf formats logs at FATAL Level
func Fatalf(format string, v ...interface{}) {
Console.Fatalf(format, v...)
os.Exit(1)
}

39
console_test.go 100644
View File

@ -0,0 +1,39 @@
package beaver
import (
"fmt"
"testing"
)
func TestConsole(t *testing.T) {
Console.Writer = testBuffer
tt := []struct {
Name string
Level Level
}{
{Name: "Trace Level", Level: TRACE},
{Name: "Debug Level", Level: DEBUG},
{Name: "Info Level", Level: INFO},
{Name: "Warn Level", Level: WARN},
{Name: "Error Level", Level: ERROR},
{Name: "Fatal Level", Level: FATAL},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
for _, lvl := range Levels() {
Console.Level = tc.Level
t.Run(fmt.Sprintf("%s Log", lvl), func(t *testing.T) {
Log(lvl, MESSAGE)
check(t, tc.Level, lvl)
})
t.Run(fmt.Sprintf("%s Logf", lvl), func(t *testing.T) {
Logf(lvl, "%s", MESSAGE)
check(t, tc.Level, lvl)
})
}
})
}
}

13
console_windows.go 100644
View File

@ -0,0 +1,13 @@
package beaver
import "golang.org/x/sys/windows"
func init() {
mode := uint32(0)
if err := windows.GetConsoleMode(windows.Stdout, &mode); err != nil {
return
}
mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
_ = windows.SetConsoleMode(windows.Stdout, mode)
}

5
go.mod 100644
View File

@ -0,0 +1,5 @@
module gitea.com/jolheiser/beaver
go 1.12
require golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a

2
go.sum 100644
View File

@ -0,0 +1,2 @@
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

142
level.go 100644
View File

@ -0,0 +1,142 @@
package beaver
import (
"fmt"
"strings"
"time"
"gitea.com/jolheiser/beaver/color"
)
var bold = color.New(color.Bold)
// Level defines a Beaver logging level
type Level int
const (
TRACE Level = iota
DEBUG
INFO
WARN
ERROR
FATAL
)
// String returns a human-friendly string
func (l Level) String() string {
switch l {
case TRACE:
return "Trace"
case DEBUG:
return "Debug"
case INFO:
return "Info"
case WARN:
return "Warn"
case ERROR:
return "Error"
case FATAL:
return "Fatal"
default:
return "N/A"
}
}
// Prefix returns a prefix for a logging level, optionally colored
func (l Level) Prefix() string {
var letter string
switch l {
case TRACE:
letter = "T"
case DEBUG:
letter = "D"
case INFO:
letter = "I"
case WARN:
letter = "W"
case ERROR:
letter = "E"
case FATAL:
letter = "F"
}
return fmt.Sprintf("[%s]", letter)
}
// Color returns a Level's color, defaulting to bold
func (l Level) Color() *color.Color {
switch l {
case TRACE:
return color.Trace
case DEBUG:
return color.Debug
case INFO:
return color.Info
case WARN:
return color.Warn
case ERROR:
return color.Error
case FATAL:
return color.Fatal
default:
return bold
}
}
// Levels returns a list of Beaver logging levels
func Levels() []Level {
return []Level{TRACE, DEBUG, INFO, WARN, ERROR, FATAL}
}
// ParseLevel parses a string and returns a Beaver Level, defaulting to Info
func ParseLevel(level string) Level {
switch strings.ToUpper(level) {
case "T", "TRACE":
return TRACE
case "D", "DEBUG":
return DEBUG
case "I", "INFO":
return INFO
case "W", "WARN":
return WARN
case "E", "ERROR":
return ERROR
case "F", "FATAL":
return FATAL
}
return INFO
}
func timePrefix(t time.Time) string {
var buf = &[]byte{}
year, month, day := t.Date()
itoa(buf, int(month), 2)
*buf = append(*buf, '/')
itoa(buf, day, 2)
*buf = append(*buf, '/')
itoa(buf, year, 4)
*buf = append(*buf, ' ')
hour, min, sec := t.Clock()
itoa(buf, hour, 2)
*buf = append(*buf, ':')
itoa(buf, min, 2)
*buf = append(*buf, ':')
itoa(buf, sec, 2)
return string(*buf)
}
func itoa(buf *[]byte, i int, wid int) {
var b [20]byte
bp := len(b) - 1
for i >= 10 || wid > 1 {
wid--
q := i / 10
b[bp] = byte('0' + i - q*10)
bp--
i = q
}
// i < 10
b[bp] = byte('0' + i)
*buf = append(*buf, b[bp:]...)
}

37
level_test.go 100644
View File

@ -0,0 +1,37 @@
package beaver
import "testing"
func TestParseLevel(t *testing.T) {
tt := []struct {
Parse string
Expected Level
}{
{"T", TRACE},
{"Trace", TRACE},
{"D", DEBUG},
{"Debug", DEBUG},
{"I", INFO},
{"Info", INFO},
{"W", WARN},
{"Warn", WARN},
{"E", ERROR},
{"Error", ERROR},
{"F", FATAL},
{"Fatal", FATAL},
{"Unknown", INFO},
{"N/A", INFO},
{"1234", INFO},
{"A Space", INFO},
}
for _, tc := range tt {
t.Run(tc.Parse, func(t *testing.T) {
level := ParseLevel(tc.Parse)
if level != tc.Expected {
t.Logf("Expected `%s`, got `%s`", tc.Expected, level)
t.Fail()
}
})
}
}

161
logger.go 100644
View File

@ -0,0 +1,161 @@
package beaver
import (
"fmt"
"io"
"os"
"runtime"
"strings"
"time"
"gitea.com/jolheiser/beaver/color"
)
// Logger is a base Beaver logger
type Logger struct {
Writer io.Writer
Level Level
Format FormatOptions
skip int
}
// FormatOptions defines formatting options for a logger
type FormatOptions struct {
TimePrefix bool
StackPrefix bool
StackLimit int
LevelPrefix bool
LevelColor bool
MessageColor bool
}
// New returns a new Beaver logger
func New(writer io.Writer, level Level, opts FormatOptions) *Logger {
return &Logger{
Writer: writer,
Level: level,
Format: opts,
skip: 3,
}
}
func (l *Logger) write(level Level, text string) {
if l.Level <= level {
_, file, line, _ := runtime.Caller(l.skip)
var message string
if l.Format.TimePrefix {
message += color.Time.Format(timePrefix(time.Now())) + " "
}
if l.Format.StackPrefix {
idx := len(file) - l.Format.StackLimit
if l.Format.StackLimit <= 0 {
idx = 0
}
if idx > 0 {
file = fmt.Sprintf("...%s", file[idx:])
}
message += color.Stack.Format(fmt.Sprintf("%s:%d", file, line)) + " "
}
if l.Format.LevelPrefix {
if l.Format.LevelColor {
message += level.Color().Format(level.Prefix())
} else {
message += level.Prefix()
}
message += " "
}
if l.Format.MessageColor {
message += level.Color().Format(text)
} else {
message += text
}
message += "\n"
_, _ = l.Writer.Write([]byte(message))
}
}
func (l *Logger) log(level Level, v ...interface{}) {
var args = make([]string, len(v))
for i := 0; i < len(v); i++ {
args[i] = fmt.Sprintf("%v", v[i])
}
text := strings.Join(args, " ")
l.write(level, text)
}
func (l *Logger) logf(level Level, format string, v ...interface{}) {
text := fmt.Sprintf(format, v...)
l.write(level, text)
}
// Low-Level Log
func (l *Logger) Log(level Level, v ...interface{}) {
l.log(level, v...)
}
// Low-Level formatted Log
func (l *Logger) Logf(level Level, format string, v ...interface{}) {
l.logf(level, format, v...)
}
// Trace logs at TRACE Level
func (l *Logger) Trace(v ...interface{}) {
l.log(TRACE, v...)
}
// Tracef formats logs at TRACE Level
func (l *Logger) Tracef(format string, v ...interface{}) {
l.logf(TRACE, format, v...)
}
// Debug logs at DEBUG Level
func (l *Logger) Debug(v ...interface{}) {
l.log(DEBUG, v...)
}
// Debugf formats logs at DEBUG Level
func (l *Logger) Debugf(format string, v ...interface{}) {
l.logf(DEBUG, format, v...)
}
// Info logs at INFO Level
func (l *Logger) Info(v ...interface{}) {
l.log(INFO, v...)
}
// Infof formats logs at INFO Level
func (l *Logger) Infof(format string, v ...interface{}) {
l.logf(INFO, format, v...)
}
// Warn logs at WARN Level
func (l *Logger) Warn(v ...interface{}) {
l.log(WARN, v...)
}
// Warnf formats logs at WARN Level
func (l *Logger) Warnf(format string, v ...interface{}) {
l.logf(WARN, format, v...)
}
// Error logs at ERROR Level
func (l *Logger) Error(v ...interface{}) {
l.log(ERROR, v...)
}
// Errorf formats logs at ERROR Level
func (l *Logger) Errorf(format string, v ...interface{}) {
l.logf(ERROR, format, v...)
}
// Fatal logs at FATAL Level
func (l *Logger) Fatal(v ...interface{}) {
l.log(FATAL, v...)
os.Exit(1)
}
// Fatalf formats logs at FATAL Level
func (l *Logger) Fatalf(format string, v ...interface{}) {
l.logf(FATAL, format, v...)
os.Exit(1)
}

71
logger_test.go 100644
View File

@ -0,0 +1,71 @@
package beaver
import (
"bytes"
"fmt"
"os"
"strings"
"testing"
)
var testBuffer = &bytes.Buffer{}
const MESSAGE = "Test"
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
func TestLogger(t *testing.T) {
log := New(testBuffer, TRACE, FormatOptions{
MessageColor: true,
})
tt := []struct {
Name string
Level Level
}{
{Name: "Trace Level", Level: TRACE},
{Name: "Debug Level", Level: DEBUG},
{Name: "Info Level", Level: INFO},
{Name: "Warn Level", Level: WARN},
{Name: "Error Level", Level: ERROR},
{Name: "Fatal Level", Level: FATAL},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
for _, lvl := range Levels() {
log.Level = tc.Level
t.Run(fmt.Sprintf("%s Log", lvl), func(t *testing.T) {
log.Log(lvl, MESSAGE)
check(t, tc.Level, lvl)
})
t.Run(fmt.Sprintf("%s Logf", lvl), func(t *testing.T) {
log.Logf(lvl, "%s", MESSAGE)
check(t, tc.Level, lvl)
})
}
})
}
}
func check(t *testing.T, current, test Level) {
f := flush()
m := test.Color().Format(MESSAGE)
if current <= test && f != m {
t.Logf("Expected `%s`, got `%s`", m, f)
t.Fail()
}
if current > test && f == m {
t.Logf("Expected no logging, got `%s`", f)
t.Fail()
}
}
func flush() string {
msg := strings.TrimSpace(testBuffer.String())
testBuffer.Reset()
return msg
}