samber / oops

🔥 Error handling library with context, assertion, stack trace and source fragments
https://pkg.go.dev/github.com/samber/oops
MIT License
385 stars 11 forks source link

Zap example #1

Open mhmtszr opened 1 year ago

mhmtszr commented 1 year ago

Hi @samber,

I tried using oops with zap here is my example:

func main() {
    logger, _ := zap.NewProduction()
    err := oops.
        In("repository").
        Tags("database", "sql").
        With("deneme", "mehmet").
        Errorf("could not fetch user")

    logger.Error(err.Error(), zap.Error(err))
}
{"level":"error","ts":1684233642.017879,"caller":"test-api/main.go:17","msg":"could not fetch user","error":"could not fetch user","errorVerbose":"Oops: could not fetch user\nAt: 2023-05-16 10:40:42.017789 +0000 UTC\nDomain: repository\nTags: database, sql\nContext:\n  * deneme: mehmet\nStackstrace:\nOops: could not fetch user\n  --- at test-api/main.go:15 main()\n","stacktrace":"main.main/test-api/main.go:17"}

I couldn't achieve the log structure like your example in readme, would be great if I get errorVerbose as a json.

samber commented 1 year ago

Hi Mehmet,

And thanks for this quick feedback! I'm doing a few iterations before the initial release and your feedback is appreciated.

I have no experience with Zap. Do you know if we can create custom formatters for Zap? I made one for logrus.

Output can be exported using ToMap(), Format(), Marshal() or LogValuer. I suppose you were using Format() here. LogValuer will be used by the next-gen Go logger of the stdlib: slog.

mhmtszr commented 1 year ago

I don't have much experience with zap custom formats but I did something like this, as I understand we need to write a custom encoder, I couldn't find an error formatter or settings for jsonEncoder or EncoderConfig.

type myCustomEncoder struct {
    zapcore.ObjectEncoder
}

func (f *myCustomEncoder) EncodeEntry(zapcore.Entry, []zap.Field) (*buffer.Buffer, error) {
    return nil, nil
}

func (f *myCustomEncoder) Clone() zapcore.Encoder {
    return nil
}

func main() {
    zap.RegisterEncoder("myCustomEncoder", func(config zapcore.EncoderConfig) (zapcore.Encoder, error) {
        return &myCustomEncoder{}, nil
    })

    const timeFormat string = "2006-01-02T15:04:05.999Z"

    encoderConfig := zapcore.EncoderConfig{
        TimeKey:        "time",
        LevelKey:       "level",
        NameKey:        "logger",
        CallerKey:      "sourceLocation",
        FunctionKey:    zapcore.OmitKey,
        MessageKey:     "message",
        StacktraceKey:  "stacktrace",
        LineEnding:     zapcore.DefaultLineEnding,
        EncodeLevel:    zapcore.CapitalLevelEncoder,
        EncodeTime:     zapcore.TimeEncoderOfLayout(timeFormat),
        EncodeDuration: zapcore.SecondsDurationEncoder,
        EncodeCaller:   zapcore.ShortCallerEncoder,
    }

    config := zap.Config{
        Level:       zap.NewAtomicLevelAt(zapcore.InfoLevel),
        Development: false,
        Sampling: &zap.SamplingConfig{
            Initial:    100,
            Thereafter: 100,
        },
        Encoding:         "myCustomEncoder",
        EncoderConfig:    encoderConfig,
        OutputPaths:      []string{"stdout"},
        ErrorOutputPaths: []string{"stderr"},
    }
    logger, _ := config.Build(zap.AddStacktrace(zap.FatalLevel))
    err := oops.
        In("repository").
        Tags("database", "sql").
        With("deneme", "mehmet").
        Errorf("could not fetch user")

    logger.Error(err.Error(), zap.Error(err))
}
samber commented 1 year ago

Zap supports only 1 encoder at a time ?