From 7af653895b6ce4754050aa9197549f74f8e49235 Mon Sep 17 00:00:00 2001 From: Olivier Poitrey Date: Mon, 10 Jul 2017 02:56:44 -0700 Subject: [PATCH] Add utility functions WithLevel and Fields Add some utility functions to ease migration from other logger API. --- context.go | 6 ++++ event.go | 9 ++++++ field.go | 54 +++++++++++++++++++++++++++++++ log.go | 25 +++++++++++++++ log_example_test.go | 9 ++++++ log_test.go | 78 ++++++++++++++++++++++++++++++++++----------- 6 files changed, 162 insertions(+), 19 deletions(-) diff --git a/context.go b/context.go index 93e0808..6c62e00 100644 --- a/context.go +++ b/context.go @@ -12,6 +12,12 @@ func (c Context) Logger() Logger { 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. func (c Context) Dict(key string, dict *Event) Context { dict.buf = append(dict.buf, '}') diff --git a/event.go b/event.go index 41bc562..e7c0e1b 100644 --- a/event.go +++ b/event.go @@ -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. // Use zerolog.Dict() to create the dictionary. func (e *Event) Dict(key string, dict *Event) *Event { diff --git a/field.go b/field.go index 02710e4..e4829c7 100644 --- a/field.go +++ b/field.go @@ -3,10 +3,64 @@ package zerolog import ( "encoding/json" "fmt" + "sort" "strconv" "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 { if len(dst) > 1 { dst = append(dst, ',') diff --git a/log.go b/log.go index c0e4e74..3247e9d 100644 --- a/log.go +++ b/log.go @@ -71,6 +71,7 @@ import ( "io" "io/ioutil" "os" + "strconv" "sync/atomic" ) @@ -246,6 +247,30 @@ func (l Logger) Panic() *Event { 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 // will still disable events produced by this method. // diff --git a/log_example_test.go b/log_example_test.go index a325309..7915865 100644 --- a/log_example_test.go +++ b/log_example_test.go @@ -91,6 +91,15 @@ func ExampleLogger_Error() { // 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() { log := zerolog.New(os.Stdout).With(). Str("foo", "bar"). diff --git a/log_test.go b/log_test.go index 6837a59..c2b8a58 100644 --- a/log_test.go +++ b/log_test.go @@ -14,7 +14,7 @@ func TestLog(t *testing.T) { log := New(out) log.Log().Msg("") 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.Log().Str("foo", "bar").Msg("") 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). Msg("") 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.Info().Msg("") 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.Info().Str("foo", "bar").Msg("") 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). Msg("") 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() 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 { - 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) now := time.Now() log.Log(). - Str("foo", "bar"). + Str("string", "foo"). + Bytes("bytes", []byte("bar")). AnErr("some_err", nil). Err(errors.New("some error")). Bool("bool", true). @@ -124,8 +154,8 @@ func TestFields(t *testing.T) { Time("time", time.Time{}). TimeDiff("diff", now, now.Add(-10*time.Second)). 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 { - t.Errorf("invalid log output: got %q, want %q", 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:\ngot: %v\nwant: %v", got, want) } } @@ -134,7 +164,8 @@ func TestFieldsDisabled(t *testing.T) { log := New(out).Level(InfoLevel) now := time.Now() log.Debug(). - Str("foo", "bar"). + Str("string", "foo"). + Bytes("bytes", []byte("bar")). AnErr("some_err", nil). Err(errors.New("some error")). Bool("bool", true). @@ -155,7 +186,7 @@ func TestFieldsDisabled(t *testing.T) { TimeDiff("diff", now, now.Add(-10*time.Second)). Msg("") 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{} 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 { - 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.Log().Str("f3", "val").Msg("") 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.Info().Msg("test") 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.Info().Msg("test") 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", 4).Msg("") 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.Warn().Msg("3") 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 { l Level p string @@ -247,6 +283,10 @@ func TestLevelWriter(t *testing.T) { {InfoLevel, `{"level":"info","message":"2"}` + "\n"}, {WarnLevel, `{"level":"warn","message":"3"}` + "\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) { 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") 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") 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) } }