Add utility functions WithLevel and Fields

Add some utility functions to ease migration from other logger API.
This commit is contained in:
Olivier Poitrey 2017-07-10 02:56:44 -07:00
parent c964fc4812
commit 7af653895b
6 changed files with 162 additions and 19 deletions

View File

@ -12,6 +12,12 @@ func (c Context) Logger() Logger {
return c.l return c.l
} }
// Fields is a helper function to use a map to set fields using type assertion.
func (c Context) Fields(fields map[string]interface{}) Context {
c.l.context = appendFields(c.l.context, fields)
return c
}
// Dict adds the field key with the dict to the logger context. // Dict adds the field key with the dict to the logger context.
func (c Context) Dict(key string, dict *Event) Context { func (c Context) Dict(key string, dict *Event) Context {
dict.buf = append(dict.buf, '}') dict.buf = append(dict.buf, '}')

View File

@ -94,6 +94,15 @@ func (e *Event) Msgf(format string, v ...interface{}) {
} }
} }
// Fields is a helper function to use a map to set fields using type assertion.
func (e *Event) Fields(fields map[string]interface{}) *Event {
if !e.enabled {
return e
}
e.buf = appendFields(e.buf, fields)
return e
}
// Dict adds the field key with a dict to the event context. // Dict adds the field key with a dict to the event context.
// Use zerolog.Dict() to create the dictionary. // Use zerolog.Dict() to create the dictionary.
func (e *Event) Dict(key string, dict *Event) *Event { func (e *Event) Dict(key string, dict *Event) *Event {

View File

@ -3,10 +3,64 @@ package zerolog
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"sort"
"strconv" "strconv"
"time" "time"
) )
func appendFields(dst []byte, fields map[string]interface{}) []byte {
keys := make([]string, 0, len(fields))
for key, _ := range fields {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
switch val := fields[key].(type) {
case string:
dst = appendString(dst, key, val)
case []byte:
dst = appendBytes(dst, key, val)
case error:
dst = appendErrorKey(dst, key, val)
case bool:
dst = appendBool(dst, key, val)
case int:
dst = appendInt(dst, key, val)
case int8:
dst = appendInt8(dst, key, val)
case int16:
dst = appendInt16(dst, key, val)
case int32:
dst = appendInt32(dst, key, val)
case int64:
dst = appendInt64(dst, key, val)
case uint:
dst = appendUint(dst, key, val)
case uint8:
dst = appendUint8(dst, key, val)
case uint16:
dst = appendUint16(dst, key, val)
case uint32:
dst = appendUint32(dst, key, val)
case uint64:
dst = appendUint64(dst, key, val)
case float32:
dst = appendFloat32(dst, key, val)
case float64:
dst = appendFloat64(dst, key, val)
case time.Time:
dst = appendTime(dst, key, val)
case time.Duration:
dst = appendDuration(dst, key, val)
case nil:
dst = append(appendKey(dst, key), "null"...)
default:
dst = appendInterface(dst, key, val)
}
}
return dst
}
func appendKey(dst []byte, key string) []byte { func appendKey(dst []byte, key string) []byte {
if len(dst) > 1 { if len(dst) > 1 {
dst = append(dst, ',') dst = append(dst, ',')

25
log.go
View File

@ -71,6 +71,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"strconv"
"sync/atomic" "sync/atomic"
) )
@ -246,6 +247,30 @@ func (l Logger) Panic() *Event {
return l.newEvent(PanicLevel, true, func(msg string) { panic(msg) }) return l.newEvent(PanicLevel, true, func(msg string) { panic(msg) })
} }
// WithLevel starts a new message with level.
//
// You must call Msg on the returned event in order to send the event.
func (l Logger) WithLevel(level Level) *Event {
switch level {
case DebugLevel:
return l.Debug()
case InfoLevel:
return l.Info()
case WarnLevel:
return l.Warn()
case ErrorLevel:
return l.Error()
case FatalLevel:
return l.Fatal()
case PanicLevel:
return l.Panic()
case Disabled:
return disabledEvent
default:
panic("zerolog: WithLevel(): invalid level: " + strconv.Itoa(int(level)))
}
}
// Log starts a new message with no level. Setting GlobalLevel to Disabled // Log starts a new message with no level. Setting GlobalLevel to Disabled
// will still disable events produced by this method. // will still disable events produced by this method.
// //

View File

@ -91,6 +91,15 @@ func ExampleLogger_Error() {
// Output: {"level":"error","error":"some error","message":"error doing something"} // Output: {"level":"error","error":"some error","message":"error doing something"}
} }
func ExampleLogger_WithLevel() {
log := zerolog.New(os.Stdout)
log.WithLevel(zerolog.InfoLevel).
Msg("hello world")
// Output: {"level":"info","message":"hello world"}
}
func ExampleLogger_Write() { func ExampleLogger_Write() {
log := zerolog.New(os.Stdout).With(). log := zerolog.New(os.Stdout).With().
Str("foo", "bar"). Str("foo", "bar").

View File

@ -14,7 +14,7 @@ func TestLog(t *testing.T) {
log := New(out) log := New(out)
log.Log().Msg("") log.Log().Msg("")
if got, want := out.String(), "{}\n"; got != want { if got, want := out.String(), "{}\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
}) })
@ -23,7 +23,7 @@ func TestLog(t *testing.T) {
log := New(out) log := New(out)
log.Log().Str("foo", "bar").Msg("") log.Log().Str("foo", "bar").Msg("")
if got, want := out.String(), `{"foo":"bar"}`+"\n"; got != want { if got, want := out.String(), `{"foo":"bar"}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
}) })
@ -35,7 +35,7 @@ func TestLog(t *testing.T) {
Int("n", 123). Int("n", 123).
Msg("") Msg("")
if got, want := out.String(), `{"foo":"bar","n":123}`+"\n"; got != want { if got, want := out.String(), `{"foo":"bar","n":123}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
}) })
} }
@ -46,7 +46,7 @@ func TestInfo(t *testing.T) {
log := New(out) log := New(out)
log.Info().Msg("") log.Info().Msg("")
if got, want := out.String(), `{"level":"info"}`+"\n"; got != want { if got, want := out.String(), `{"level":"info"}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
}) })
@ -55,7 +55,7 @@ func TestInfo(t *testing.T) {
log := New(out) log := New(out)
log.Info().Str("foo", "bar").Msg("") log.Info().Str("foo", "bar").Msg("")
if got, want := out.String(), `{"level":"info","foo":"bar"}`+"\n"; got != want { if got, want := out.String(), `{"level":"info","foo":"bar"}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
}) })
@ -67,7 +67,7 @@ func TestInfo(t *testing.T) {
Int("n", 123). Int("n", 123).
Msg("") Msg("")
if got, want := out.String(), `{"level":"info","foo":"bar","n":123}`+"\n"; got != want { if got, want := out.String(), `{"level":"info","foo":"bar","n":123}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
}) })
} }
@ -95,7 +95,36 @@ func TestWith(t *testing.T) {
Logger() Logger()
log.Log().Msg("") log.Log().Msg("")
if got, want := out.String(), `{"foo":"bar","error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11,"float64":12,"time":"0001-01-01T00:00:00Z"}`+"\n"; got != want { if got, want := out.String(), `{"foo":"bar","error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11,"float64":12,"time":"0001-01-01T00:00:00Z"}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
}
}
func TestFieldsMap(t *testing.T) {
out := &bytes.Buffer{}
log := New(out)
log.Log().Fields(map[string]interface{}{
"nil": nil,
"string": "foo",
"bytes": []byte("bar"),
"error": errors.New("some error"),
"bool": true,
"int": int(1),
"int8": int8(2),
"int16": int16(3),
"int32": int32(4),
"int64": int64(5),
"uint": uint(6),
"uint8": uint8(7),
"uint16": uint16(8),
"uint32": uint32(9),
"uint64": uint64(10),
"float32": float32(11),
"float64": float64(12),
"dur": 1 * time.Second,
"time": time.Time{},
}).Msg("")
if got, want := out.String(), `{"bool":true,"bytes":"bar","dur":1000,"error":"some error","float32":11,"float64":12,"int":1,"int16":3,"int32":4,"int64":5,"int8":2,"nil":null,"string":"foo","time":"0001-01-01T00:00:00Z","uint":6,"uint16":8,"uint32":9,"uint64":10,"uint8":7}`+"\n"; got != want {
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
} }
@ -104,7 +133,8 @@ func TestFields(t *testing.T) {
log := New(out) log := New(out)
now := time.Now() now := time.Now()
log.Log(). log.Log().
Str("foo", "bar"). Str("string", "foo").
Bytes("bytes", []byte("bar")).
AnErr("some_err", nil). AnErr("some_err", nil).
Err(errors.New("some error")). Err(errors.New("some error")).
Bool("bool", true). Bool("bool", true).
@ -124,8 +154,8 @@ func TestFields(t *testing.T) {
Time("time", time.Time{}). Time("time", time.Time{}).
TimeDiff("diff", now, now.Add(-10*time.Second)). TimeDiff("diff", now, now.Add(-10*time.Second)).
Msg("") Msg("")
if got, want := out.String(), `{"foo":"bar","error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11,"float64":12,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000}`+"\n"; got != want { if got, want := out.String(), `{"string":"foo","bytes":"bar","error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11,"float64":12,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
} }
@ -134,7 +164,8 @@ func TestFieldsDisabled(t *testing.T) {
log := New(out).Level(InfoLevel) log := New(out).Level(InfoLevel)
now := time.Now() now := time.Now()
log.Debug(). log.Debug().
Str("foo", "bar"). Str("string", "foo").
Bytes("bytes", []byte("bar")).
AnErr("some_err", nil). AnErr("some_err", nil).
Err(errors.New("some error")). Err(errors.New("some error")).
Bool("bool", true). Bool("bool", true).
@ -155,7 +186,7 @@ func TestFieldsDisabled(t *testing.T) {
TimeDiff("diff", now, now.Add(-10*time.Second)). TimeDiff("diff", now, now.Add(-10*time.Second)).
Msg("") Msg("")
if got, want := out.String(), ""; got != want { if got, want := out.String(), ""; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
} }
@ -163,7 +194,7 @@ func TestMsgf(t *testing.T) {
out := &bytes.Buffer{} out := &bytes.Buffer{}
New(out).Log().Msgf("one %s %.1f %d %v", "two", 3.4, 5, errors.New("six")) New(out).Log().Msgf("one %s %.1f %d %v", "two", 3.4, 5, errors.New("six"))
if got, want := out.String(), `{"message":"one two 3.4 5 six"}`+"\n"; got != want { if got, want := out.String(), `{"message":"one two 3.4 5 six"}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
} }
@ -172,7 +203,7 @@ func TestWithAndFieldsCombined(t *testing.T) {
log := New(out).With().Str("f1", "val").Str("f2", "val").Logger() log := New(out).With().Str("f1", "val").Str("f2", "val").Logger()
log.Log().Str("f3", "val").Msg("") log.Log().Str("f3", "val").Msg("")
if got, want := out.String(), `{"f1":"val","f2":"val","f3":"val"}`+"\n"; got != want { if got, want := out.String(), `{"f1":"val","f2":"val","f3":"val"}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
} }
@ -182,7 +213,7 @@ func TestLevel(t *testing.T) {
log := New(out).Level(Disabled) log := New(out).Level(Disabled)
log.Info().Msg("test") log.Info().Msg("test")
if got, want := out.String(), ""; got != want { if got, want := out.String(), ""; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
}) })
@ -191,7 +222,7 @@ func TestLevel(t *testing.T) {
log := New(out).Level(InfoLevel) log := New(out).Level(InfoLevel)
log.Info().Msg("test") log.Info().Msg("test")
if got, want := out.String(), `{"level":"info","message":"test"}`+"\n"; got != want { if got, want := out.String(), `{"level":"info","message":"test"}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
}) })
} }
@ -204,7 +235,7 @@ func TestSampling(t *testing.T) {
log.Log().Int("i", 3).Msg("") log.Log().Int("i", 3).Msg("")
log.Log().Int("i", 4).Msg("") log.Log().Int("i", 4).Msg("")
if got, want := out.String(), "{\"sample\":2,\"i\":2}\n{\"sample\":2,\"i\":4}\n"; got != want { if got, want := out.String(), "{\"sample\":2,\"i\":2}\n{\"sample\":2,\"i\":4}\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
} }
@ -239,6 +270,11 @@ func TestLevelWriter(t *testing.T) {
log.Info().Msg("2") log.Info().Msg("2")
log.Warn().Msg("3") log.Warn().Msg("3")
log.Error().Msg("4") log.Error().Msg("4")
log.WithLevel(DebugLevel).Msg("5")
log.WithLevel(InfoLevel).Msg("6")
log.WithLevel(WarnLevel).Msg("7")
log.WithLevel(ErrorLevel).Msg("8")
want := []struct { want := []struct {
l Level l Level
p string p string
@ -247,6 +283,10 @@ func TestLevelWriter(t *testing.T) {
{InfoLevel, `{"level":"info","message":"2"}` + "\n"}, {InfoLevel, `{"level":"info","message":"2"}` + "\n"},
{WarnLevel, `{"level":"warn","message":"3"}` + "\n"}, {WarnLevel, `{"level":"warn","message":"3"}` + "\n"},
{ErrorLevel, `{"level":"error","message":"4"}` + "\n"}, {ErrorLevel, `{"level":"error","message":"4"}` + "\n"},
{DebugLevel, `{"level":"debug","message":"5"}` + "\n"},
{InfoLevel, `{"level":"info","message":"6"}` + "\n"},
{WarnLevel, `{"level":"warn","message":"7"}` + "\n"},
{ErrorLevel, `{"level":"error","message":"8"}` + "\n"},
} }
if got := lw.ops; !reflect.DeepEqual(got, want) { if got := lw.ops; !reflect.DeepEqual(got, want) {
t.Errorf("invalid ops:\ngot:\n%v\nwant:\n%v", got, want) t.Errorf("invalid ops:\ngot:\n%v\nwant:\n%v", got, want)
@ -265,7 +305,7 @@ func TestContextTimestamp(t *testing.T) {
log.Log().Msg("hello world") log.Log().Msg("hello world")
if got, want := out.String(), `{"time":"2001-02-03T04:05:06Z","foo":"bar","message":"hello world"}`+"\n"; got != want { if got, want := out.String(), `{"time":"2001-02-03T04:05:06Z","foo":"bar","message":"hello world"}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
} }
@ -281,6 +321,6 @@ func TestEventTimestamp(t *testing.T) {
log.Log().Timestamp().Msg("hello world") log.Log().Timestamp().Msg("hello world")
if got, want := out.String(), `{"foo":"bar","time":"2001-02-03T04:05:06Z","message":"hello world"}`+"\n"; got != want { if got, want := out.String(), `{"foo":"bar","time":"2001-02-03T04:05:06Z","message":"hello world"}`+"\n"; got != want {
t.Errorf("invalid log output: got %q, want %q", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
} }