Is your feature request related to a problem? Please describe
So, right now in case of error I just put all the information inside the error message (fmt.Errorf/errors.WithMessage) and later on if I want to see specific fields such as IDs on the entry level -- I need to care about it above. It generates duplications: error contains too much information + logs with multiple zap.Field passed inside for more comfortable searching.
Typical scenario:
err := fmt.Errorf("something wrong happened, id: %s, status: %d", id, status) // returned from a function
logger.Error("log message", zap.Error(err), zap.String("id", id), zap.Int("status", status))
Describe the solution you'd like
Introduce new interface type zapcore.ErrorMarshaler which gives opportunity to add extra inline fields to the entry.
type ErrCustom struct {
Message string
ID string
Status int
}
func (e *ErrCustom) Error() string { return e.Message }
func (e *ErrCustom) MarshalLogError(enc ObjectEncoder) error {
// enc.AddString("error", e.Error()) <- this one should probably stay the same by default, inside `encodeError` func
enc.AddString("id", e.ID)
enc.AddInt("status", e.Status)
return nil
}
Reuse zapcore.ObjectMarshaler instead on a new interface, but it may confuse a little.
+ unexpected behavior in case someone used this interface for their errors already.
Is this a breaking change?
Since we introduce absolutely new interface -- it should be backward compatible, or at least I don't see the possible issues with it... In case of re-usage existing one (ObjectMarshaler), I can imagine unexpected fields to appear for some extraordinary cases, but it's very unlikely from my perspective.
Additional context
The problem may appear in the wrapped error. For example:
In this scenario, we need to perform Unwrap which may lead to performance problem (is it?)
The question is what to do if multiple underlying errors implement the ErrorMarshaler interface. The right solution is probably to call MarshalLogError recursively for each one.
The API can actually provide wrappers for errors that allow easily insert zap.Field into it.
// zap.ErrorWithFields()
// OR
// zap.FieldedError()
3. In case this behavior leads to breaking changes or performance issues -- we can simply add a config/option flag for it, or provide a possibility to change `zapcore.encodeError` behavior in a way.
Is your feature request related to a problem? Please describe
So, right now in case of error I just put all the information inside the error message (
fmt.Errorf
/errors.WithMessage
) and later on if I want to see specific fields such as IDs on the entry level -- I need to care about it above. It generates duplications: error contains too much information + logs with multiplezap.Field
passed inside for more comfortable searching. Typical scenario:Describe the solution you'd like
Introduce new interface type
zapcore.ErrorMarshaler
which gives opportunity to add extra inline fields to the entry.In code:
Describe alternatives you've considered
Reuse
zapcore.ObjectMarshaler
instead on a new interface, but it may confuse a little. + unexpected behavior in case someone used this interface for their errors already.Is this a breaking change?
Since we introduce absolutely new interface -- it should be backward compatible, or at least I don't see the possible issues with it... In case of re-usage existing one (ObjectMarshaler), I can imagine unexpected fields to appear for some extraordinary cases, but it's very unlikely from my perspective.
Additional context
The problem may appear in the wrapped error. For example:
In this scenario, we need to perform
Unwrap
which may lead to performance problem (is it?) The question is what to do if multiple underlying errors implement theErrorMarshaler
interface. The right solution is probably to callMarshalLogError
recursively for each one.The API can actually provide wrappers for errors that allow easily insert
zap.Field
into it.func Do() error { id := "{UUID}" status := 1 originalErr := errors.New("something wrong happened") zapErr := zap.ErrorWithFields(originalErr, zap.String("id", id), zap.Int("status", status)) return zapErr }