Skip to content

proposal: errors: add (stack)trace at error annotation  #60873

Open
@flibustenet

Description

@flibustenet

This proposal try to address the lack of traceback in errors but keep the way we handle errors as value annotated where the error occur.

Errors are values, a string or a custom error.
To retrieve the stack of errors we need to follow the chain of strings, each annotation should be written carefully to retrieve easily from where the error comes. If we just return err one time we loose the path.

Because of this we can embed all the stack in a custom error at the higher lever and print it at the end with the hope that we didn't loose the path with a return fmt.Errorf("... %v"). Like that we can just return err everywhere, we'll just use the full traceback to see what's happen like in most of the others languages.
Often we'll have both, a huge traceback and a chain of annotations in the end and full of return err everywhere. This is not fun and not idiomatic in Go.

Sometimes we just need an annotation, in a lib for example, and sometimes it's interesting to have the method and the exact line in an application. In a web server we generaly need only the stack after the handler and before the lib but not in the server and middlewares.

My proposal is to can add easily the trace and just this trace when we annotate the error. Something like fmt.Errorf("@trace I call foo: %v", err)

The result is a minimal traceback like that:
https://go.dev/play/p/VJSXkbOX7J_c

> main.main() prog.go:44
 f1 call f2: 
> main.f1() prog.go:13
 f2 call f3: 
> main.f2() prog.go:22
 simple error with no trace

I believe it follow the Go way of handling errors.
The error is still a value, a value with a trace as annotation (not to do for an API).
We decide at each error if we need the trace or not and not for the whole chain.
We encourage to annotate.
Explicit.
Easy to implement.

It's easy to experiment, just replace fmt.Errorf with a function like this:

func Errorf(s string, vals ...any) error {
	pc, file, line, ok := runtime.Caller(2)
	if ok {
		info := fmt.Sprintf("\n> %s() %s:%d\n",
			runtime.FuncForPC(pc).Name(), filepath.Base(file), line)
		s = strings.Replace(s, "@trace", info, 1)
	}
	return fmt.Errorf(s, vals...)
}

Hope it give at least inspiration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Proposalerror-handlingLanguage & library change proposals that are about error handling.

    Type

    No type

    Projects

    Status

    Incoming

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions