diff --git a/bridges/otelzap/core.go b/bridges/otelzap/core.go index 00396681555..e4be7f783b8 100644 --- a/bridges/otelzap/core.go +++ b/bridges/otelzap/core.go @@ -151,8 +151,6 @@ func (o *Core) Write(ent zapcore.Entry, fields []zapcore.Field) error { // TODO: Handle attributes passed via With (exceptions: context.Context and zap.Namespace). // TODO: Handle context.Context containing trace context. // TODO: Handle zap.Namespace. - // TODO: Handle zap.Object. - // TODO: Handle zap.Array. // TODO: Handle ent.LoggerName. r.AddAttributes(o.attr...) diff --git a/bridges/otelzap/encoder.go b/bridges/otelzap/encoder.go index 474bb9f6e1c..eed14b0798f 100644 --- a/bridges/otelzap/encoder.go +++ b/bridges/otelzap/encoder.go @@ -40,8 +40,11 @@ func (m *objectEncoder) AddArray(key string, v zapcore.ArrayMarshaler) error { } func (m *objectEncoder) AddObject(k string, v zapcore.ObjectMarshaler) error { - // TODO - return nil + // TODO: Use objectEncoder from a pool. + newobj := newObjectEncoder(2) + err := v.MarshalLogObject(newobj) + m.kv = append(m.kv, log.Map(k, newobj.kv...)) + return err } func (m *objectEncoder) AddBinary(k string, v []byte) { @@ -155,17 +158,23 @@ func assignUintValue(v uint64) log.Value { // arrayEncoder implements [zapcore.ArrayEncoder]. type arrayEncoder struct { - elems []log.Value // nolint:unused + elems []log.Value } -// TODO. func (a *arrayEncoder) AppendArray(v zapcore.ArrayMarshaler) error { - return nil + // TODO: Use arrayEncoder from a pool. + arr := &arrayEncoder{} + err := v.MarshalLogArray(arr) + a.elems = append(a.elems, log.SliceValue(arr.elems...)) + return err } -// TODO. func (a *arrayEncoder) AppendObject(v zapcore.ObjectMarshaler) error { - return nil + // TODO: Use objectEncoder from a pool. + m := newObjectEncoder(2) + err := v.MarshalLogObject(m) + a.elems = append(a.elems, log.MapValue(m.kv...)) + return err } // TODO. diff --git a/bridges/otelzap/encoder_test.go b/bridges/otelzap/encoder_test.go index da8bc56e30e..561eb37f34f 100644 --- a/bridges/otelzap/encoder_test.go +++ b/bridges/otelzap/encoder_test.go @@ -6,6 +6,7 @@ package otelzap import ( + "errors" "testing" "time" @@ -18,11 +19,33 @@ import ( // Copied from https://github.com/uber-go/zap/blob/b39f8b6b6a44d8371a87610be50cce58eeeaabcb/zapcore/memory_encoder_test.go. func TestObjectEncoder(t *testing.T) { + // Expected output of a turducken. + wantTurducken := map[string]interface{}{ + "ducks": []interface{}{ + map[string]interface{}{"in": "chicken"}, + map[string]interface{}{"in": "chicken"}, + }, + } + tests := []struct { desc string f func(zapcore.ObjectEncoder) expected interface{} }{ + { + desc: "AddObject", + f: func(e zapcore.ObjectEncoder) { + assert.NoError(t, e.AddObject("k", loggable{true}), "Expected AddObject to succeed.") + }, + expected: map[string]interface{}{"loggable": "yes"}, + }, + { + desc: "AddObject (nested)", + f: func(e zapcore.ObjectEncoder) { + assert.NoError(t, e.AddObject("k", turducken{}), "Expected AddObject to succeed.") + }, + expected: wantTurducken, + }, { desc: "AddArray", f: func(e zapcore.ObjectEncoder) { @@ -35,6 +58,13 @@ func TestObjectEncoder(t *testing.T) { }, expected: []interface{}{true, false, true}, }, + { + desc: "AddArray (nested)", + f: func(e zapcore.ObjectEncoder) { + assert.NoError(t, e.AddArray("k", turduckens(2)), "Expected AddArray to succeed.") + }, + expected: []interface{}{wantTurducken, wantTurducken}, + }, { desc: "AddBinary", f: func(e zapcore.ObjectEncoder) { e.AddBinary("k", []byte("foo")) }, @@ -164,6 +194,19 @@ func TestArrayEncoder(t *testing.T) { f func(zapcore.ArrayEncoder) expected interface{} }{ + // AppendObject is covered by AddObject (nested) case above. + { + desc: "AppendArray (arrays of arrays)", + f: func(e zapcore.ArrayEncoder) { + err := e.AppendArray(zapcore.ArrayMarshalerFunc(func(inner zapcore.ArrayEncoder) error { + inner.AppendBool(true) + inner.AppendBool(false) + return nil + })) + assert.NoError(t, err) + }, + expected: []interface{}{true, false}, + }, {"AppendBool", func(e zapcore.ArrayEncoder) { e.AppendBool(true) }, true}, {"AppendByteString", func(e zapcore.ArrayEncoder) { e.AppendByteString([]byte("foo")) }, "foo"}, {"AppendFloat64", func(e zapcore.ArrayEncoder) { e.AppendFloat64(3.14) }, 3.14}, @@ -201,6 +244,52 @@ func TestArrayEncoder(t *testing.T) { } } +type turducken struct{} + +func (t turducken) MarshalLogObject(enc zapcore.ObjectEncoder) error { + return enc.AddArray("ducks", zapcore.ArrayMarshalerFunc(func(arr zapcore.ArrayEncoder) error { + for i := 0; i < 2; i++ { + err := arr.AppendObject(zapcore.ObjectMarshalerFunc(func(inner zapcore.ObjectEncoder) error { + inner.AddString("in", "chicken") + return nil + })) + if err != nil { + return err + } + } + return nil + })) +} + +type turduckens int + +func (t turduckens) MarshalLogArray(enc zapcore.ArrayEncoder) error { + var err error + tur := turducken{} + for i := 0; i < int(t); i++ { + err = errors.Join(err, enc.AppendObject(tur)) + } + return err +} + +type loggable struct{ bool } + +func (l loggable) MarshalLogObject(enc zapcore.ObjectEncoder) error { + if !l.bool { + return errors.New("can't marshal") + } + enc.AddString("loggable", "yes") + return nil +} + +func (l loggable) MarshalLogArray(enc zapcore.ArrayEncoder) error { + if !l.bool { + return errors.New("can't marshal") + } + enc.AppendBool(true) + return nil +} + func value2Result(v log.Value) any { switch v.Kind() { case log.KindBool: