rs / zerolog

Zero Allocation JSON Logger
MIT License
10.39k stars 566 forks source link

Readable stack trace with cockroachdb/errors #614

Closed nikitacrit closed 8 months ago

nikitacrit commented 10 months ago

Hi! I am trying to use cockroachdb/errors package with zerolog. I want to make multiline, readable, verbose stack trace output at the end of log message for local development. Any ideas of better implementation? Current is annoying couse of hard coded internal formatting values (colorization). I faced core problem in string quotating code that ruin line breaks and I see no possibility too use FormatFieldValue here. String quotation code:

case string:
    if needsQuote(fValue) {
        buf.WriteString(fv(strconv.Quote(fValue)))
    } else {
        buf.WriteString(fv(fValue))
    }

Expected result:

2023-11-03T20:08:44.7548652+03:00 ERR error="cool error text" stack="
cool error text
(1) attached stack trace
  -- stack trace:
  | main.main
  |     C:/Users/nikita/projects/test1/main.go:21
  | runtime.main
  |     C:/Program Files/Go/src/runtime/proc.go:267
  | runtime.goexit
  |     C:/Program Files/Go/src/runtime/asm_amd64.s:1650
Wraps: (2) cool error text
Error types: (1) *withstack.withStack (2) *errutil.leafError"

Implementation:

func main() {
    zerolog.TimeFieldFormat = time.RFC3339Nano
    zerolog.ErrorStackMarshaler = errStackMarshaller

    log.Logger = log.Output(beautifulWriter(os.Stderr))

    log.Error().Stack().Err(errors.New("cool error text")).Send()
}

func beautifulWriter(out io.Writer) zerolog.ConsoleWriter {
    return zerolog.ConsoleWriter{
        Out:           out,
        TimeFormat:    time.RFC3339Nano,
        FieldsExclude: []string{zerolog.ErrorStackFieldName},
        FormatExtra: func(fields map[string]any, buf *bytes.Buffer) error {
            stack, ok := fields[zerolog.ErrorStackFieldName]
            if !ok {
                return nil
            }

            s := fmt.Sprintf("\x1b[%dm %v=\x1b[0m\"\n%v\"", 36, zerolog.ErrorStackFieldName, stack)

            _, err := buf.WriteString(s)
            if err != nil {
                return err
            }

            return nil
        },
    }
}

func errStackMarshaller(err error) any {
    return fmt.Sprintf("%+v", err)
}