Skip to content

Commit 7ef4863

Browse files
authored
INFOPLAT-2875: OtelZap refactor zap->otel conversion (#1637)
* OtelZap fix issue with nil Stringer mapping * Revert "OtelZap fix issue with nil Stringer mapping" This reverts commit c886e57. * Implement ObjectEncoder and let the fields write themselves * Add implementation for the rest otelAttrEncoder methods
1 parent d611017 commit 7ef4863

3 files changed

Lines changed: 487 additions & 92 deletions

File tree

pkg/logger/otelzap/encoder.go

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
package otelzap
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"go.opentelemetry.io/otel/attribute"
8+
"go.uber.org/zap/zapcore"
9+
)
10+
11+
// otelAttrEncoder implements zapcore.ObjectEncoder to encode zap fields into OpenTelemetry attributes
12+
type otelAttrEncoder struct {
13+
attributes []attribute.KeyValue
14+
namespace string
15+
}
16+
17+
// otelArrayEncoder implements zapcore.ArrayEncoder to collect array elements as strings
18+
type otelArrayEncoder struct {
19+
elements []string
20+
}
21+
22+
func (a *otelArrayEncoder) AppendString(v string) { a.elements = append(a.elements, v) }
23+
func (a *otelArrayEncoder) AppendInt64(v int64) {
24+
a.elements = append(a.elements, fmt.Sprintf("%d", v))
25+
}
26+
func (a *otelArrayEncoder) AppendInt(v int) { a.elements = append(a.elements, fmt.Sprintf("%d", v)) }
27+
func (a *otelArrayEncoder) AppendInt32(v int32) {
28+
a.elements = append(a.elements, fmt.Sprintf("%d", v))
29+
}
30+
func (a *otelArrayEncoder) AppendInt16(v int16) {
31+
a.elements = append(a.elements, fmt.Sprintf("%d", v))
32+
}
33+
func (a *otelArrayEncoder) AppendInt8(v int8) { a.elements = append(a.elements, fmt.Sprintf("%d", v)) }
34+
func (a *otelArrayEncoder) AppendUint64(v uint64) {
35+
a.elements = append(a.elements, fmt.Sprintf("%d", v))
36+
}
37+
func (a *otelArrayEncoder) AppendUint32(v uint32) {
38+
a.elements = append(a.elements, fmt.Sprintf("%d", v))
39+
}
40+
func (a *otelArrayEncoder) AppendUint16(v uint16) {
41+
a.elements = append(a.elements, fmt.Sprintf("%d", v))
42+
}
43+
func (a *otelArrayEncoder) AppendUint8(v uint8) {
44+
a.elements = append(a.elements, fmt.Sprintf("%d", v))
45+
}
46+
func (a *otelArrayEncoder) AppendUint(v uint) { a.elements = append(a.elements, fmt.Sprintf("%d", v)) }
47+
func (a *otelArrayEncoder) AppendUintptr(v uintptr) {
48+
a.elements = append(a.elements, fmt.Sprintf("%d", v))
49+
}
50+
func (a *otelArrayEncoder) AppendFloat64(v float64) {
51+
a.elements = append(a.elements, fmt.Sprintf("%g", v))
52+
}
53+
func (a *otelArrayEncoder) AppendFloat32(v float32) {
54+
a.elements = append(a.elements, fmt.Sprintf("%g", v))
55+
}
56+
func (a *otelArrayEncoder) AppendBool(v bool) { a.elements = append(a.elements, fmt.Sprintf("%t", v)) }
57+
func (a *otelArrayEncoder) AppendArray(zapcore.ArrayMarshaler) error {
58+
a.elements = append(a.elements, "[nested array]")
59+
return nil
60+
}
61+
func (a *otelArrayEncoder) AppendObject(zapcore.ObjectMarshaler) error {
62+
a.elements = append(a.elements, "[object]")
63+
return nil
64+
}
65+
func (a *otelArrayEncoder) AppendReflected(v any) error {
66+
a.elements = append(a.elements, fmt.Sprintf("%+v", v))
67+
return nil
68+
}
69+
func (a *otelArrayEncoder) AppendByteString(v []byte) { a.elements = append(a.elements, string(v)) }
70+
func (a *otelArrayEncoder) AppendComplex128(v complex128) {
71+
a.elements = append(a.elements, fmt.Sprintf("%v", v))
72+
}
73+
func (a *otelArrayEncoder) AppendComplex64(v complex64) {
74+
a.elements = append(a.elements, fmt.Sprintf("%v", v))
75+
}
76+
func (a *otelArrayEncoder) AppendDuration(v time.Duration) {
77+
a.elements = append(a.elements, v.String())
78+
}
79+
func (a *otelArrayEncoder) AppendTime(v time.Time) {
80+
a.elements = append(a.elements, v.Format(time.RFC3339))
81+
}
82+
83+
func (e *otelAttrEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
84+
// Create a simple array encoder that converts everything to strings
85+
encoder := &otelArrayEncoder{}
86+
err := marshaler.MarshalLogArray(encoder)
87+
if err != nil {
88+
return err
89+
}
90+
91+
e.attributes = append(e.attributes, attribute.StringSlice(e.prefixKey(key), encoder.elements))
92+
return nil
93+
}
94+
95+
func (e *otelAttrEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) error {
96+
// Create a nested encoder for the object
97+
objectEncoder := &otelAttrEncoder{}
98+
err := marshaler.MarshalLogObject(objectEncoder)
99+
if err != nil {
100+
return err
101+
}
102+
103+
// Add all attributes from the object with the key as prefix
104+
for _, attr := range objectEncoder.attributes {
105+
prefixedKey := key + "." + string(attr.Key)
106+
e.attributes = append(e.attributes, attribute.KeyValue{
107+
Key: attribute.Key(prefixedKey),
108+
Value: attr.Value,
109+
})
110+
}
111+
return nil
112+
}
113+
114+
func (e *otelAttrEncoder) AddBinary(key string, value []byte) {
115+
e.attributes = append(e.attributes, attribute.String(e.prefixKey(key), string(value)))
116+
}
117+
118+
func (e *otelAttrEncoder) AddBool(key string, value bool) {
119+
e.attributes = append(e.attributes, attribute.Bool(e.prefixKey(key), value))
120+
}
121+
122+
func (e *otelAttrEncoder) AddByteString(key string, value []byte) {
123+
e.attributes = append(e.attributes, attribute.String(e.prefixKey(key), string(value)))
124+
}
125+
126+
func (e *otelAttrEncoder) AddComplex128(key string, value complex128) {
127+
e.attributes = append(e.attributes, attribute.String(e.prefixKey(key), fmt.Sprintf("%v", value)))
128+
}
129+
130+
func (e *otelAttrEncoder) AddComplex64(key string, value complex64) {
131+
e.attributes = append(e.attributes, attribute.String(e.prefixKey(key), fmt.Sprintf("%v", value)))
132+
}
133+
134+
func (e *otelAttrEncoder) AddDuration(key string, value time.Duration) {
135+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), int64(value)))
136+
}
137+
138+
func (e *otelAttrEncoder) AddFloat64(key string, value float64) {
139+
e.attributes = append(e.attributes, attribute.Float64(e.prefixKey(key), value))
140+
}
141+
142+
func (e *otelAttrEncoder) AddFloat32(key string, value float32) {
143+
e.attributes = append(e.attributes, attribute.Float64(e.prefixKey(key), float64(value)))
144+
}
145+
146+
func (e *otelAttrEncoder) AddInt(key string, value int) {
147+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), int64(value)))
148+
}
149+
150+
func (e *otelAttrEncoder) AddInt64(key string, value int64) {
151+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), value))
152+
}
153+
154+
func (e *otelAttrEncoder) AddInt32(key string, value int32) {
155+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), int64(value)))
156+
}
157+
158+
func (e *otelAttrEncoder) AddInt16(key string, value int16) {
159+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), int64(value)))
160+
}
161+
162+
func (e *otelAttrEncoder) AddInt8(key string, value int8) {
163+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), int64(value)))
164+
}
165+
166+
func (e *otelAttrEncoder) AddString(key string, value string) {
167+
e.attributes = append(e.attributes, attribute.String(e.prefixKey(key), value))
168+
}
169+
170+
func (e *otelAttrEncoder) AddTime(key string, value time.Time) {
171+
e.attributes = append(e.attributes, attribute.String(e.prefixKey(key), value.Format(time.RFC3339)))
172+
}
173+
174+
func (e *otelAttrEncoder) AddUint(key string, value uint) {
175+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), int64(value)))
176+
}
177+
178+
func (e *otelAttrEncoder) AddUint64(key string, value uint64) {
179+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), int64(value)))
180+
}
181+
182+
func (e *otelAttrEncoder) AddUint32(key string, value uint32) {
183+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), int64(value)))
184+
}
185+
186+
func (e *otelAttrEncoder) AddUint16(key string, value uint16) {
187+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), int64(value)))
188+
}
189+
190+
func (e *otelAttrEncoder) AddUint8(key string, value uint8) {
191+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), int64(value)))
192+
}
193+
194+
func (e *otelAttrEncoder) AddUintptr(key string, value uintptr) {
195+
e.attributes = append(e.attributes, attribute.Int64(e.prefixKey(key), int64(value)))
196+
}
197+
198+
func (e *otelAttrEncoder) AddReflected(key string, value any) error {
199+
e.attributes = append(e.attributes, attribute.String(e.prefixKey(key), fmt.Sprintf("%+v", value)))
200+
return nil
201+
}
202+
203+
func (e *otelAttrEncoder) OpenNamespace(key string) {
204+
if e.namespace == "" {
205+
e.namespace = key
206+
} else {
207+
e.namespace = e.namespace + "." + key
208+
}
209+
}
210+
211+
// helper method to apply namespace prefix to keys
212+
func (e *otelAttrEncoder) prefixKey(key string) string {
213+
if e.namespace == "" {
214+
return key
215+
}
216+
return e.namespace + "." + key
217+
}

pkg/logger/otelzap/otelzap.go

Lines changed: 6 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package otelzap
33
import (
44
"context"
55
"fmt"
6-
"math"
76
"time"
87

98
"go.opentelemetry.io/otel/attribute"
@@ -84,7 +83,7 @@ func WithLevelEnabler(levelEnabler zapcore.LevelEnabler) Option {
8483
}
8584

8685
func (o OtelZapCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
87-
var attributes []attribute.KeyValue
86+
encoder := &otelAttrEncoder{}
8887
var spanCtx *oteltrace.SpanContext
8988

9089
// Add core-attached fields
@@ -94,15 +93,18 @@ func (o OtelZapCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
9493
spanCtx = &ctxValue
9594
}
9695
} else {
97-
attributes = append(attributes, mapZapField(f))
96+
f.AddTo(encoder)
9897
}
9998
}
10099

101100
// Add fields passed during log call
102101
for _, f := range fields {
103-
attributes = append(attributes, mapZapField(f))
102+
f.AddTo(encoder)
104103
}
105104

105+
// Start with encoder attributes
106+
attributes := encoder.attributes
107+
106108
// Add exception metadata
107109
if entry.Level > zapcore.InfoLevel {
108110
if entry.Caller.Defined {
@@ -146,61 +148,6 @@ func (o OtelZapCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
146148
return nil
147149
}
148150

149-
func mapZapField(f zapcore.Field) attribute.KeyValue {
150-
switch f.Type {
151-
case zapcore.StringType:
152-
return attribute.String(f.Key, f.String)
153-
154-
case zapcore.Int64Type, zapcore.Int32Type, zapcore.Int16Type, zapcore.Int8Type:
155-
return attribute.Int64(f.Key, f.Integer)
156-
157-
case zapcore.Uint64Type, zapcore.Uint32Type, zapcore.Uint16Type, zapcore.Uint8Type, zapcore.UintptrType:
158-
return attribute.Int64(f.Key, int64(f.Integer))
159-
160-
case zapcore.BoolType:
161-
return attribute.Bool(f.Key, f.Integer == 1)
162-
163-
case zapcore.Float64Type:
164-
return attribute.Float64(f.Key, math.Float64frombits(uint64(f.Integer)))
165-
166-
case zapcore.ErrorType:
167-
if err, ok := f.Interface.(error); ok {
168-
return attribute.String(f.Key, err.Error())
169-
}
170-
return attribute.String(f.Key, "invalid error field")
171-
172-
case zapcore.StringerType:
173-
return attribute.String(f.Key, f.Interface.(fmt.Stringer).String())
174-
175-
case zapcore.TimeType:
176-
if t, ok := f.Interface.(time.Time); ok {
177-
return attribute.String(f.Key, t.Format(time.RFC3339))
178-
}
179-
return attribute.String(f.Key, fmt.Sprintf("invalid time: %v", f.Interface))
180-
181-
case zapcore.DurationType:
182-
if d, ok := f.Interface.(time.Duration); ok {
183-
return attribute.String(f.Key, d.String())
184-
}
185-
return attribute.String(f.Key, fmt.Sprintf("invalid duration: %v", f.Interface))
186-
187-
case zapcore.BinaryType:
188-
if b, ok := f.Interface.([]byte); ok {
189-
return attribute.String(f.Key, fmt.Sprintf("binary data: %x", b))
190-
}
191-
return attribute.String(f.Key, fmt.Sprintf("invalid binary: %v", f.Interface))
192-
193-
case zapcore.ByteStringType:
194-
if b, ok := f.Interface.([]byte); ok {
195-
return attribute.String(f.Key, fmt.Sprintf("byte string: %x", b))
196-
}
197-
return attribute.String(f.Key, fmt.Sprintf("invalid byte string: %v", f.Interface))
198-
199-
default:
200-
return attribute.String(f.Key, f.String)
201-
}
202-
}
203-
204151
func mapZapSeverity(level zapcore.Level) otellog.Severity {
205152
switch level {
206153
case zapcore.DebugLevel:

0 commit comments

Comments
 (0)