Skip to content

Commit

Permalink
Merge pull request #14 from rhnvrm/fix-caller-codecov
Browse files Browse the repository at this point in the history
fix: more tests, improve codecov, broken caller
  • Loading branch information
mr-karan authored Jul 8, 2022
2 parents de8a6c9 + 7c2fb31 commit 3fd32be
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 45 deletions.
28 changes: 0 additions & 28 deletions buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,6 @@ func (bb *byteBuffer) AppendFloat(f float64, bitSize int) {
bb.B = strconv.AppendFloat(bb.B, f, 'f', -1, bitSize)
}

// Len returns the length of the underlying buffer.
func (bb *byteBuffer) Len() int {
return len(bb.B)
}

// Cap returns the capacity of the underlying buffer.
func (bb *byteBuffer) Cap() int {
return cap(bb.B)
}

// Bytes returns a mutable reference to the underlying buffer.
func (bb *byteBuffer) Bytes() []byte {
return bb.B
Expand All @@ -81,21 +71,3 @@ func (bb *byteBuffer) Bytes() []byte {
func (bb *byteBuffer) Reset() {
bb.B = bb.B[:0]
}

// Write implements io.Writer.
func (bb *byteBuffer) Write(bs []byte) (int, error) {
bb.B = append(bb.B, bs...)
return len(bs), nil
}

// WriteByte writes a single byte to the buffer
func (bb *byteBuffer) WriteByte(v byte) error {
bb.B = append(bb.B, v)
return nil
}

// WriteString writes a string to the buffer.
func (bb *byteBuffer) WriteString(s string) (int, error) {
bb.B = append(bb.B, s...)
return len(s), nil
}
16 changes: 14 additions & 2 deletions log.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const (
var (
hex = "0123456789abcdef"
bufPool byteBufferPool
exit = func() { os.Exit(1) }
)

type Opts struct {
Expand Down Expand Up @@ -82,6 +83,9 @@ func New(opts Opts) Logger {
if opts.Level == 0 {
opts.Level = InfoLevel
}
if opts.CallerSkipFrameCount == 0 {
opts.CallerSkipFrameCount = 3
}

return Logger{
out: newSyncWriter(opts.Writer),
Expand Down Expand Up @@ -156,7 +160,7 @@ func (l Logger) Error(msg string, fields ...any) {
// It aborts the current program with an exit code of 1.
func (l Logger) Fatal(msg string, fields ...any) {
l.handleLog(msg, FatalLevel, fields...)
os.Exit(1)
exit()
}

// handleLog emits the log after filtering log level
Expand Down Expand Up @@ -295,10 +299,16 @@ func writeToBuf(buf *byteBuffer, key string, val any, lvl Level, color, space bo
buf.AppendByte('=')

switch v := val.(type) {
case nil:
buf.AppendString("null")
case []byte:
escapeAndWriteString(buf, string(v))
case string:
escapeAndWriteString(buf, v)
case int:
buf.AppendInt(int64(v))
case int8:
buf.AppendInt(int64(v))
case int16:
buf.AppendInt(int64(v))
case int32:
Expand All @@ -309,6 +319,8 @@ func writeToBuf(buf *byteBuffer, key string, val any, lvl Level, color, space bo
buf.AppendFloat(float64(v), 32)
case float64:
buf.AppendFloat(float64(v), 64)
case bool:
buf.AppendBool(v)
case error:
escapeAndWriteString(buf, v.Error())
case fmt.Stringer:
Expand All @@ -325,7 +337,7 @@ func writeToBuf(buf *byteBuffer, key string, val any, lvl Level, color, space bo
// escapeAndWriteString escapes the string if any unwanted chars are there.
func escapeAndWriteString(buf *byteBuffer, s string) {
idx := strings.IndexFunc(s, checkEscapingRune)
if idx != -1 {
if idx != -1 || s == "null" {
writeQuotedString(buf, s)
return
}
Expand Down
164 changes: 149 additions & 15 deletions log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,29 @@ package logf
import (
"bytes"
"errors"
"log"
"strconv"
"sync"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestLogFormatWithEnableCaller(t *testing.T) {
buf := &bytes.Buffer{}
l := New(Opts{Writer: buf, EnableCaller: true})

l.Info("hello world")
require.Contains(t, buf.String(), `level=info message="hello world" caller=`)
require.Contains(t, buf.String(), `logf/log_test.go:18`)
buf.Reset()

lC := New(Opts{Writer: buf, EnableCaller: true, EnableColor: true})
lC.Info("hello world")
require.Contains(t, buf.String(), `logf/log_test.go:24`)
buf.Reset()
}

func TestLevelParsing(t *testing.T) {
cases := []struct {
String string
Expand All @@ -25,67 +41,185 @@ func TestLevelParsing(t *testing.T) {

for _, c := range cases {
t.Run(c.String, func(t *testing.T) {
assert.Equal(t, c.Lvl.String(), c.String, "level should be equal")
require.Equal(t, c.Lvl.String(), c.String, "level should be equal")
})
}

// Check for an invalid case.
t.Run("invalid", func(t *testing.T) {
var invalidLvl Level = 10
assert.Equal(t, invalidLvl.String(), "invalid lvl", "invalid level")
require.Equal(t, invalidLvl.String(), "invalid lvl", "invalid level")
})
}

func TestNewLoggerDefault(t *testing.T) {
l := New(Opts{})
assert.Equal(t, l.Opts.Level, InfoLevel, "level is info")
assert.Equal(t, l.Opts.EnableColor, false, "color output is disabled")
assert.Equal(t, l.Opts.EnableCaller, false, "caller is disabled")
assert.Equal(t, l.Opts.CallerSkipFrameCount, 0, "skip frame count is 0")
assert.Equal(t, l.Opts.TimestampFormat, defaultTSFormat, "timestamp format is default")
require.Equal(t, l.Opts.Level, InfoLevel, "level is info")
require.Equal(t, l.Opts.EnableColor, false, "color output is disabled")
require.Equal(t, l.Opts.EnableCaller, false, "caller is disabled")
require.Equal(t, l.Opts.CallerSkipFrameCount, 3, "skip frame count is 3")
require.Equal(t, l.Opts.TimestampFormat, defaultTSFormat, "timestamp format is default")
}

func TestNewSyncWriterWithNil(t *testing.T) {
w := newSyncWriter(nil)
require.NotNil(t, w.w, "writer should not be nil")
}

func TestLogFormat(t *testing.T) {
buf := &bytes.Buffer{}
l := New(Opts{Writer: buf})

l := New(Opts{Writer: buf, Level: DebugLevel})
// Debug log.
l.Debug("debug log")
require.Contains(t, buf.String(), `level=debug message="debug log"`)
buf.Reset()

l = New(Opts{Writer: buf})

// Debug log but with defualt level set to info.
l.Debug("debug log")
require.NotContains(t, buf.String(), `level=debug message="debug log"`)
buf.Reset()

// Info log.
l.Info("hello world")
assert.Contains(t, buf.String(), `level=info message="hello world"`, "info log")
require.Contains(t, buf.String(), `level=info message="hello world"`, "info log")
buf.Reset()

// Log with field.
l.Warn("testing fields", "stack", "testing")
assert.Contains(t, buf.String(), `level=warn message="testing fields" stack=testing`, "warning log")
require.Contains(t, buf.String(), `level=warn message="testing fields" stack=testing`, "warning log")
buf.Reset()

// Log with error.
fakeErr := errors.New("this is a fake error")
l.Error("testing error", "error", fakeErr)
assert.Contains(t, buf.String(), `level=error message="testing error" error="this is a fake error"`, "error log")
require.Contains(t, buf.String(), `level=error message="testing error" error="this is a fake error"`, "error log")
buf.Reset()

// Fatal log
var hadExit = false
exit = func() {
hadExit = true
}

l.Fatal("fatal log")
require.True(t, hadExit, "exit should have been called")
require.Contains(t, buf.String(), `level=fatal message="fatal log"`, "fatal log")
buf.Reset()
}

func TestLogFormatWithColor(t *testing.T) {
buf := &bytes.Buffer{}
l := New(Opts{Writer: buf, EnableColor: true})

// Info log.
l.Info("hello world")
require.Contains(t, buf.String(), "\x1b[36mlevel\x1b[0m=info \x1b[36mmessage\x1b[0m=\"hello world\" \n")
buf.Reset()
}

func TestLoggerTypes(t *testing.T) {
buf := &bytes.Buffer{}
l := New(Opts{Writer: buf, Level: DebugLevel})
type foo struct {
A int
}
l.Info("hello world",
"string", "foo",
"int", 1,
"int8", int8(1),
"int16", int16(1),
"int32", int32(1),
"int64", int64(1),
"float32", float32(1.0),
"float64", float64(1.0),
"struct", foo{A: 1},
"bool", true,
)

require.Contains(t, buf.String(), "level=info message=\"hello world\" string=foo int=1 int8=1 int16=1 int32=1 int64=1 float32=1 float64=1 struct={1} bool=true \n")
}

func TestLogFormatWithDefaultFields(t *testing.T) {
buf := &bytes.Buffer{}
l := New(Opts{Writer: buf, DefaultFields: []any{"defaultkey", "defaultvalue"}})

l.Info("hello world")
assert.Contains(t, buf.String(), `level=info message="hello world" defaultkey=defaultvalue`)
require.Contains(t, buf.String(), `level=info message="hello world" defaultkey=defaultvalue`)
buf.Reset()

l.Info("hello world", "component", "logf")
assert.Contains(t, buf.String(), `level=info message="hello world" defaultkey=defaultvalue component=logf`)
require.Contains(t, buf.String(), `level=info message="hello world" defaultkey=defaultvalue component=logf`)
buf.Reset()
}

type errWriter struct{}

func (w *errWriter) Write(p []byte) (int, error) {
return 0, errors.New("dummy error")
}

func TestIoWriterError(t *testing.T) {
w := &errWriter{}
l := New(Opts{Writer: w})
buf := &bytes.Buffer{}
log.SetOutput(buf)
log.SetFlags(0)
l.Info("hello world")
require.Contains(t, buf.String(), "error logging: dummy error\n")
}

func TestWriteQuotedStringCases(t *testing.T) {
buf := &bytes.Buffer{}
l := New(Opts{Writer: buf})

// cases from
// https://github.com/go-logfmt/logfmt/blob/99455b83edb21b32a1f1c0a32f5001b77487b721/encode_test.go
data := []struct {
key, value interface{}
want string
}{
{key: "k", value: "v", want: "k=v"},
{key: "k", value: nil, want: "k=null"},
{key: `\`, value: "v", want: `\=v`},
{key: "k", value: "", want: "k="},
{key: "k", value: "null", want: `k="null"`},
{key: "k", value: "<nil>", want: `k=<nil>`},
{key: "k", value: true, want: "k=true"},
{key: "k", value: 1, want: "k=1"},
{key: "k", value: 1.025, want: "k=1.025"},
{key: "k", value: 1e-3, want: "k=0.001"},
{key: "k", value: 3.5 + 2i, want: "k=(3.5+2i)"},
{key: "k", value: "v v", want: `k="v v"`},
{key: "k", value: " ", want: `k=" "`},
{key: "k", value: `"`, want: `k="\""`},
{key: "k", value: `=`, want: `k="="`},
{key: "k", value: `\`, want: `k=\`},
{key: "k", value: `=\`, want: `k="=\\"`},
{key: "k", value: `\"`, want: `k="\\\""`},
{key: "k", value: "\xbd", want: `k="\ufffd"`},
{key: "k", value: "\ufffd\x00", want: `k="\ufffd\u0000"`},
{key: "k", value: "\ufffd", want: `k="\ufffd"`},
{key: "k", value: []byte("\ufffd\x00"), want: `k="\ufffd\u0000"`},
{key: "k", value: []byte("\ufffd"), want: `k="\ufffd"`},
}

for _, d := range data {
l.Info("hello world", d.key, d.value)
require.Contains(t, buf.String(), d.want)
buf.Reset()
}
}

func TestOddNumberedFields(t *testing.T) {
buf := &bytes.Buffer{}
l := New(Opts{Writer: buf})

// Give a odd number of fields.
l.Info("hello world", "key1", "val1", "key2")
assert.Contains(t, buf.String(), `level=info message="hello world" key1=val1`)
require.Contains(t, buf.String(), `level=info message="hello world" key1=val1`)
buf.Reset()
}

Expand Down

0 comments on commit 3fd32be

Please sign in to comment.