8 ms·
Eris – A better way to handle, trace, and log errors in Go
- lowmagnet 7y agoIDK about the error json, looks too much like a PHP dump when a descriptive error wrapper will do.
- holaatme 7y agoIf you are reading the JSON yourself, you’re doing it wrong. JSON logs can be parsed and stored for analysis and generally deal with multi line issues much better than any non json.
- EdwardDiego 7y agoBingo, we emit all our logs as JSON for ingestion into ES to allow easy analysis.
- morningvera 7y agoMy friend and I have been working on eris for a while and we’re very excited to get feedback from fellow gophers. eris provides a better way to handle, trace, and log errors in Go. More info here github.com/rotisserie/eris and godoc.org/github.com/rotisserie/eris You can reach out to us on slack (gorotisserie.slack.com/archives/CS13EC3T6) for additional comments and feedback. Thanks!
- nicpottier 7y agoLooks nice! Any plans to add support for uploading stacks to Sentry? That's a must have for us and why we are using pkg/errors.
- morningvera 7y agoThanks! We'd love to add support if you need it. I'll make a note of it but feel free to add an issue for it if you have time!
- erik_seaberg 7y agoStack traces and structured output are handy, but I was hoping for a way to factor out the handler boilerplate that makes app code unreadable (like the "check" or "try" proposal for future versions).
- caylus 7y agoThis is really cool! My company has a similar internal library but it doesn't format wrapped errors as well as Eris. One other issue I've struggled with is in propagating errors across goroutines. If an error is created in the child routine, `runtime.Callers` doesn't include any stack frames from the parent. Assuming the parent wraps the error, it sounds like Eris would give you at least one line of the parent stack trace. Does it handle this specifically by including all of them?
- awinter-py 7y agoHave spent a while in the py/js world but have switched to rust/go for part of this year -- biggest change is boilerplate relating to error handling. Automatic stack capture for exceptions is something my language could conceivably do on my behalf. Writing even 3 lines of code per function to propogate up the error is a huge pain, especially because it pollutes the return type -- MustWhatever() in go is much easier to use than Whatever() returning (type, err)
- sagichmal 7y agoYou're fighting the language. Unless you're writing prototypes where crashing doesn't matter, you should be writing error handling code first, and your business logic second -- so-called "sad path first" programming, c.f. "happy-path first" programming that you usually do in languages with exceptions like Python.
- zbentley 7y agoI'm super into the error handling philosophies of Go and Rust, but I don't think either of them is "sad path first". That implies that you can think about your error cases in a meaningful way before you have concrete business logic, which seems unlikely.
- sagichmal 7y agoOf course you can. Sad path first means, after you write your data types and interface signatures, writing the first stub implementations as func (t *Thing) Process(id int) (string, error) { return "", fmt.Errorf("not implemented") } and then filling them in gradually like func (t *Thing) Process(id int) (string, error) { dat, err := t.store.Read(id) if err != nil { return "", fmt.Errorf("error reading ID: %w", err) } cert, err := dat.ExtractCertificate() if err != nil { return "", fmt.Errorf("error extracting certificate: %w", err) } return cert.Name(), nil } and explicitly not func (t *Thing) Process(id int) (string, error) { dat, _ := t.store.Read(id) // TODO: error handling cert, _ := dat.ExtractCertificate() // TODO: error handling return cert.Name(), nil } Writing code this way, explicit error handling upfront, is fundamental to reliability (for a large class of applications).
- mirimir 7y agoI wonder if they picked "Eris" based on Greg Hill's Principia Discordia.
- mirimir 7y agoI did see that it's named after the Greek goddess Eris. It's just that she isn't a major goddess, and is famous only for her "apple of beauty" trick. And outside classical scholars, that's arguably via Principia Discordia. So I'm curious.
- directionless 7y agoI'd love to see a bit of the docs telling me how this improves on pkg/errors and go 1.13. At first pass, I'm not sure? It seems to have a bit of a cleaner presentation in the error stack -- Its an array, which feels nicer than a string.
- bouncycastle 7y agoI think a lot of programmers (and languages) confuse errors and exceptions. In most cases, errors should be expected, like an End Of File error or socket disconnect error, they aren't really exceptions. You don't really want a stack trace if an EOF happens, so you? When there really is an exception, why not just use panic and let it bubble up?
- zeta0134 7y agoEhh, I'm not convinced the vocabulary there fits. An error can be an exception, if it was unexpected. That's the real distinction: errors are expected and can (optionally) be handled explicitly. Anything unexpected, in your code or within a library function, is an exception and should bubble up / halt execution. But the trouble is that _expectedness_ isn't often a language construct, but more like business logic, and will have different meanings (and presumed severity) between library author and end user. I think there are a lot of schools of thought on this precisely because it is a difficult problem to solve: it requires getting a bunch of programmers to agree about how the edge cases should be handled, and that's no small feat.
- gregwebs 7y agoPanic is hidden and not composable. Composition: Errors as values means it is easy to write functions to deal with them. Errors as exceptions means invoking special language constructs. And program termination doesn't compose well. Library authors always avoid panics. Generally only applications are good at deciding when to panic and when to catch a panic. Hidden: how do you know if a function panics? If it returns an error, I see that in the type signature and I can be forced to handle it by linting tools. People seem to not like Javas checked exceptions, but I think that like most things Java it had to do with some of the implementation details, I think the concept is much preferable to hidden exceptions/panics.
- rendaw 7y agoWhat's an error and what's an exception depends significantly on context. For the socket disconnect for example, maybe a child process maintains a connection to the parent manager process. A socket disconnect is absolutely unexpected then and explicitly handling it adds noise.
- chetanhs 7y agoThis looks pretty good. And I also get the reason behind it’s name. But for a utility package like this, it’s better if it was named errors. It makes the usage and call sites clear. Ex: eris.New() vs errors.New()