Live data from Hacker News

Eris – A better way to handle, trace, and log errors in Go

github.com

31–40 of 61 posts

Re: Eris – A better way to handle, trace, and log errors in Go

#31
post #3

IDK about the error json, looks too much like a PHP dump when a descriptive error wrapper will do.

If 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.

Bingo, we emit all our logs as JSON for ingestion into ES to allow easy analysis.

Re: Eris – A better way to handle, trace, and log errors in Go

#32
I 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?

Re: Eris – A better way to handle, trace, and log errors in Go

#33

Have 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 t…

What surprises me as a primarily JVM developer now supporting Go apps and dabbling in Go development is that the logging libraries (like klog) I've encountered don't support the level of configuration I'm used to in JVM apps. I miss being able to configure individual loggers at desired levels with a logback.xml in the JAR, but especially the ability to change logging levels at runtime.

I guess it's part of the Go philosophy - more dynamic logging would most likely incur relatively higher performance costs.

Re: Eris – A better way to handle, trace, and log errors in Go

#34
post #17

I wonder if they picked "Eris" based on Greg Hill's Principia Discordia .

I 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.

Re: Eris – A better way to handle, trace, and log errors in Go

#35

I 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?

Ehh, 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.

Re: Eris – A better way to handle, trace, and log errors in Go

#36

I 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?

Panic 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.

Re: Eris – A better way to handle, trace, and log errors in Go

#37

I 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?

What'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.

Re: Eris – A better way to handle, trace, and log errors in Go

#38

I 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?

Rust has no exceptions. Still, stack bubbling shows back up, implemented semi-manually by setting up each error type to be automatically cast to a more generic error type and then returned. To me that's an indication that exceptions are "real," as a legitimate pattern in real world programs, and not just an invention of overeager programming language designers.

Re: Eris – A better way to handle, trace, and log errors in Go

#39

Have 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 t…

What surprises me as a primarily JVM developer now supporting Go apps and dabbling in Go development is that the logging libraries (like klog) I've encountered don't support the level of configuration I'm used to in JVM apps. I miss being able to configure individual loggers at desired levels with a logback.xml in the JAR, but especially the ability to change logging levels at runtime. I guess it's part of the Go phi…

You might wanna look at using something like zap instead, as it enables you to do runtime loglevel changes...

Re: Eris – A better way to handle, trace, and log errors in Go

#40

Earlier quoted context omitted.

I'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.

Of 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)…

Sure, but in a better language, the second version already does the exact same thing as the first one. Your `return fmt.Errorf` is not exception handling, it is simply manual exception bubbling. It is boilerplate that you can forget to add, and that makes it harder to understand what the code is supposed to do. Maybe for the first error you are adding some context that the Read function didn't have, but for the second, the error handling code is doing absolutely nothing that an automatic exception stack trace didn't do.

Even worse, with this style of programming, someone up the stack who would actually want to handle these errors has no mechanism to do, since you're returning the same type from both error cases. If they wanted to handle the certificate error but not the read error, they would have to parse the error message string, which is brittle. But if you did want to add appropriate context, your function would bloat even more. Not to mention that the standard library doesn't really help, since it generally doesn't define any specific error types to set up some patterns for this. Even in your example, from the start you assumed that your function can either succeed or fail in a generic way, you didn't think that the signature may want to encode multiple different error types, which is what GP was talking about when saying you can't usually think about the sad case before the happy case. Sure, if the extent to which you want to define sad case first is 'sad case can happen', you can.

Go's error handling strategy is its weakest aspect, and it is annoying to hear advocates pretend that Go is doing it right, when 90% of Go code is doing the same thing as 90% of exception-based code, just manually and with less helpful context for either logging or for the 10% of code which actually wants to handle errors.

Post reply on HN