Add support for typed array.

This commit is contained in:
Olivier Poitrey 2017-07-26 00:14:43 -07:00
parent 6cdd9977c4
commit 614d88bbf8
5 changed files with 321 additions and 1 deletions

169
array.go Normal file
View File

@ -0,0 +1,169 @@
package zerolog
import (
"sync"
"time"
"github.com/rs/zerolog/internal/json"
)
var arrayPool = &sync.Pool{
New: func() interface{} {
return &Array{
buf: make([]byte, 0, 500),
}
},
}
type Array struct {
buf []byte
}
// Arr creates an array to be added to an Event or Context.
func Arr() *Array {
a := arrayPool.Get().(*Array)
a.buf = a.buf[:0]
return a
}
func (*Array) MarshalZerologArray(*Array) {
}
func (a *Array) write(dst []byte) []byte {
if len(a.buf) == 0 {
dst = append(dst, `[]`...)
} else {
a.buf[0] = '['
dst = append(append(dst, a.buf...), ']')
}
arrayPool.Put(a)
return dst
}
// Object marshals an object that implement the LogObjectMarshaler
// interface and append it to the array.
func (a *Array) Object(obj LogObjectMarshaler) *Array {
a.buf = append(a.buf, ',')
e := Dict()
obj.MarshalZerologObject(e)
e.buf = append(e.buf, '}')
a.buf = append(a.buf, e.buf...)
return a
}
// Str append the val as a string to the array.
func (a *Array) Str(val string) *Array {
a.buf = json.AppendString(append(a.buf, ','), val)
return a
}
// Bytes append the val as a string to the array.
func (a *Array) Bytes(val []byte) *Array {
a.buf = json.AppendBytes(append(a.buf, ','), val)
return a
}
// Err append the err as a string to the array.
func (a *Array) Err(err error) *Array {
a.buf = json.AppendError(append(a.buf, ','), err)
return a
}
// Bool append the val as a bool to the array.
func (a *Array) Bool(b bool) *Array {
a.buf = json.AppendBool(append(a.buf, ','), b)
return a
}
// Int append i as a int to the array.
func (a *Array) Int(i int) *Array {
a.buf = json.AppendInt(append(a.buf, ','), i)
return a
}
// Int8 append i as a int8 to the array.
func (a *Array) Int8(i int8) *Array {
a.buf = json.AppendInt8(append(a.buf, ','), i)
return a
}
// Int16 append i as a int16 to the array.
func (a *Array) Int16(i int16) *Array {
a.buf = json.AppendInt16(append(a.buf, ','), i)
return a
}
// Int32 append i as a int32 to the array.
func (a *Array) Int32(i int32) *Array {
a.buf = json.AppendInt32(append(a.buf, ','), i)
return a
}
// Int64 append i as a int64 to the array.
func (a *Array) Int64(i int64) *Array {
a.buf = json.AppendInt64(append(a.buf, ','), i)
return a
}
// Uint append i as a uint to the array.
func (a *Array) Uint(i uint) *Array {
a.buf = json.AppendUint(append(a.buf, ','), i)
return a
}
// Uint8 append i as a uint8 to the array.
func (a *Array) Uint8(i uint8) *Array {
a.buf = json.AppendUint8(append(a.buf, ','), i)
return a
}
// Uint16 append i as a uint16 to the array.
func (a *Array) Uint16(i uint16) *Array {
a.buf = json.AppendUint16(append(a.buf, ','), i)
return a
}
// Uint32 append i as a uint32 to the array.
func (a *Array) Uint32(i uint32) *Array {
a.buf = json.AppendUint32(append(a.buf, ','), i)
return a
}
// Uint64 append i as a uint64 to the array.
func (a *Array) Uint64(i uint64) *Array {
a.buf = json.AppendUint64(append(a.buf, ','), i)
return a
}
// Float32 append f as a float32 to the array.
func (a *Array) Float32(f float32) *Array {
a.buf = json.AppendFloat32(append(a.buf, ','), f)
return a
}
// Float64 append f as a float64 to the array.
func (a *Array) Float64(f float64) *Array {
a.buf = json.AppendFloat64(append(a.buf, ','), f)
return a
}
// Time append t formated as string using zerolog.TimeFieldFormat.
func (a *Array) Time(t time.Time) *Array {
a.buf = json.AppendTime(append(a.buf, ','), t, TimeFieldFormat)
return a
}
// Dur append d to the array.
func (a *Array) Dur(d time.Duration) *Array {
a.buf = json.AppendDuration(append(a.buf, ','), d, DurationFieldUnit, DurationFieldInteger)
return a
}
// Interface append i marshaled using reflection.
func (a *Array) Interface(i interface{}) *Array {
if obj, ok := i.(LogObjectMarshaler); ok {
return a.Object(obj)
}
a.buf = json.AppendInterface(append(a.buf, ','), i)
return a
}

30
array_test.go Normal file
View File

@ -0,0 +1,30 @@
package zerolog
import (
"testing"
"time"
)
func TestArray(t *testing.T) {
a := Arr().
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).
Str("a").
Time(time.Time{}).
Dur(0)
want := `[true,1,2,3,4,5,6,7,8,9,10,11,12,"a","0001-01-01T00:00:00Z",0]`
if got := string(a.write([]byte{})); got != want {
t.Errorf("Array.write()\ngot: %s\nwant: %s", got, want)
}
}

View File

@ -31,6 +31,26 @@ func (c Context) Dict(key string, dict *Event) Context {
return c
}
// Array adds the field key with an array to the event context.
// Use zerolog.Arr() to create the array or pass a type that
// implement the LogArrayMarshaler interface.
func (c Context) Array(key string, arr LogArrayMarshaler) Context {
c.l.context = json.AppendKey(c.l.context, key)
if arr, ok := arr.(*Array); ok {
c.l.context = arr.write(c.l.context)
return c
}
var a *Array
if aa, ok := arr.(*Array); ok {
a = aa
} else {
a = Arr()
arr.MarshalZerologArray(a)
}
c.l.context = a.write(c.l.context)
return c
}
// Object marshals an object that implement the LogObjectMarshaler interface.
func (c Context) Object(key string, obj LogObjectMarshaler) Context {
e := newEvent(levelWriterAdapter{ioutil.Discard}, 0, true)

View File

@ -28,10 +28,18 @@ type Event struct {
done func(msg string)
}
// LogObjectMarshaler provides a strongly-typed and encoding-agnostic interface
// to be implemented by types used with Event/Context's Object methods.
type LogObjectMarshaler interface {
MarshalZerologObject(e *Event)
}
// LogArrayMarshaler provides a strongly-typed and encoding-agnostic interface
// to be implemented by types used with Event/Context's Array methods.
type LogArrayMarshaler interface {
MarshalZerologArray(a *Array)
}
func newEvent(w LevelWriter, level Level, enabled bool) *Event {
if !enabled {
return &Event{}
@ -127,6 +135,25 @@ func Dict() *Event {
return newEvent(levelWriterAdapter{ioutil.Discard}, 0, true)
}
// Array adds the field key with an array to the event context.
// Use zerolog.Arr() to create the array or pass a type that
// implement the LogArrayMarshaler interface.
func (e *Event) Array(key string, arr LogArrayMarshaler) *Event {
if !e.enabled {
return e
}
e.buf = json.AppendKey(e.buf, key)
var a *Array
if aa, ok := arr.(*Array); ok {
a = aa
} else {
a = Arr()
arr.MarshalZerologArray(a)
}
e.buf = a.write(e.buf)
return e
}
func (e *Event) appendObject(obj LogObjectMarshaler) {
pos := len(e.buf)
obj.MarshalZerologObject(e)
@ -143,6 +170,9 @@ func (e *Event) appendObject(obj LogObjectMarshaler) {
// Object marshals an object that implement the LogObjectMarshaler interface.
func (e *Event) Object(key string, obj LogObjectMarshaler) *Event {
if !e.enabled {
return e
}
e.buf = json.AppendKey(e.buf, key)
e.appendObject(obj)
return e
@ -166,7 +196,7 @@ func (e *Event) Strs(key string, vals []string) *Event {
return e
}
// Bytes adds the field key with val as a []byte to the *Event context.
// Bytes adds the field key with val as a string to the *Event context.
func (e *Event) Bytes(key string, val []byte) *Event {
if !e.enabled {
return e

View File

@ -150,9 +150,49 @@ func (u User) MarshalZerologObject(e *zerolog.Event) {
Time("created", u.Created)
}
type Users []User
func (uu Users) MarshalZerologArray(a *zerolog.Array) {
for _, u := range uu {
a.Object(u)
}
}
func ExampleEvent_Array() {
log := zerolog.New(os.Stdout)
log.Log().
Str("foo", "bar").
Array("array", zerolog.Arr().
Str("baz").
Int(1),
).
Msg("hello world")
// Output: {"foo":"bar","array":["baz",1],"message":"hello world"}
}
func ExampleEvent_Array_object() {
log := zerolog.New(os.Stdout)
// Users implements zerolog.LogArrayMarshaler
u := Users{
User{"John", 35, time.Time{}},
User{"Bob", 55, time.Time{}},
}
log.Log().
Str("foo", "bar").
Array("users", u).
Msg("hello world")
// 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"}
}
func ExampleEvent_Object() {
log := zerolog.New(os.Stdout)
// User implements zerolog.LogObjectMarshaler
u := User{"John", 35, time.Time{}}
log.Log().
@ -222,7 +262,38 @@ func ExampleContext_Dict() {
// Output: {"foo":"bar","dict":{"bar":"baz","n":1},"message":"hello world"}
}
func ExampleContext_Array() {
log := zerolog.New(os.Stdout).With().
Str("foo", "bar").
Array("array", zerolog.Arr().
Str("baz").
Int(1),
).Logger()
log.Log().Msg("hello world")
// Output: {"foo":"bar","array":["baz",1],"message":"hello world"}
}
func ExampleContext_Array_object() {
// Users implements zerolog.LogArrayMarshaler
u := Users{
User{"John", 35, time.Time{}},
User{"Bob", 55, time.Time{}},
}
log := zerolog.New(os.Stdout).With().
Str("foo", "bar").
Array("users", u).
Logger()
log.Log().Msg("hello world")
// 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"}
}
func ExampleContext_Object() {
// User implements zerolog.LogObjectMarshaler
u := User{"John", 35, time.Time{}}
log := zerolog.New(os.Stdout).With().