@@ -81,6 +81,10 @@ type zapLogger struct {
81
81
// Logger.Error calls.
82
82
errorKey string
83
83
84
+ // errorKeyDetailsSuffix gets appended to the field name
85
+ // when logging additional details obtained via MarshalLog.
86
+ errorKeyDetailsSuffix string
87
+
84
88
// allowZapFields enables logging of strongly-typed Zap
85
89
// fields. It is off by default because it breaks
86
90
// implementation agnosticism.
@@ -136,15 +140,15 @@ func (zl *zapLogger) handleFields(lvl int, args []interface{}, additional ...zap
136
140
continue
137
141
}
138
142
if zl .panicMessages {
139
- zl .l .WithOptions (zap .AddCallerSkip (1 )).DPanic ("strongly-typed Zap Field passed to logr" , zapIt ("zap field" , args [i ]))
143
+ zl .l .WithOptions (zap .AddCallerSkip (1 )).DPanic ("strongly-typed Zap Field passed to logr" , zl . zapIt ("zap field" , args [i ]))
140
144
}
141
145
break
142
146
}
143
147
144
148
// make sure this isn't a mismatched key
145
149
if i == len (args )- 1 {
146
150
if zl .panicMessages {
147
- zl .l .WithOptions (zap .AddCallerSkip (1 )).DPanic ("odd number of arguments passed as key-value pairs for logging" , zapIt ("ignored key" , args [i ]))
151
+ zl .l .WithOptions (zap .AddCallerSkip (1 )).DPanic ("odd number of arguments passed as key-value pairs for logging" , zl . zapIt ("ignored key" , args [i ]))
148
152
}
149
153
break
150
154
}
@@ -156,35 +160,63 @@ func (zl *zapLogger) handleFields(lvl int, args []interface{}, additional ...zap
156
160
if ! isString {
157
161
// if the key isn't a string, DPanic and stop logging
158
162
if zl .panicMessages {
159
- zl .l .WithOptions (zap .AddCallerSkip (1 )).DPanic ("non-string key argument passed to logging, ignoring all later arguments" , zapIt ("invalid key" , key ))
163
+ zl .l .WithOptions (zap .AddCallerSkip (1 )).DPanic ("non-string key argument passed to logging, ignoring all later arguments" , zl . zapIt ("invalid key" , key ))
160
164
}
161
165
break
162
166
}
163
167
164
- fields = append (fields , zapIt (keyStr , val ))
168
+ fields = append (fields , zl . zapIt (keyStr , val ))
165
169
i += 2
166
170
}
167
171
168
172
return append (fields , additional ... )
169
173
}
170
174
171
- func zapIt (field string , val interface {}) zap.Field {
175
+ func (zl * zapLogger ) zapIt (field string , val interface {}) zap.Field {
176
+ if err , ok := val .(error ); ok {
177
+ return zl .zapError (field , err )
178
+ }
179
+
172
180
// Handle types that implement logr.Marshaler: log the replacement
173
181
// object instead of the original one.
174
182
if marshaler , ok := val .(logr.Marshaler ); ok {
175
183
field , val = invokeMarshaler (field , marshaler )
176
184
}
177
- if keysAndValues , ok := val .(logr.KeysAndValues ); ok {
185
+ switch val := val .(type ) {
186
+ case logr.KeysAndValues :
178
187
return zap .Object (field , zapcore .ObjectMarshalerFunc (func (encoder zapcore.ObjectEncoder ) error {
179
- for _ , keyAndValue := range keysAndValues {
188
+ for _ , keyAndValue := range val {
180
189
encoder .AddReflected (keyAndValue .Key , keyAndValue .Value )
181
190
}
182
191
return nil
183
192
}))
184
193
}
194
+
185
195
return zap .Any (field , val )
186
196
}
187
197
198
+ func (zl * zapLogger ) zapError (field string , err error ) zap.Field {
199
+ if err == nil {
200
+ return zap .Skip ()
201
+ }
202
+ return zap .Inline (zapcore .ObjectMarshalerFunc (func (encoder zapcore.ObjectEncoder ) (retErr error ) {
203
+ // Always log as a normal error first.
204
+ zap .NamedError (field , err ).AddTo (encoder )
205
+
206
+ // Extra details are optional, but might be available if the error also
207
+ // implements MarshalLog.
208
+ if logMarshaler , ok := err .(logr.Marshaler ); ok {
209
+ func () {
210
+ if r := recover (); r != nil {
211
+ retErr = fmt .Errorf ("PANIC=%v" , r )
212
+ }
213
+ zl .zapIt (field + zl .errorKeyDetailsSuffix , logMarshaler .MarshalLog ()).AddTo (encoder )
214
+ }()
215
+ }
216
+ return
217
+ }))
218
+ }
219
+
188
220
func invokeMarshaler (field string , m logr.Marshaler ) (f string , ret interface {}) {
189
221
defer func () {
190
222
if r := recover (); r != nil {
@@ -221,10 +253,22 @@ func (zl *zapLogger) Info(lvl int, msg string, keysAndVals ...interface{}) {
221
253
222
254
func (zl * zapLogger ) Error (err error , msg string , keysAndVals ... interface {}) {
223
255
if checkedEntry := zl .l .Check (zap .ErrorLevel , msg ); checkedEntry != nil {
224
- checkedEntry .Write (zl .handleFields (noLevel , keysAndVals , zap . NamedError (zl .errorKey , err ))... )
256
+ checkedEntry .Write (zl .handleFields (noLevel , keysAndVals , zl . zapError (zl .errorKey , err ))... )
225
257
}
226
258
}
227
259
260
+ // errorToString converts an error to a string,
261
+ // handling panics if they occur.
262
+ func errorToString (err error ) (ret string ) {
263
+ defer func () {
264
+ if err := recover (); err != nil {
265
+ ret = fmt .Sprintf ("<panic: %s>" , err )
266
+ }
267
+ }()
268
+ ret = err .Error ()
269
+ return
270
+ }
271
+
228
272
func (zl * zapLogger ) WithValues (keysAndValues ... interface {}) logr.LogSink {
229
273
newLogger := * zl
230
274
newLogger .l = zl .l .With (zl .handleFields (noLevel , keysAndValues )... )
@@ -269,6 +313,7 @@ func NewLoggerWithOptions(l *zap.Logger, opts ...Option) logr.Logger {
269
313
l : log ,
270
314
}
271
315
zl .errorKey = "error"
316
+ zl .errorKeyDetailsSuffix = "Details"
272
317
zl .panicMessages = true
273
318
for _ , option := range opts {
274
319
option (zl )
@@ -298,6 +343,15 @@ func ErrorKey(key string) Option {
298
343
}
299
344
}
300
345
346
+ // ErrorKeyDetailsSuffix replaces the default "Details" suffix that gets
347
+ // appended to the field name for an error when logging the error details
348
+ // obtained through MarshalLog.
349
+ func ErrorKeyDetailsSuffix (key string ) Option {
350
+ return func (zl * zapLogger ) {
351
+ zl .errorKeyDetailsSuffix = key
352
+ }
353
+ }
354
+
301
355
// AllowZapFields controls whether strongly-typed Zap fields may
302
356
// be passed instead of a key/value pair. This is disabled by
303
357
// default because it breaks implementation agnosticism.
0 commit comments