zap: Expose the ability to disable errorVerbose via logger config

I use Zap for basically every service I write, it’s great! There’s one issue I have though. Since I use pkg/errors, each error satisfies the fmt.Formatter interface and thus, every error log comes with a stack trace. This is great in production when that information is vital, but during development, (i.e. with NewDevelopmentConfig) they still show up; which, given the less vital scenario, is a bit annoying.

I notice there’s DisableStacktrace which removes the stack trace that originates from the log call itself but no way to disable errorVerbose.

In development, stack traces clutter the logs and makes scanning the terminal pretty difficult. I usually don’t need to know the full stack trace, only the top-most element (or, the top-most errors.New call). And if I do, in a perfect world, I could flip a env var to enable it!

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 46
  • Comments: 25 (2 by maintainers)

Commits related to this issue

Most upvoted comments

Really, what I would like is for the cause trace provided by pkg/errors to be pretty-printed so that I can read it. Right now, the formatting is that it provides several lines, separated by \n which get shoved into the errorVerbose field. Imagine if the stacktrace provided by Zap were to be shoved into a field in development mode. It would be very hard to read.

The printing of a stacktrace is a property of the encoder. You could achieve what you want by using an encoder config with AddStacktrace(zapcore.ErrorLevel) and silencing the errorVerbose field in a way like the one above.

Another workaround sample based on using AddStacktrace is to have a wrapper function and use that for logging.

func noStacktraceError(l *zap.Logger, msg string, e error) {
	// increase stacktrace level to zap.DPanicLevel to avoid having stacktrace for errors
	l.WithOptions(zap.AddStacktrace(zap.DPanicLevel)).Error(msg, zap.Error(e))
}

func main() {
	l, _ := zap.NewDevelopment()
	e := fmt.Errorf("foo")

	l.Error("normal error", zap.Error(e))
	noStacktraceError(l, "no stacktrace error", e)
}

Right now, the formatting is that it provides several lines, separated by \n which get shoved into the errorVerbose field.

This. 10000% This.

Do you have any update about it ?

I’m sorry, then i’m out of ideas!

edit: I just had the idea that one can offload that task to the encoder maybe… something like this could work

type errorVerboseToStacktraceEncoder struct {
	zapcore.Encoder
}

type stackTracer interface {
	StackTrace() errors.StackTrace
}

func (he errorVerboseToStacktraceEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
	filteredFields := make([]zapcore.Field, 0)
	var st stackTracer
	for _, f := range fields {
		if f.Type != zapcore.ErrorType {
			filteredFields = append(filteredFields, f)
			continue
		}

		if t, ok := f.Interface.(stackTracer); ok {
			st = t
		}
		filteredFields = append(filteredFields, PlainError(f.Interface.(error)))
	}
	if st != nil {
		ent.Stack = fmt.Sprintf("%+v", st.StackTrace())
	}
	return he.Encoder.EncodeEntry(ent, filteredFields)
}

basically you look for errors in fields that fulfill the stackTracer interface ( pkg/errors does) and in that case you add a plain error and override the stack of the entry with the stack from the error. I tried it and it works; but im not sure if that is the best idea

edit2: there are some bugs in that code; one should take more care in plugging the stack traces together, ( if there are more than one error fields i.e.) but the idea should basically work