From a025d45231e5359143134c3c73d0b0f3975ed705 Mon Sep 17 00:00:00 2001 From: Ravi Raju Date: Wed, 16 May 2018 18:42:33 -0700 Subject: [PATCH] EmbedObject() API and knob to change caller frames (#66) * Fix for a bug in cbor decodeFloat * Add EmbedObject() method * knob to change the depth of caller frames to skip * removed EmbedObj() for array - since it is same as Object() --- binary_test.go | 45 ++++++++++++++++++++++++++++++++++++++++++++ context.go | 12 +++++++++++- event.go | 11 ++++++++++- globals.go | 3 +++ journald/journald.go | 3 +++ log_example_test.go | 44 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 2 deletions(-) diff --git a/binary_test.go b/binary_test.go index f878532..d02e3f9 100644 --- a/binary_test.go +++ b/binary_test.go @@ -283,6 +283,21 @@ func ExampleEvent_Object() { // Output: {"foo":"bar","user":{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},"message":"hello world"} } +func ExampleEvent_EmbedObject() { + price := Price{val: 6449, prec: 2, unit: "$"} + + dst := bytes.Buffer{} + log := New(&dst) + + log.Log(). + Str("foo", "bar"). + EmbedObject(price). + Msg("hello world") + + fmt.Println(decodeIfBinaryToString(dst.Bytes())) + // Output: {"foo":"bar","price":"$64.49","message":"hello world"} +} + func ExampleEvent_Interface() { dst := bytes.Buffer{} log := New(&dst) @@ -384,6 +399,36 @@ func ExampleContext_Array_object() { // Output: {"foo":"bar","users":[{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},{"name":"Bob","age":55,"created":"0001-01-01T00:00:00Z"}],"message":"hello world"} } +type Price struct { + val uint64 + prec int + unit string +} + +func (p Price) MarshalZerologObject(e *Event) { + denom := uint64(1) + for i := 0; i < p.prec; i++ { + denom *= 10 + } + result := []byte(p.unit) + result = append(result, fmt.Sprintf("%d.%d", p.val/denom, p.val%denom)...) + e.Str("price", string(result)) +} + +func ExampleContext_EmbedObject() { + price := Price{val: 6449, prec: 2, unit: "$"} + + dst := bytes.Buffer{} + log := New(&dst).With(). + Str("foo", "bar"). + EmbedObject(price). + Logger() + + log.Log().Msg("hello world") + + fmt.Println(decodeIfBinaryToString(dst.Bytes())) + // Output: {"foo":"bar","price":"$64.49","message":"hello world"} +} func ExampleContext_Object() { // User implements LogObjectMarshaler u := User{"John", 35, time.Time{}} diff --git a/context.go b/context.go index d9f9c22..fd26d5d 100644 --- a/context.go +++ b/context.go @@ -59,6 +59,15 @@ func (c Context) Object(key string, obj LogObjectMarshaler) Context { return c } +// EmbedObject marshals and Embeds an object that implement the LogObjectMarshaler interface. +func (c Context) EmbedObject(obj LogObjectMarshaler) Context { + e := newEvent(levelWriterAdapter{ioutil.Discard}, 0) + e.EmbedObject(obj) + c.l.context = enc.AppendObjectData(c.l.context, e.buf) + eventPool.Put(e) + return c +} + // Str adds the field key with val as a string to the logger context. func (c Context) Str(key, val string) Context { c.l.context = enc.AppendString(enc.AppendKey(c.l.context, key), val) @@ -321,7 +330,8 @@ func (c Context) Interface(key string, i interface{}) Context { type callerHook struct{} func (ch callerHook) Run(e *Event, level Level, msg string) { - e.caller(4) + //Two extra frames to skip (added by hook infra). + e.caller(CallerSkipFrameCount+2) } var ch = callerHook{} diff --git a/event.go b/event.go index 85e90ee..1e191a3 100644 --- a/event.go +++ b/event.go @@ -179,6 +179,15 @@ func (e *Event) Object(key string, obj LogObjectMarshaler) *Event { return e } +// Object marshals an object that implement the LogObjectMarshaler interface. +func (e *Event) EmbedObject(obj LogObjectMarshaler) *Event { + if e == nil { + return e + } + obj.MarshalZerologObject(e) + return e +} + // Str adds the field key with val as a string to the *Event context. func (e *Event) Str(key, val string) *Event { if e == nil { @@ -581,7 +590,7 @@ func (e *Event) Interface(key string, i interface{}) *Event { // Caller adds the file:line of the caller with the zerolog.CallerFieldName key. func (e *Event) Caller() *Event { - return e.caller(2) + return e.caller(CallerSkipFrameCount) } func (e *Event) caller(skip int) *Event { diff --git a/globals.go b/globals.go index 5c2aa9c..1a7dbcf 100644 --- a/globals.go +++ b/globals.go @@ -19,6 +19,9 @@ var ( // CallerFieldName is the field name used for caller field. CallerFieldName = "caller" + // CallerSkipFrameCount is the number of stack frames to skip to find the caller. + CallerSkipFrameCount = 2 + // TimeFieldFormat defines the time format of the Time field type. // If set to an empty string, the time is formatted as an UNIX timestamp // as integer. diff --git a/journald/journald.go b/journald/journald.go index fecc99b..dde4e3e 100644 --- a/journald/journald.go +++ b/journald/journald.go @@ -1,5 +1,8 @@ // +build !windows +// Package journald provides a io.Writer to send the logs +// to journalD component of systemd. + package journald // This file provides a zerolog writer so that logs printed diff --git a/log_example_test.go b/log_example_test.go index 5d541b3..56ee9c8 100644 --- a/log_example_test.go +++ b/log_example_test.go @@ -4,6 +4,7 @@ package zerolog_test import ( "errors" + "fmt" stdlog "log" "net" "os" @@ -195,6 +196,22 @@ func (u User) MarshalZerologObject(e *zerolog.Event) { Time("created", u.Created) } +type Price struct { + val uint64 + prec int + unit string +} + +func (p Price) MarshalZerologObject(e *zerolog.Event) { + denom := uint64(1) + for i := 0; i < p.prec; i++ { + denom *= 10 + } + result := []byte(p.unit) + result = append(result, fmt.Sprintf("%d.%d", p.val/denom, p.val%denom)...) + e.Str("price", string(result)) +} + type Users []User func (uu Users) MarshalZerologArray(a *zerolog.Array) { @@ -248,6 +265,19 @@ func ExampleEvent_Object() { // Output: {"foo":"bar","user":{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},"message":"hello world"} } +func ExampleEvent_EmbedObject() { + log := zerolog.New(os.Stdout) + + price := Price{val: 6449, prec: 2, unit: "$"} + + log.Log(). + Str("foo", "bar"). + EmbedObject(price). + Msg("hello world") + + // Output: {"foo":"bar","price":"$64.49","message":"hello world"} +} + func ExampleEvent_Interface() { log := zerolog.New(os.Stdout) @@ -351,6 +381,20 @@ func ExampleContext_Object() { // Output: {"foo":"bar","user":{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},"message":"hello world"} } +func ExampleContext_EmbedObject() { + + price := Price{val: 6449, prec: 2, unit: "$"} + + log := zerolog.New(os.Stdout).With(). + Str("foo", "bar"). + EmbedObject(price). + Logger() + + log.Log().Msg("hello world") + + // Output: {"foo":"bar","price":"$64.49","message":"hello world"} +} + func ExampleContext_Interface() { obj := struct { Name string `json:"name"`